]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 12282 - General summary reports. Patch by gerv; r=joel.
authorgerv%gerv.net <>
Wed, 25 Sep 2002 14:11:32 +0000 (14:11 +0000)
committergerv%gerv.net <>
Wed, 25 Sep 2002 14:11:32 +0000 (14:11 +0000)
Bugzilla/Search.pm
checksetup.pl
globals.pl
query.cgi
report.cgi [new file with mode: 0755]
t/004template.t
template/en/default/global/code-error.html.tmpl
template/en/default/reports/table.csv.tmpl [new file with mode: 0644]
template/en/default/reports/table.html.tmpl [new file with mode: 0644]
template/en/default/search/search-report-table.html.tmpl [new file with mode: 0644]

index d6e7a9b7f6649898e6c0c959210a0786364d3552..642965eb2c4d33f7ec7a7c0f918c2ce9e59ec281 100644 (file)
@@ -813,6 +813,10 @@ sub init {
             $suppseen{$str} = 1;
         }
     }
+    
+    # Make sure we create a legal SQL query.
+    @andlist = ("1 = 1") if !@andlist;
+    
     my $query =  ("SELECT DISTINCT " . 
                     join(', ', @fields) .
                   ", COUNT(DISTINCT ugmap.group_id) AS cntuseringroups, " .
index b752f9b6564b6fe3ba04b98f5470bd6acebaa10c..27bcf26f9ca4a0a19d4ddd283db20c217381bb17 100755 (executable)
@@ -563,6 +563,7 @@ $contenttypes = {
    "rdf" => "application/xml" , 
    "xml" => "text/xml" , 
     "js" => "application/x-javascript" , 
+   "csv" => "text/plain" ,
 };
 ');
 
@@ -932,6 +933,7 @@ END
          js => sub { return $_; },
          html_linebreak => sub { return $_; },
          url_quote => sub { return $_; },
+         csv => sub { return $_; },
         },
       }) || die ("Could not create Template: " . Template->error() . "\n");
 
index 242c8a5f65416ad053112f0bd58f1d8930b8e0e0..64031bc8534930b150d6b1c09138ec710a24ef64 100644 (file)
@@ -1564,6 +1564,18 @@ $::template ||= Template->new(
         # filter should be used for a full URL that may have
         # characters that need encoding.
         url_quote => \&url_quote ,
+        
+        # In CSV, quotes are doubled, and any value containing a quote or a
+        # comma is enclosed in quotes.
+        csv => sub
+        {
+            my ($var) = @_;
+            $var =~ s/"/""/;
+            if ($var =~ /",/) {
+                $var = "\"$var\"";
+            }
+            return $var;
+        } ,
       } ,
   }
 ) || die("Template creation failed: " . Template->error());
index cd679e8e8602d7909223c502ea015aafa2a2adb3..4bda141c35eb78fbc9d6adfedd4abe1732ac838f 100755 (executable)
--- a/query.cgi
+++ b/query.cgi
@@ -130,7 +130,9 @@ sub PrefillForm {
                       "long_desc", "long_desc_type", "bug_file_loc",
                       "bug_file_loc_type", "status_whiteboard",
                       "status_whiteboard_type", "bug_id",
-                      "bugidtype", "keywords", "keywords_type") {
+                      "bugidtype", "keywords", "keywords_type",
+                      "x_axis_field", "y_axis_field") 
+    {
         # This is a bit of a hack. The default, empty list has 
         # three entries to accommodate the needs of the email fields -
         # we use each position to denote the relevant field. Array
diff --git a/report.cgi b/report.cgi
new file mode 100755 (executable)
index 0000000..5258977
--- /dev/null
@@ -0,0 +1,130 @@
+#!/usr/bonsaitools/bin/perl -wT
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# 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 Bugzilla Bug Tracking System.
+#
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s): Gervase Markham <gerv@gerv.net>
+#                 <rdean@cambianetworks.com>
+
+use diagnostics;
+use strict;
+use lib ".";
+
+require "CGI.pl";
+
+use vars qw($template $vars);
+
+use Bugzilla::Search;
+
+ConnectToDatabase();
+
+GetVersionTable();
+
+quietly_check_login();
+
+# If other report types are added, some of this code can be moved into a sub,
+# and the correct sub chosen based on $::FORM{'type'}.
+
+$::FORM{'y_axis_field'} || ThrowCodeError("no_y_axis_defined");
+
+my $col_field = $::FORM{'x_axis_field'};
+my $row_field = $::FORM{'y_axis_field'};
+
+my %columns;
+$columns{'bug_severity'}     = "bugs.bug_severity";        
+$columns{'priority'}         = "bugs.priority";
+$columns{'rep_platform'}     = "bugs.rep_platform";
+$columns{'assigned_to'}      = "map_assigned_to.login_name";
+$columns{'reporter'}         = "map_reporter.login_name";
+$columns{'qa_contact'}       = "map_qa_contact.login_name";
+$columns{'bug_status'}       = "bugs.bug_status";
+$columns{'resolution'}       = "bugs.resolution";
+$columns{'component'}        = "map_components.name";
+$columns{'product'}          = "map_products.name";
+$columns{'version'}          = "bugs.version";
+$columns{'op_sys'}           = "bugs.op_sys";
+$columns{'votes'}            = "bugs.votes";
+$columns{'keywords'}         = "bugs.keywords";
+$columns{'target_milestone'} = "bugs.target_milestone";
+
+my @axis_fields = ($row_field);
+# The X axis (horizontal) is optional
+push(@axis_fields, $col_field) if $col_field;
+
+my @selectnames = map($columns{$_}, @axis_fields);
+
+my $search = new Bugzilla::Search('fields' => \@selectnames, 
+                                  'url' => $::buffer);
+my $query = $search->getSQL();
+
+$query =~ s/DISTINCT//;
+SendSQL($query, $::userid);
+
+# We have a hash for each direction for the totals, and a hash of hashes for 
+# the data itself.
+my %data;
+my %row_totals;
+my %col_totals;
+my $grand_total;
+
+# Read the bug data and increment the counts.
+while (MoreSQLData()) {
+    my ($row, $col) = FetchSQLData();    
+    $row = "" if !defined($row);
+    $col = "" if !defined($col);
+    
+    $data{$row}{$col}++;    
+    $row_totals{$row}++;    
+    $col_totals{$col}++;    
+    $grand_total++;
+}
+
+$vars->{'data'} = \%data;
+$vars->{'row_totals'} = \%row_totals;
+$vars->{'col_totals'} = \%col_totals;
+$vars->{'grand_total'} = $grand_total;
+
+# Determine the labels for the rows and columns
+my @row_names = sort(keys(%row_totals));
+my @col_names = sort(keys(%col_totals));
+
+$vars->{'row_names'} = \@row_names;
+$vars->{'col_names'} = \@col_names;
+
+$vars->{'row_field'} = $row_field;
+$vars->{'col_field'} = $col_field;
+
+$::buffer =~ s/format=[^&]*&?//g;
+
+# Calculate the base query URL for the hyperlinked numbers
+my $buglistbase = $::buffer;
+$buglistbase =~ s/$row_field=[^&]*&?//g;
+$buglistbase =~ s/$col_field=[^&]*&?//g;
+
+$vars->{'buglistbase'} = $buglistbase;
+$vars->{'buffer'} = $::buffer;
+
+$::FORM{'type'} =~ s/[^a-zA-Z\-]//g;
+
+# Generate and return the result from the appropriate template.
+my $format = GetFormat("reports/$::FORM{'type'}", 
+                       $::FORM{'format'}, 
+                       $::FORM{'ctype'});
+print "Content-Type: $format->{'contenttype'}\n\n";
+$template->process("$format->{'template'}", $vars)
+  || ThrowTemplateError($template->error());
index 41d515435a732fb8f272f53b1929eafe09e9c3b1..02541d351a482fc076fbdea4d5867576f3db5041 100644 (file)
@@ -81,6 +81,7 @@ my $template = Template->new(
         js        => sub { return $_ } ,
         strike    => sub { return $_ } ,
         url_quote => sub { return $_ } ,
+        csv       => sub { return $_ } ,
     },
 }
 );
index 55fac2acaf43ef7e0c1804d4310f2125f1d487c2..bf93977adc4b600a23c8010463d23f8ad6be14c6 100644 (file)
   [% ELSIF error == "no_bug_data" %]
     No data when fetching bug [% bug_id %].
     
+  [% ELSIF error == "no_y_axis_defined" %]
+    No Y axis was defined when creating report. The X axis is optional,
+    but the Y axis is compulsory.
+    
   [% ELSIF error == "template_error" %]
     [% template_error_msg %]
 
diff --git a/template/en/default/reports/table.csv.tmpl b/template/en/default/reports/table.csv.tmpl
new file mode 100644 (file)
index 0000000..96b0c39
--- /dev/null
@@ -0,0 +1,41 @@
+[%# 1.0@bugzilla.org %]
+[%# 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 Bugzilla Bug Tracking System.
+  #
+  # The Initial Developer of the Original Code is Netscape Communications
+  # Corporation. Portions created by Netscape are
+  # Copyright (C) 1998 Netscape Communications Corporation. All
+  # Rights Reserved.
+  #
+  # Contributor(s): Gervase Markham <gerv@gerv.net>
+  #%]
+[%# INTERFACE:
+  # See report.html.tmpl.
+  #%]
+[% row_field FILTER csv -%]
+
+[% IF col_field -%]
+ \ [% col_field FILTER csv -%],
+[% FOREACH col = col_names -%]
+[% col FILTER csv -%],
+[% END -%]
+[% ELSE -%]
+,Number of bugs,
+[% END %]
+
+[% FOREACH row = row_names %]
+[% row FILTER csv -%],
+[% FOREACH col = col_names %]
+[% IF data.$row AND data.$row.$col %][% data.$row.$col -%],[% ELSE %]0,[% END %]
+[% END %]
+
+[% END %]
diff --git a/template/en/default/reports/table.html.tmpl b/template/en/default/reports/table.html.tmpl
new file mode 100644 (file)
index 0000000..d9a04d5
--- /dev/null
@@ -0,0 +1,143 @@
+ <!-- 1.0@bugzilla.org -->
+[%# 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 Bugzilla Bug Tracking System.
+  #
+  # The Initial Developer of the Original Code is Netscape Communications
+  # Corporation. Portions created by Netscape are
+  # Copyright (C) 1998 Netscape Communications Corporation. All
+  # Rights Reserved.
+  #
+  # Contributor(s): Gervase Markham <gerv@gerv.net>
+  #                 <rdean@cambianetworks.com>
+  #%]
+
+[%# INTERFACE:
+  # basequery: The base query for this table, in URL form
+  # row_field: string. The field name for the data in table rows
+  # col_field: string. The field name for the data in table columns
+  # col_names: array of strings. Values for the columns
+  # row_names: array of strings. Values for the rows
+  # col_totals: hash of integers. Totals for the columns, indexed by col_names.
+  # row_totals: hash of integers. Totals for the rows, indexed by row_names.
+  # data: hash of hash of numbers. Bug counts indexed by col_names and 
+  #       row_names values.
+  #%]
+
+[% PROCESS global/header.html.tmpl 
+  title = "Report"
+  onload = "selectProduct(document.forms['queryform']);"
+  style = "
+    .t1     { background-color: #ffffff }
+    .t2     { background-color: #dfefff }
+    .t3     { background-color: #dddddd }
+    .t4     { background-color: #c3d3ed }
+    .ttotal { background-color: #cfffdf }
+  "
+%]
+
+<div align="center">
+  <table>
+    <tr>
+      <td>
+      </td>
+      <td align="center">
+        <strong>[% col_field FILTER html %]</strong>
+      </td>
+    </tr>
+
+    <tr>
+      <td valign="middle">
+        <strong>[% row_field FILTER html %]</strong>
+      </td>
+      <td>
+
+
+[% classes = [ [ "t1", "t2" ] , [ "t3", "t4" ] ] %]
+[% col_idx = 0 %]
+[% row_idx = 0 %]
+
+<table>
+  [% IF col_names %]
+    <tr>
+      <td class="[% classes.$row_idx.$col_idx %]">
+      </td>
+      [% FOREACH col = col_names %]
+        [%# If no col header, skip the col. This makes display look right if
+            there's no defined X axis. Not doing this gives us two cols. %]
+        [% NEXT IF col == "" %] 
+        [% col_idx = 1 - col_idx %]
+        <td class="[% classes.$row_idx.$col_idx %]">
+          [% col FILTER html %]
+        </td>
+      [% END %]
+      <td class="ttotal">
+        Total
+      </td>
+    </tr>
+  [% END %]
+  
+  [% FOREACH row = row_names %]
+    [% row_idx = 1 - row_idx %]
+    <tr>
+      <td class="[% classes.$row_idx.$col_idx %]">
+        [% row FILTER html %]
+      </td>
+      [% FOREACH col = col_names %]
+        [% NEXT IF col == "" %]
+        [% col_idx = 1 - col_idx %]
+        <td class="[% classes.$row_idx.$col_idx %]" align="center">
+          [% IF data.$row.$col AND data.$row.$col > 0 %]
+            <a href="buglist.cgi?[% buglistbase %]&
+              [% row_field FILTER url_quote %]=[% row FILTER url_quote %]&
+              [% col_field FILTER url_quote %]=[% col FILTER url_quote %]">
+              [% data.$row.$col %]</a>
+          [% ELSE %]
+            .
+          [% END %]
+        </td>
+      [% END %] 
+      <td class="ttotal">
+        [% row_totals.$row %]
+      </td>
+    </tr>
+  [% END %]
+  
+  <tr>
+    [% row_idx = 1 - row_idx %]
+    <td class="ttotal">
+      Total
+    </td>
+    [% FOREACH col = col_names %]
+      [% NEXT IF col == "" %]
+      <td class="ttotal" align="center">
+        [% col_totals.$col %]
+      </td> 
+    [% END %]
+    <td class="ttotal">
+      <strong>
+        [% grand_total %]
+      </strong>
+    </td>
+  </tr>
+</table>
+      
+      
+      </td>
+    </tr>
+  </table>
+  
+  <a href="query.cgi?[% buffer %]&format=report-table">Edit this report</a>
+</div>
+
+<br>
+
+[% PROCESS global/footer.html.tmpl %]
diff --git a/template/en/default/search/search-report-table.html.tmpl b/template/en/default/search/search-report-table.html.tmpl
new file mode 100644 (file)
index 0000000..32f8161
--- /dev/null
@@ -0,0 +1,125 @@
+<!-- 1.0@bugzilla.org -->
+[%# 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 Bugzilla Bug Tracking System.
+  #
+  # The Initial Developer of the Original Code is Netscape Communications
+  # Corporation. Portions created by Netscape are
+  # Copyright (C) 1998 Netscape Communications Corporation. All
+  # Rights Reserved.
+  #
+  # Contributor(s): Gervase Markham <gerv@gerv.net>
+  #%]
+
+[%# INTERFACE:
+  # This template has no interface. However, to use it, you need to fulfill
+  # the interfaces of the templates it contains.
+  #%]
+
+[% PROCESS global/header.html.tmpl 
+  title = "Generate Report"
+  onload = "selectProduct(document.forms['reportform']);"
+%]
+
+<p>
+  Produce a table of bug counts by choosing two fields to plot against each 
+  other, and then refining your set of bugs using the rest of the form.
+</p>
+
+[% button_name = "Generate Report" %]
+
+<form method="get" action="report.cgi" name="reportform">
+
+<table>
+  <tr>
+    <th align="center">
+      Vertical Axis
+    </th>
+    <th align="center">
+      Horizontal Axis
+    </th>
+    <th>
+      &nbsp;&nbsp;
+    </th>
+    <th align="center">
+      Format
+    </th>
+  </tr>
+  
+  <tr>
+    <td align="center">
+      [% PROCESS select sel = { name => 'y_axis_field' } %]
+    </td>
+    <td align="center">  
+      [% PROCESS select sel = { name => 'x_axis_field', noop = 1 } %]
+    </td>
+    <td>
+      &nbsp;&nbsp;
+    </td>
+    <td>
+      <input type="radio" name="ctype" value="html" checked>HTML
+      <input type="radio" name="ctype" value="csv">CSV
+    </td>
+  </tr>
+</table>
+
+<hr>
+
+[% PROCESS search/form.html.tmpl %]
+
+<br>
+<input type="submit" value="[% button_name %]">
+<input type="hidden" name="type" value="table">
+<hr>
+
+[% PROCESS "search/boolean-charts.html.tmpl" %]
+
+</form>
+
+[% PROCESS global/footer.html.tmpl %]
+
+[%############################################################################%]
+[%# Block for SELECT fields                                                  #%]
+[%############################################################################%]
+
+[% BLOCK select %]
+  [% fields = [
+    { name => "",                 description => "---" },
+    { name => "product",          description => "Product" },
+    { name => "component",        description => "Component" },
+    { name => "version",          description => "Version" },
+    { name => "rep_platform",     description => "Platform" },
+    { name => "op_sys",           description => "OS" },
+    { name => "bug_status",       description => "Status" },
+    { name => "resolution",       description => "Resolution" },
+    { name => "bug_severity",     description => "Severity" },
+    { name => "priority",         description => "Priority" },
+    { name => "target_milestone", description => "Target Milestone" },
+    { name => "keywords",         description => "Keywords" },
+    { name => "assigned_to",      description => "Assignee" },
+    { name => "reporter",         description => "Reporter" },
+    { name => "qa_contact",       description => "QA Contact" },
+    { name => "votes",            description => "Votes" } ] %]
+
+  <select name="[% sel.name %]">
+    [% FOREACH field = fields %]
+      [% NEXT IF field.name == "" AND !sel.noop %]
+      [% NEXT IF field.name == "target_milestone" AND
+                               !Param('usetargetmilestone') %]
+      [% NEXT IF field.name == "qa_contact" AND !Param('useqacontact') %]
+      [% NEXT IF field.name == "votes" AND !Param('usevotes') %]
+      
+      <option value="[% field.name FILTER html %]" 
+        [% " selected" IF default.${sel.name}.0 == field.name %]>
+        [% field.description FILTER html %]</option>
+    [% END %]
+  </select>
+[% END %]