]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1309965 - Report: List of Triage Owners which can be sorted and filtered on produ...
authorDavid Lawrence <dkl@mozilla.com>
Thu, 27 Oct 2016 10:42:51 +0000 (10:42 +0000)
committerDavid Lawrence <dkl@mozilla.com>
Thu, 27 Oct 2016 10:42:51 +0000 (10:42 +0000)
extensions/BMO/Extension.pm
extensions/BMO/lib/Reports/Triage.pm
extensions/BMO/template/en/default/hook/reports/menu-end.html.tmpl
extensions/BMO/template/en/default/pages/triage_owners.html.tmpl [new file with mode: 0644]
extensions/BMO/web/js/triage_owners.js [new file with mode: 0644]
extensions/BMO/web/styles/triage_reports.css

index da62861ca7e702f67ba468425fa8101b9820de59..e54a674baecdff46b808c35ec145f0e1e793d13d 100644 (file)
@@ -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;
index e9a987b5488b98c44b604579d2859d0420b2de77..90c77459fd592fa3938b61b0748d29d2a8e553bc 100644 (file)
@@ -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;
index 6d03e92842e7db23fc8e2cc6b1a839cde57d49db..8252633504e3bc27475d3199c81fb2dd959ecac2 100644 (file)
@@ -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>
       <a href="[% urlbase FILTER none %]page.cgi?id=user_activity.html">User Changes</a>
     </strong> - Show changes made by an individual user.
   </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>
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 (file)
index 0000000..3663c29
--- /dev/null
@@ -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 (file)
index 0000000..2122255
--- /dev/null
@@ -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();
+    });
+});
index 6190fd32c010f6ee993d395ac6588514b4a2160b..8eb2c6e8714ce802bfd52bbdeff67f3a213058e5 100644 (file)
@@ -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;
 }
   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;
+}