From 290029c1c1ba8b3f1ef18b56c94f947bd550d056 Mon Sep 17 00:00:00 2001 From: Byron Jones Date: Wed, 24 Apr 2013 12:52:26 +0800 Subject: Bug 853483: Triage report times out on Firefox (Any) query --- extensions/BMO/lib/Reports/Triage.pm | 217 +++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 extensions/BMO/lib/Reports/Triage.pm (limited to 'extensions/BMO/lib/Reports/Triage.pm') diff --git a/extensions/BMO/lib/Reports/Triage.pm b/extensions/BMO/lib/Reports/Triage.pm new file mode 100644 index 000000000..debb50577 --- /dev/null +++ b/extensions/BMO/lib/Reports/Triage.pm @@ -0,0 +1,217 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Extension::BMO::Reports::Triage; +use strict; + +use Bugzilla::Component; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Product; +use Bugzilla::User; +use Bugzilla::Util qw(detaint_natural); +use Date::Parse; + +# set an upper limit on the *unfiltered* number of bugs to process +use constant MAX_NUMBER_BUGS => 4000; + +sub report { + my ($vars, $filter) = @_; + my $dbh = Bugzilla->dbh; + my $input = Bugzilla->input_params; + my $user = Bugzilla->user; + + if (exists $input->{'action'} && $input->{'action'} eq 'run' && $input->{'product'}) { + + # load product and components from input + + my $product = Bugzilla::Product->new({ name => $input->{'product'} }) + || ThrowUserError('invalid_object', { object => 'Product', value => $input->{'product'} }); + + my @component_ids; + if ($input->{'component'} ne '') { + my $ra_components = ref($input->{'component'}) + ? $input->{'component'} : [ $input->{'component'} ]; + foreach my $component_name (@$ra_components) { + my $component = Bugzilla::Component->new({ name => $component_name, product => $product }) + || ThrowUserError('invalid_object', { object => 'Component', value => $component_name }); + push @component_ids, $component->id; + } + } + + # determine which comment filters to run + + my $filter_commenter = $input->{'filter_commenter'}; + my $filter_commenter_on = $input->{'commenter'}; + my $filter_last = $input->{'filter_last'}; + my $filter_last_period = $input->{'last'}; + + if (!$filter_commenter || $filter_last) { + $filter_commenter = '1'; + $filter_commenter_on = 'reporter'; + } + + my $filter_commenter_id; + if ($filter_commenter && $filter_commenter_on eq 'is') { + Bugzilla::User::match_field({ 'commenter_is' => {'type' => 'single'} }); + my $user = Bugzilla::User->new({ name => $input->{'commenter_is'} }) + || ThrowUserError('invalid_object', { object => 'User', value => $input->{'commenter_is'} }); + $filter_commenter_id = $user ? $user->id : 0; + } + + my $filter_last_time; + if ($filter_last) { + if ($filter_last_period eq 'is') { + $filter_last_period = -1; + $filter_last_time = str2time($input->{'last_is'} . " 00:00:00") || 0; + } else { + detaint_natural($filter_last_period); + $filter_last_period = 14 if $filter_last_period < 14; + } + } + + # form sql queries + + my $now = (time); + my $bugs_sql = " + SELECT bug_id, short_desc, reporter, creation_ts + FROM bugs + WHERE product_id = ? + AND bug_status = 'UNCONFIRMED'"; + if (@component_ids) { + $bugs_sql .= " AND component_id IN (" . join(',', @component_ids) . ")"; + } + $bugs_sql .= " + ORDER BY creation_ts + "; + + my $comment_count_sql = " + SELECT COUNT(*) + FROM longdescs + WHERE bug_id = ? + "; + + my $comment_sql = " + SELECT who, bug_when, type, thetext, extra_data + FROM longdescs + WHERE bug_id = ? + "; + if (!Bugzilla->user->is_insider) { + $comment_sql .= " AND isprivate = 0 "; + } + $comment_sql .= " + ORDER BY bug_when DESC + LIMIT 1 + "; + + my $attach_sql = " + SELECT description, isprivate + FROM attachments + WHERE attach_id = ? + "; + + # work on an initial list of bugs + + my $list = $dbh->selectall_arrayref($bugs_sql, undef, $product->id); + my @bugs; + + # this can be slow to process, resulting in 'service unavailable' errors from zeus + # so if too many bugs are returned, throw an error + + if (scalar(@$list) > MAX_NUMBER_BUGS) { + ThrowUserError('report_too_many_bugs'); + } + + foreach my $entry (@$list) { + my ($bug_id, $summary, $reporter_id, $creation_ts) = @$entry; + + next unless $user->can_see_bug($bug_id); + + # get last comment information + + my ($comment_count) = $dbh->selectrow_array($comment_count_sql, undef, $bug_id); + my ($commenter_id, $comment_ts, $type, $comment, $extra) + = $dbh->selectrow_array($comment_sql, undef, $bug_id); + my $commenter = 0; + + # apply selected filters + + if ($filter_commenter) { + next if $comment_count <= 1; + + if ($filter_commenter_on eq 'reporter') { + next if $commenter_id != $reporter_id; + + } elsif ($filter_commenter_on eq 'noconfirm') { + $commenter = Bugzilla::User->new({ id => $commenter_id, cache => 1 }); + next if $commenter_id != $reporter_id + || $commenter->in_group('canconfirm'); + + } elsif ($filter_commenter_on eq 'is') { + next if $commenter_id != $filter_commenter_id; + } + } else { + $input->{'commenter'} = ''; + $input->{'commenter_is'} = ''; + } + + if ($filter_last) { + my $comment_time = str2time($comment_ts) + or next; + if ($filter_last_period == -1) { + next if $comment_time >= $filter_last_time; + } else { + next if $now - $comment_time <= 60 * 60 * 24 * $filter_last_period; + } + } else { + $input->{'last'} = ''; + $input->{'last_is'} = ''; + } + + # get data for attachment comments + + if ($comment eq '' && $type == CMT_ATTACHMENT_CREATED) { + my ($description, $is_private) = $dbh->selectrow_array($attach_sql, undef, $extra); + next if $is_private && !Bugzilla->user->is_insider; + $comment = "(Attachment) " . $description; + } + + # truncate long comments + + if (length($comment) > 80) { + $comment = substr($comment, 0, 80) . '...'; + } + + # build bug hash for template + + my $bug = {}; + $bug->{id} = $bug_id; + $bug->{summary} = $summary; + $bug->{reporter} = Bugzilla::User->new({ id => $reporter_id, cache => 1 }); + $bug->{creation_ts} = $creation_ts; + $bug->{commenter} = $commenter || Bugzilla::User->new({ id => $commenter_id, cache => 1 }); + $bug->{comment_ts} = $comment_ts; + $bug->{comment} = $comment; + $bug->{comment_count} = $comment_count; + push @bugs, $bug; + } + + @bugs = sort { $b->{comment_ts} cmp $a->{comment_ts} } @bugs; + + $vars->{bugs} = \@bugs; + } else { + $input->{action} = ''; + } + + if (!$input->{filter_commenter} && !$input->{filter_last}) { + $input->{filter_commenter} = 1; + } + + $vars->{'input'} = $input; +} + +1; -- cgit v1.2.3-24-g4f1b