summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--extensions/BMO/Extension.pm9
-rw-r--r--extensions/BMO/lib/Reports/Triage.pm134
-rw-r--r--extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl19
-rw-r--r--extensions/BMO/template/en/default/pages/triage_owners.html.tmpl136
-rw-r--r--extensions/BMO/web/js/triage_owners.js54
-rw-r--r--extensions/BMO/web/styles/triage_reports.css22
6 files changed, 364 insertions, 10 deletions
diff --git a/extensions/BMO/Extension.pm b/extensions/BMO/Extension.pm
index da62861ca..e54a674ba 100644
--- a/extensions/BMO/Extension.pm
+++ b/extensions/BMO/Extension.pm
@@ -190,9 +190,14 @@ sub page_before_template {
require Bugzilla::Extension::BMO::Reports::UserActivity;
Bugzilla::Extension::BMO::Reports::UserActivity::report($vars);
- } elsif ($page eq 'triage_reports.html') {
+ }
+ elsif ($page eq 'triage_reports.html') {
+ require Bugzilla::Extension::BMO::Reports::Triage;
+ Bugzilla::Extension::BMO::Reports::Triage::unconfirmed($vars);
+ }
+ elsif ($page eq 'triage_owners.html') {
require Bugzilla::Extension::BMO::Reports::Triage;
- Bugzilla::Extension::BMO::Reports::Triage::report($vars);
+ Bugzilla::Extension::BMO::Reports::Triage::owners($vars);
}
elsif ($page eq 'group_admins.html') {
require Bugzilla::Extension::BMO::Reports::Groups;
diff --git a/extensions/BMO/lib/Reports/Triage.pm b/extensions/BMO/lib/Reports/Triage.pm
index e9a987b54..90c77459f 100644
--- a/extensions/BMO/lib/Reports/Triage.pm
+++ b/extensions/BMO/lib/Reports/Triage.pm
@@ -16,13 +16,24 @@ use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Product;
use Bugzilla::User;
-use Bugzilla::Util qw(detaint_natural);
+use Bugzilla::Util qw(detaint_natural trim);
use Date::Parse;
+use JSON::XS;
+use List::MoreUtils qw(any);
+
# set an upper limit on the *unfiltered* number of bugs to process
use constant MAX_NUMBER_BUGS => 4000;
-sub report {
+use constant DEFAULT_OWNER_PRODUCTS => (
+ 'Core',
+ 'Firefox',
+ 'Firefox for Android',
+ 'Firefox for iOS',
+ 'Toolkit',
+);
+
+sub unconfirmed {
my ($vars, $filter) = @_;
my $dbh = Bugzilla->dbh;
my $input = Bugzilla->input_params;
@@ -217,4 +228,123 @@ sub report {
$vars->{'input'} = $input;
}
+sub owners {
+ my ($vars, $filter) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $input = Bugzilla->input_params;
+ my $user = Bugzilla->user;
+
+ Bugzilla::User::match_field({ 'owner' => {'type' => 'multi'} });
+
+ my @products;
+ if (!$input->{product} && $input->{owner}) {
+ @products = @{ $user->get_selectable_products };
+ }
+ else {
+ my @product_names = $input->{product} ? ($input->{product}) : DEFAULT_OWNER_PRODUCTS;
+ foreach my $name (@product_names) {
+ push(@products, Bugzilla::Product->check({ name => $name }));
+ }
+ }
+
+ my @component_ids;
+ if (@products == 1 && $input->{'component'}) {
+ my $ra_components = ref($input->{'component'})
+ ? $input->{'component'}
+ : [ $input->{'component'} ];
+ foreach my $component_name (@$ra_components) {
+ my $component = Bugzilla::Component->check({ name => $component_name, product => $products[0] });
+ push @component_ids, $component->id;
+ }
+ }
+
+ my @owner_names = split(/[,;]+/, $input->{owner}) if $input->{owner};
+ my @owner_ids;
+ foreach my $name (@owner_names) {
+ $name = trim($name);
+ next unless $name;
+ push(@owner_ids, login_to_id($name, THROW_ERROR));
+ }
+
+ my $sql = "SELECT products.name, components.name, components.id, components.triage_owner_id
+ FROM components JOIN products ON components.product_id = products.id
+ WHERE products.id IN (" . join(',', map { $_->id } @products) . ")";
+ if (@component_ids) {
+ $sql .= " AND components.id IN (" . join(',', @component_ids) . ")";
+ }
+ if (@owner_ids) {
+ $sql .= " AND components.triage_owner_id IN (" . join(',', @owner_ids) . ")";
+ }
+ $sql .= " ORDER BY products.name, components.name";
+
+ my $rows = $dbh->selectall_arrayref($sql);
+
+ my $bug_count_sth = $dbh->prepare("
+ SELECT COUNT(bugs.bug_id)
+ FROM bugs INNER JOIN components AS map_component ON bugs.component_id = map_component.id
+ INNER JOIN bug_status AS map_bug_status ON bugs.bug_status = map_bug_status.value
+ INNER JOIN priority AS map_priority ON bugs.priority = map_priority.value
+ WHERE bugs.resolution IN ('')
+ AND bugs.priority IN ('--')
+ AND bugs.creation_ts >= '2016-06-01'
+ AND (NOT( EXISTS (
+ SELECT 1
+ FROM bugs bugs_1
+ LEFT JOIN attachments AS attachments_1 ON bugs_1.bug_id = attachments_1.bug_id
+ LEFT JOIN flags AS flags_1 ON bugs_1.bug_id = flags_1.bug_id AND (flags_1.attach_id = attachments_1.attach_id OR flags_1.attach_id IS NULL)
+ LEFT JOIN flagtypes AS flagtypes_1 ON flags_1.type_id = flagtypes_1.id
+ WHERE bugs_1.bug_id = bugs.bug_id AND CONCAT(flagtypes_1.name, flags_1.status) = 'needinfo?')))
+ AND bugs.component_id = ?");
+
+ my @results;
+ foreach my $row (@$rows) {
+ my ($product_name, $component_name, $component_id, $triage_owner_id) = @$row;
+ my $triage_owner = $triage_owner_id
+ ? Bugzilla::User->new({ id => $triage_owner_id, cache => 1 })
+ : "";
+ my $data = {
+ product => $product_name,
+ component => $component_name,
+ owner => $triage_owner,
+ };
+ $data->{buglist_url} = 'priority=--&resolution=---&f1=creation_ts&o1=greaterthaneq&v1=2016-06-01'.
+ '&f2=flagtypes.name&o2=notequals&v2=needinfo%3F';
+ if ($triage_owner) {
+ $data->{buglist_url} .= '&f3=triage_owner&o3=equals&v3=' . $triage_owner->login;
+ }
+ $bug_count_sth->execute($component_id);
+ ($data->{bug_count}) = $bug_count_sth->fetchrow_array();
+ push @results, $data;
+ }
+ $vars->{results} = \@results;
+
+ my $json_data = { products => [] };
+ foreach my $product (@{ $user->get_selectable_products }) {
+ my $prod_data = {
+ name => $product->name,
+ components => [],
+ };
+ foreach my $component (@{ $product->components }) {
+ my $selected = 0;
+ if ($input->{product}
+ && $input->{product} eq $product->name
+ && $input->{component})
+ {
+ $selected = 1 if (ref $input->{component} && any { $_ eq $component->name } @{ $input->{component} });
+ $selected = 1 if (!ref $input->{componet} && $input->{component} eq $component->name);
+ }
+ my $comp_data = {
+ name => $component->name,
+ selected => $selected
+ };
+ push(@{ $prod_data->{components} }, $comp_data);
+ }
+ push(@{ $json_data->{products} }, $prod_data);
+ }
+
+ $vars->{product} = $input->{product};
+ $vars->{owner} = $input->{owner};
+ $vars->{json_data} = encode_json($json_data);
+}
+
1;
diff --git a/extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl b/extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl
index 6d03e9284..825263350 100644
--- a/extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl
+++ b/extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl
@@ -6,6 +6,20 @@
# defined by the Mozilla Public License, v. 2.0.
#%]
+<h2>Triage Reports</h2>
+<ul>
+ <li>
+ <strong>
+ <a href="[% urlbase FILTER none %]page.cgi?id=triage_reports.html">Unconfirmed Report</a>
+ </strong> - Report on UNCONFIRMED [% terms.bugs %] to assist triage.
+ </li>
+ <li>
+ <strong>
+ <a href="[% urlbase FILTER none %]page.cgi?id=triage_owners.html">Triage Owners</a>
+ </strong> - Report on triage owners per product and component.
+ </li>
+</ul>
+
<h2>Other Reports</h2>
<ul>
@@ -16,11 +30,6 @@
</li>
<li>
<strong>
- <a href="[% urlbase FILTER none %]page.cgi?id=triage_reports.html">Unconfirmed Report</a>
- </strong> - Report on UNCONFIRMED [% terms.bugs %] to assist triage.
- </li>
- <li>
- <strong>
<a href="[% urlbase FILTER none %]page.cgi?id=release_tracking_report.html">Release Tracking Report</a>
</strong> - For triaging release-train flag information.
</li>
diff --git a/extensions/BMO/template/en/default/pages/triage_owners.html.tmpl b/extensions/BMO/template/en/default/pages/triage_owners.html.tmpl
new file mode 100644
index 000000000..3663c2925
--- /dev/null
+++ b/extensions/BMO/template/en/default/pages/triage_owners.html.tmpl
@@ -0,0 +1,136 @@
+[%# The contents of this file are subject to the Mozilla Public
+ # License Version 1.1 (the "License"); you may not use this file
+ # except in compliance with the License. You may obtain a copy of
+ # the License at http://www.mozilla.org/MPL/
+ #
+ # Software distributed under the License is distributed on an "AS
+ # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ # implied. See the License for the specific language governing
+ # rights and limitations under the License.
+ #
+ # The Original Code is the BMO Extension
+ #
+ # The Initial Developer of the Original Code is the Mozilla Foundation
+ # Portions created by the Initial Developers are Copyright (C) 2011 the
+ # Initial Developer. All Rights Reserved.
+ #
+ # Contributor(s):
+ # Byron Jones <bjones@mozilla.com>
+ #%]
+
+[% PROCESS global/variables.none.tmpl %]
+
+[% INCLUDE global/header.html.tmpl
+ title = "Triage Owners"
+ javascript_urls = [ "js/util.js", "js/field.js", "js/productform.js",
+ "extensions/BMO/web/js/triage_owners.js" ]
+ style_urls = [ "skins/standard/buglist.css",
+ "extensions/BMO/web/styles/triage_reports.css" ]
+ generate_api_token = 1
+%]
+
+<noscript>
+<h2>Javascript is required to use this report.</h2>
+</noscript>
+
+[% PROCESS "global/field-descs.none.tmpl" %]
+
+<form id="triageOwners" name="triageOwners" action="page.cgi" method="GET">
+ <input type="hidden" name="id" value="triage_owners.html">
+ <input type="hidden" name="json_data" id="json_data" data-json_data="[% json_data FILTER html %]">
+
+ <h3>Show Triage Owners</h3>
+
+ <table id="triage_owners_form">
+ <tr>
+ <th>Product:</th>
+ <td>
+ <select name="product" id="product">
+ <option value="">__Any__</option>
+ [% FOREACH p = user.get_selectable_products %]
+ <option value="[% p.name FILTER html %]"
+ [% " selected" IF product == p.name %]>
+ [% p.name FILTER html %]
+ </option>
+ [% END %]
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <th>Component:</th>
+ <td>
+ <select name="component" id="component" multiple size="5"></select>
+ </td>
+ </tr>
+ <tr>
+ <th>Owner:</th>
+ <td>
+ [% INCLUDE global/userselect.html.tmpl
+ id = "owner"
+ name = "owner"
+ value = owner
+ size = 40
+ classes = ["bz_userfield"]
+ multiple = 5
+ %]
+ </td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td>
+ <input type="submit" value="Generate Report">
+ </td>
+ </tr>
+ </table>
+</form>
+
+[% IF NOT product %]
+ <p>Displaying components from default product list.</p>
+[% END %]
+
+[% IF results.size > 0 %]
+ <p>
+ <small>Each triage owner links to a buglist of all open [% terms.bugs %], since 2016-06-01, without a pending needinfo, where the priority is '--'.</small>
+ </p>
+ [% current_product = "" %]
+ <table border="0" cellspacing="0" id="report" width="100%">
+ </tr>
+ [% FOREACH r = results %]
+ [% count = loop.count() %]
+ [% IF current_product != r.product %]
+ [% current_product = r.product %]
+ <tr class="product_header">
+ <th colspan="3">[% r.product FILTER html %]</th>
+ </tr>
+ [% END %]
+ <tr class="bz_bugitem [% count % 2 == 1 ? "bz_row_odd" : "bz_row_even" %]">
+ <td>
+ [% r.component FILTER html %]
+ </td>
+ <td>
+ [% IF r.owner.id %]
+ [% INCLUDE global/user.html.tmpl who = r.owner %]
+ [% ELSE %]
+ <em>None</em>
+ [% END %]
+ </td>
+ <td>
+ [% IF r.buglist_url %]
+ <a href="[% urlbase FILTER none %]buglist.cgi?product=[% r.product FILTER uri %]&component=[% r.component FILTER uri %]&[% r.buglist_url FILTER none %]">
+ [% r.bug_count FILTER html +%] [%+ terms.bugs %] found.
+ </a>
+ [% ELSE %]
+ None
+ [% END %]
+ </td>
+ </tr>
+ [% END %]
+ </table>
+ <p>
+ Found [% results.size %] component[% 's' IF results.size != 1 %]:
+ </p>
+[% ELSE %]
+ <p>No components found.</p>
+[% END %]
+
+[% INCLUDE global/footer.html.tmpl %]
diff --git a/extensions/BMO/web/js/triage_owners.js b/extensions/BMO/web/js/triage_owners.js
new file mode 100644
index 000000000..212225586
--- /dev/null
+++ b/extensions/BMO/web/js/triage_owners.js
@@ -0,0 +1,54 @@
+/* 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. */
+
+$(function() {
+ 'use strict';
+
+ var json_data = {};
+
+ function onSelectProduct() {
+ var component = $('#component');
+ var product = $('#product');
+
+ if (product.val() == '') {
+ component.empty();
+ return;
+ }
+
+ if (!json_data) return;
+
+ component.empty();
+ component.append(new Option('__Any__', ''));
+
+ var products = json_data.products;
+ for (var i = 0, l = products.length; i < l; i++) {
+ if (products[i].name != product.val()) continue;
+ var components = products[i].components;
+ for (var j = 0, k = components.length; j < k; j++) {
+ var selected = !!components[j].selected;
+ component.append(new Option(components[j].name,
+ components[j].name,
+ selected, selected));
+ }
+ }
+ }
+
+ $('#product').change(function() {
+ onSelectProduct();
+ });
+
+ $('#triageOwners').submit(function() {
+ // do not pass json_data in the params
+ $('#json_data').remove();
+ return true;
+ });
+
+ $(document).ready(function () {
+ json_data = $('#json_data').data('json_data');
+ onSelectProduct();
+ });
+});
diff --git a/extensions/BMO/web/styles/triage_reports.css b/extensions/BMO/web/styles/triage_reports.css
index 6190fd32c..8eb2c6e87 100644
--- a/extensions/BMO/web/styles/triage_reports.css
+++ b/extensions/BMO/web/styles/triage_reports.css
@@ -1,3 +1,10 @@
+/* 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. */
+
.hidden {
display: none;
}
@@ -14,10 +21,23 @@
background: #ccccff;
}
-#report td {
+#report th, #report td {
padding: 1px 10px 1px 10px;
}
#report-header {
background: #dddddd;
}
+
+tr.product_header {
+ background: #dddddd;
+}
+
+#triage_owners_form th {
+ text-align: right;
+ vertical-align: top;
+}
+
+#report th, #report td {
+ text-align: left;
+}