Actually prune messages from bugs
[emesinae:emesinae.git] / Emesinae / Bug.pm
1 package Emesinae::Bug;
2
3 use warnings;
4 use strict;
5
6 use Emesinae::Message;
7 use Emesinae::Common;
8
9 my $SELECT_FIELDS =
10   "bugs.bug_id,title_raw,creationdate,lastchangedate,owner_raw,severity,open";
11
12 sub new {
13     my $class = shift;
14     my $dbh   = shift;
15     my %args  = @_;
16     my $row;
17
18     die if $args{Fields} and $args{ID};
19
20     if ( $args{Fields} ) {
21         $row = $args{Fields};
22     }
23     elsif ( $args{ID} ) {
24         $row = $dbh->selectrow_arrayref( "
25             SELECT $SELECT_FIELDS FROM bugs WHERE bug_id = ?
26         ", {}, $args{ID} );
27         return undef unless $row;
28     }
29     else { die "Emesinae::Bug->new needs ID or Fields"; }
30
31     my $self = {
32         dbh            => $dbh,
33         id             => $row->[0],
34         title          => $row->[1],
35         creationdate   => $row->[2],
36         lastchangedate => $row->[3],
37         owner          => $row->[4],
38         severity       => $row->[5],
39         open           => $row->[6] eq "true",
40     };
41
42     bless $self, $class;
43     return $self;
44 }
45
46 sub assert_valid_severity {
47     my $sev = shift;
48     die "Invalid severity `$sev'" unless $sev ~~ @{ $c{SeverityLevels} };
49 }
50
51 sub create {
52     my $class = shift;
53     my $dbh   = shift;
54     my $msgid = shift;
55     my $subj  = shift;
56     my $owner = shift;
57     my $sev   = shift;
58
59     $sev = $c{DefaultSeverity} unless $sev;
60
61     assert_valid_severity $sev;
62
63     my $sth = $dbh->prepare(
64         q{
65         INSERT
66           INTO bugs (title_raw,creationdate,lastchangedate,owner_raw,severity)
67         VALUES (?,strftime('%s', 'now'),strftime('%s', 'now'),?,?)
68     }
69     );
70
71     $sth->execute( $subj, $owner, $sev ) or die "Failed to insert";
72
73     my $bugid = $dbh->sqlite_last_insert_rowid();
74
75     my $self = $class->new( $dbh, ID => $bugid );
76
77     my $msg = Emesinae::Message->lookup_msgid( $dbh, $msgid, Insert => 1 );
78     die "lookup_msgid or insert failed" unless $msg;
79
80     $self->add_subthread($msg);
81
82     return $self;
83 }
84
85 sub _set {
86     my $self = shift;
87     my ( $field, $value ) = @_;
88
89     assert_valid_severity $value if $field eq "severity";
90
91     my $sth = $self->{dbh}->prepare( "
92         UPDATE bugs
93            SET $field=?,lastchangedate=strftime('%s', 'now')
94          WHERE bug_id = ?
95     " );
96     $sth->execute( $value, $self->{id} ) or die "Setting $field";
97
98     $self->{$field} = $value;
99 }
100
101 sub set_title {
102     my $self  = shift;
103     my $title = shift;
104
105     $self->_set( "title_raw", $title );
106 }
107
108 sub set_owner {
109     my $self  = shift;
110     my $owner = shift;
111
112     $self->_set( "owner_raw", $owner );
113 }
114
115 sub set_severity {
116     my $self = shift;
117     my $sev  = shift;
118
119     assert_valid_severity($sev);
120
121     $self->_set( "severity", $sev );
122 }
123
124 sub _lookup_tag($$$) {
125     my ( $self, $ns, $tag ) = @_;
126
127     my $row = $self->{dbh}->selectrow_arrayref( "
128         SELECT tag_id
129           FROM tags
130          WHERE namespace = ?
131            AND name = ?
132     ", {}, $ns, $tag ) || die "Unable to find tag $ns:$tag";
133
134     return $row->[0];
135 }
136
137 sub set_tag($$$) {
138     my ( $self, $ns, $tag ) = @_;
139
140     my $tag_id = $self->_lookup_tag( $ns, $tag );
141
142     my $sth = $self->{dbh}->prepare( "
143         INSERT OR REPLACE INTO bug2tag(bug_id,tag_id)
144                   VALUES (?,?)
145     " );
146     $sth->execute( $self->{id}, $tag_id )
147       or die "Setting tag $ns:$tag on Bug #" . $self->{id};
148     $sth->rows == 1 or die "Failed to set tag $ns:$tag on Bug #" . $self->{id};
149 }
150
151 sub clear_tag($$$) {
152     my ( $self, $ns, $tag ) = @_;
153
154     my $tag_id = $self->_lookup_tag( $ns, $tag );
155
156     my $sth = $self->{dbh}->prepare( "
157         DELETE FROM bug2tag
158               WHERE bug_id = ?
159                 AND tag_id = ?
160     " );
161     $sth->execute( $self->{id}, $tag_id )
162       or die "Clearing tag $ns:$tag on Bug #" . $self->{id};
163 }
164
165 sub tags($$) {
166     my ( $self, $ns ) = @_;
167
168     my $tref = $self->{dbh}->selectall_arrayref( "
169         SELECT tags.name
170           FROM tags,bug2tag
171          WHERE tags.tag_id == bug2tag.tag_id
172            AND tags.namespace == ?
173            AND bug2tag.bug_id == ?
174     ", {}, $ns, $self->{id} ) || die "Listing $ns tags on Bug #" . $self->{id};
175
176     return map { $_->[0] } @{$tref};
177 }
178
179 sub set_status {
180     my $self = shift;
181     my $open = shift;
182
183     my $sth = $self->{dbh}->prepare(
184         q{
185         UPDATE bugs
186            SET open=?
187          WHERE bug_id = ?
188     }
189     );
190
191     $sth->execute( $open ? "true" : "false", $self->{id} )
192       or die "Failed to set status";
193     $self->{open} = $open;
194 }
195
196 sub close {
197     my $self = shift;
198     $self->set_status(0);
199 }
200
201 sub reopen {
202     my $self = shift;
203     $self->set_status(1);
204 }
205
206 sub addremove_subthread {
207     my $self = shift;
208     my $add  = shift;
209     my $m    = shift;
210
211     my $sth = $self->{dbh}->prepare(
212         q{
213         INSERT
214           INTO bug2message (include,bug_id,message_id)
215         VALUES (?,?,?)
216     }
217     );
218
219     $sth->execute( $add ? "true" : "false", $self->{id}, $m->{id} )
220       or die "Failed to insert";
221 }
222
223 sub add_subthread {
224     my $self = shift;
225     my $m    = shift;
226
227     $self->addremove_subthread( 1, $m );
228 }
229
230 sub remove_subthread {
231     my $self = shift;
232     my $m    = shift;
233
234     $self->addremove_subthread( 0, $m );
235 }
236
237 sub messages {
238     my $self = shift;
239
240     my $sth = $self->{dbh}->prepare(
241         q{
242         SELECT include,message_id FROM bug2message WHERE bug_id = ? ORDER BY bug2message_id
243     }
244     );
245
246     $sth->execute( $self->{id} );
247
248     my %refs;
249
250     while ( defined( my $row = $sth->fetch ) ) {
251         my ( $inc, $msgid ) = @$row;
252         my $m = Emesinae::Message->lookup_id( $self->{dbh}, $msgid );
253         die "lookup_id failed" unless $m;
254
255         $m->subthread( $inc, \%refs );
256     }
257     return map { $_->{Message} } grep { $_->{Include} eq "true" } values %refs;
258 }
259
260 sub listall ($$;%) {
261     my $class = shift;
262     my $dbh   = shift;
263     my %args  = @_;
264
265     my ( @args, @where );
266
267     my $select = "SELECT DISTINCT $SELECT_FIELDS FROM bugs";
268
269     if    ( $args{State} eq "Open" )   { push @where, qq(open = "true" ) }
270     elsif ( $args{State} eq "Closed" ) { push @where, qq(open = "false" ) }
271
272     if ( @{ $args{Severities} } ) {
273
274 #print "<p>SEVERITIES: ".@{$args{Severities}}." ".join(" ",@{$args{Severities}})."</p>";
275         map { assert_valid_severity($_) } @{ $args{Severities} };
276         push @where,
277           join( " OR ",
278             map { push @args, $_; qq{severity = ?} } @{ $args{Severities} } );
279     }
280
281     if ( @{ $args{Affects} } ) {
282
283  #print "<p>AFFECTS: ".@{$args{Affects}}." ".join(" ",@{$args{Affects}})."</p>";
284         $select .= " JOIN bug2tag ON bug2tag.bug_id == bugs.bug_id ";
285         $select .= " JOIN tags ON bug2tag.tag_id == tags.tag_id ";
286
287         push @where, "tags.namespace = \"affects\"";
288         push @where,
289           join( " OR ",
290             map { push @args, $_; qq{tags.name = ?} } @{ $args{Affects} } );
291     }
292
293     $select .= " WHERE " . join( " AND ", map { qq{($_)} } @where ) if @where;
294     $select .= " ORDER BY bugs.bug_id";
295
296     #print "<p>QUERY: $select</p>";
297     #print "<p>ARGS:".join(",", @args)."</p>";
298
299     my $sth = $dbh->prepare($select);
300     $sth->execute(@args);
301     my @r;
302     while ( my @row = $sth->fetchrow_array ) {
303         push @r, $class->new( $dbh, Fields => \@row );
304     }
305     return @r;
306 }
307
308 1;
309