]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 173005 - Add bar charts, pie charts etc. to reporting. Patch by gerv; 2xr=joel.
authorgerv%gerv.net <>
Tue, 29 Oct 2002 15:43:57 +0000 (15:43 +0000)
committergerv%gerv.net <>
Tue, 29 Oct 2002 15:43:57 +0000 (15:43 +0000)
18 files changed:
checksetup.pl
query.cgi
report.cgi
template/en/default/global/code-error.html.tmpl
template/en/default/global/field-descs.html.tmpl
template/en/default/global/user-error.html.tmpl
template/en/default/index.html.tmpl
template/en/default/reports/menu.html.tmpl
template/en/default/reports/report-bar.png.tmpl [new file with mode: 0644]
template/en/default/reports/report-line.png.tmpl [new file with mode: 0644]
template/en/default/reports/report-pie.png.tmpl [new file with mode: 0644]
template/en/default/reports/report-table.csv.tmpl
template/en/default/reports/report-table.html.tmpl
template/en/default/reports/report.csv.tmpl [new file with mode: 0644]
template/en/default/reports/report.html.tmpl [new file with mode: 0644]
template/en/default/search/search-report-graph.html.tmpl [new file with mode: 0644]
template/en/default/search/search-report-select.html.tmpl [new file with mode: 0644]
template/en/default/search/search-report-table.html.tmpl

index 1acec457c32e42d22ce29267be9e172a1aba570d..58c2c130fefeedce893871955ed07181005d4c6f 100755 (executable)
@@ -258,25 +258,38 @@ foreach my $module (@{$modules}) {
 }
 
 print "\nThe following Perl modules are optional:\n" unless $silent;
-my $charts = 0;
-$charts++ if have_vers("GD","1.19");
-$charts++ if have_vers("Chart::Base","0.99");
-my $xmlparser = have_vers("XML::Parser",0);
+my $gd          = have_vers("GD","1.20");
+my $chartbase   = have_vers("Chart::Base","0.99");
+my $xmlparser   = have_vers("XML::Parser",0);
+my $gdgraph     = have_vers("GD::Graph",0);
+my $gdtextalign = have_vers("GD::Text::Align",0);
 
 print "\n" unless $silent;
-if (($charts != 2) && !$silent) {
-    print "If you you want to see graphical bug dependency charts, you may install\n",
-    "the optional libgd and the Perl modules GD-1.19 and Chart::Base-0.99b, e.g. by\n",
-    "running (as root)\n\n",
-    "   perl -MCPAN -e'install \"LDS/GD-1.19.tar.gz\"'\n",
-    "   perl -MCPAN -e'install \"N/NI/NINJAZ/Chart-0.99b.tar.gz\"'\n\n";
+if ((!$gd || !$chartbase) && !$silent) {
+    print "If you you want to see graphical bug charts (plotting historical ";
+    print "data over \ntime), you should install libgd and the following Perl ";     print "modules:\n\n";
+    print "GD:          perl -MCPAN -e'install \"GD\"'\n" if !$gd;
+    print "Chart 0.99b: perl -MCPAN " . 
+          "-e'install \"N/NI/NINJAZ/Chart-0.99b.tar.gz\"'\n" if !$chartbase;
+    print "\n";
 }
 if (!$xmlparser && !$silent) {
     print "If you want to use the bug import/export feature to move bugs to or from\n",
     "other bugzilla installations, you will need to install the XML::Parser module by\n",
-    "running (as root)\n\n",
+    "running (as root):\n\n",
     "   perl -MCPAN -e'install \"XML::Parser\"'\n\n";
 }
+if ((!$gd || !$gdgraph || !$gdtextalign) && !$silent) {
+    print "If you you want to see graphical bug reports (bar, pie and line ";
+    print "charts of \ncurrent data), you should install libgd and the ";
+    print "following Perl modules:\n\n";
+    print "GD:              perl -MCPAN -e'install \"GD\"'\n" if !$gd;
+    print "GD::Graph:       perl -MCPAN " .
+           "-e'install \"GD::Graph\"'\n" if !$gdgraph;
+    print "GD::Text::Align: perl -MCPAN " . 
+           "-e'install \"GD::Text::Align\"'\n" if !$gdtextalign;
+    print "\n";
+}
 if (%missing) {
     print "\n\n";
     print "Bugzilla requires some Perl modules which are either missing from your\n",
@@ -580,6 +593,7 @@ $contenttypes = {
    "xml" => "text/xml" , 
     "js" => "application/x-javascript" , 
    "csv" => "text/plain" ,
+   "png" => "image/png" ,
 };
 ');
 
index 18e6c795643f216576cc550440a97b5c9d11cbde..dec75628dcb6128083f38fd267374b85316a21fe 100755 (executable)
--- a/query.cgi
+++ b/query.cgi
@@ -131,7 +131,8 @@ sub PrefillForm {
                       "bug_file_loc_type", "status_whiteboard",
                       "status_whiteboard_type", "bug_id",
                       "bugidtype", "keywords", "keywords_type",
-                      "x_axis_field", "y_axis_field", "z_axis_field") 
+                      "x_axis_field", "y_axis_field", "z_axis_field",
+                      "chart_format", "cumulate") 
     {
         # This is a bit of a hack. The default, empty list has 
         # three entries to accommodate the needs of the email fields -
index f4cb74dadad18ca56274989a7c69909feb0db158..9543bc5b7349c69bae141a8f482bb44318510530 100755 (executable)
@@ -34,24 +34,58 @@ ConnectToDatabase();
 
 GetVersionTable();
 
-quietly_check_login();
+confirm_login();
 
-if ($::FORM{'action'} ne "plot") {
+my $action = $cgi->param('action') || 'menu';
+
+if ($action eq "menu") {
+    # No need to do any searching in this case, so bail out early.
     print "Content-Type: text/html\n\n";
     $template->process("reports/menu.html.tmpl", $vars)
       || ThrowTemplateError($template->error());
     exit;
 }
 
-$::FORM{'y_axis_field'} || ThrowCodeError("no_y_axis_defined");
+my $col_field = $cgi->param('x_axis_field') || '';
+my $row_field = $cgi->param('y_axis_field') || '';
+my $tbl_field = $cgi->param('z_axis_field') || '';
+
+if (!($col_field || $row_field || $tbl_field)) {
+    ThrowUserError("no_axes_defined");
+}
+
+my $width = $cgi->param('width');
+my $height = $cgi->param('height');
 
-if ($::FORM{'z_axis_field'} && !$::FORM{'x_axis_field'}) {
-    ThrowUserError("z_axis_defined_with_no_x_axis");
+if (defined($width)) {
+   (detaint_natural($width) && $width > 0)
+     || ThrowCodeError("invalid_dimensions");
+   $width <= 2000 || ThrowUserError("chart_too_large");
 }
 
-my $col_field = $::FORM{'x_axis_field'};
-my $row_field = $::FORM{'y_axis_field'};
-my $tbl_field = $::FORM{'z_axis_field'};
+if (defined($height)) {
+   (detaint_natural($height) && $height > 0)
+     || ThrowCodeError("invalid_dimensions");
+   $height <= 2000 || ThrowUserError("chart_too_large");
+}
+
+# These shenanigans are necessary to make sure that both vertical and 
+# horizontal 1D tables convert to the correct dimension when you ask to
+# display them as some sort of chart.
+if ($::FORM{'format'} && $::FORM{'format'} eq "table") {
+    if ($col_field && !$row_field) {    
+        # 1D *tables* should be displayed vertically (with a row_field only)
+        $row_field = $col_field;
+        $col_field = '';
+    }
+}
+else {
+    if ($row_field && !$col_field) {
+        # 1D *charts* should be displayed horizontally (with an col_field only)
+        $col_field = $row_field;
+        $row_field = '';
+    }
+}
 
 my %columns;
 $columns{'bug_severity'}     = "bugs.bug_severity";        
@@ -83,6 +117,9 @@ my $search = new Bugzilla::Search('fields' => \@selectnames,
                                   'params' => $params);
 my $query = $search->getSQL();
 
+$::SIG{TERM} = 'DEFAULT';
+$::SIG{PIPE} = 'DEFAULT';
+
 SendSQL($query);
 
 # We have a hash of hashes for the data itself, and a hash to hold the 
@@ -90,9 +127,11 @@ SendSQL($query);
 my %data;
 my %names;
 
-# Read the bug data and increment the counts.
+# Read the bug data and count the bugs for each possible value of row, column
+# and table.
 while (MoreSQLData()) {
     my ($row, $col, $tbl) = FetchSQLData();
+    $row = "" if ($row eq $columns{''});
     $col = "" if ($col eq $columns{''});
     $tbl = "" if ($tbl eq $columns{''});
     
@@ -102,25 +141,106 @@ while (MoreSQLData()) {
     $names{"tbl"}{$tbl}++;
 }
 
-# Determine the labels for the rows and columns
+my @col_names = sort(keys(%{$names{"col"}}));
+my @row_names = sort(keys(%{$names{"row"}}));
+my @tbl_names = sort(keys(%{$names{"tbl"}}));
+
+# The GD::Graph package requires a particular format of data, so once we've
+# gathered everything into the hashes and made sure we know the size of the
+# data, we reformat it into an array of arrays of arrays of data.
+push(@tbl_names, "-total-") if (scalar(@tbl_names) > 1);
+    
+my @image_data;
+foreach my $tbl (@tbl_names) {
+    my @tbl_data;
+    push(@tbl_data, \@col_names);
+    foreach my $row (@row_names) {
+        my @col_data;
+        foreach my $col (@col_names) {
+            $data{$tbl}{$col}{$row} = $data{$tbl}{$col}{$row} || 0;
+            push(@col_data, $data{$tbl}{$col}{$row});
+            if ($tbl ne "-total-") {
+                # This is a bit sneaky. We spend every loop except the last
+                # building up the -total- data, and then last time round,
+                # we process it as another tbl, and push() the total values 
+                # into the image_data array.
+                $data{"-total-"}{$col}{$row} += $data{$tbl}{$col}{$row};
+            }
+        }
+
+        push(@tbl_data, \@col_data);
+    }
+    
+    push(@image_data, \@tbl_data);
+}
+
 $vars->{'col_field'} = $col_field;
 $vars->{'row_field'} = $row_field;
 $vars->{'tbl_field'} = $tbl_field;
-$vars->{'names'} = \%names;
-$vars->{'data'} = \%data;
 $vars->{'time'} = time();
 
-$cgi->delete('format');
+$vars->{'col_names'} = \@col_names;
+$vars->{'row_names'} = \@row_names;
+$vars->{'tbl_names'} = \@tbl_names;
+
+$vars->{'width'} = $width if $width;
+$vars->{'height'} = $height if $height;
+
+$vars->{'query'} = $query;
+$vars->{'debug'} = $::FORM{'debug'};
+
+my $formatparam = $cgi->param('format');
+
+if ($action eq "wrap") {
+    # So which template are we using? If action is "wrap", we will be using
+    # no format (it gets passed through to be the format of the actual data),
+    # and either report.csv.tmpl (CSV), or report.html.tmpl (everything else).
+    # report.html.tmpl produces an HTML framework for either tables of HTML
+    # data, or images generated by calling report.cgi again with action as
+    # "plot".
+    $formatparam =~ s/[^a-zA-Z\-]//g;
+    trick_taint($formatparam);
+    $vars->{'format'} = $formatparam;
+    $formatparam = '';
+
+    # We need a number of different variants of the base URL for different
+    # URLs in the HTML.
+    $vars->{'buglistbase'} = $cgi->canonicalise_query(
+        "x_axis_field", "y_axis_field", "z_axis_field", "format", @axis_fields);
+    $vars->{'imagebase'}   = $cgi->canonicalise_query( 
+                    $tbl_field, "action", "ctype", "format", "width", "height");
+    $vars->{'switchbase'}  = $cgi->canonicalise_query( 
+                                "action", "ctype", "format", "width", "height");
+    $vars->{'data'} = \%data;
+}
+elsif ($action eq "plot") {
+    # If action is "plot", we will be using a format as normal (pie, bar etc.)
+    # and a ctype as normal (currently only png.)
+    $vars->{'cumulate'} = $cgi->param('cumulate') ? 1 : 0;
+    $vars->{'data'} = \@image_data;
+}
+else {
+    ThrowUserError("unknown_action", {action => $cgi->param('action')});
+}
+
+my $format = GetFormat("reports/report", $formatparam, $cgi->param('ctype'));
 
-# Calculate the base query URL for the hyperlinked numbers
-$vars->{'querybase'} = $cgi->canonicalise_query("x_axis_field",
-                                                "y_axis_field",
-                                                "z_axis_field",
-                                                @axis_fields);
-$vars->{'query'} = $cgi->query_string();
+# If we get a template or CGI error, it comes out as HTML, which isn't valid
+# PNG data, and the browser just displays a "corrupt PNG" message. So, you can
+# set debug=1 to always get an HTML content-type, and view the error.
+$format->{'ctype'} = "text/html" if $::FORM{'debug'};
 
-# Generate and return the result from the appropriate template.
-my $format = GetFormat("reports/report", $::FORM{'format'}, $::FORM{'ctype'});
 print "Content-Type: $format->{'ctype'}\n\n";
+
+# Problems with this CGI are often due to malformed data. Setting debug=1
+# prints out both data structures.
+if ($::FORM{'debug'}) {
+    use Data::Dumper;
+    print "<pre>data hash:\n";
+    print Dumper(%data) . "\n\n";
+    print "data array:\n";
+    print Dumper(@image_data) . "\n\n</pre>";
+}
+
 $template->process("$format->{'template'}", $vars)
   || ThrowTemplateError($template->error());
index baad2f5f03da40facdcbed23d728c8033bf4403b..42c07ab2d2d49c2371df8b7ea48f6dd3990208c4 100644 (file)
     The [% component FILTER html %] component doesn't exist in the 
     [% product FILTER html %] product.
     
+  [% ELSIF error == "invalid_dimensions" %]
+    [% title = "Invalid Dimensions" %]
+    The width or height specified is not a positive integer.
+    
   [% ELSIF error == "mismatched_bug_ids_on_obsolete" %]
     Attachment [% attach_id FILTER html %] ([% description FILTER html %]) 
     is attached to bug [% attach_bug_id FILTER html %], but you tried to 
index 6349b6b1bbc5458170823cbb29b7706b4c5c8888..3e83da7c0cc4c88e10b79ccdebb2edf1855ced32 100644 (file)
@@ -1,4 +1,4 @@
-<!-- 1.0@bugzilla.org -->
+[%# 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
index ddc9ce11c1efe3eb5e1917ae73c01b2015583922..d08c96c4bd2859d0bd821e3ae061eacb141673ae 100644 (file)
     [% title = "Bugs Not Changed" %]
     Um, you apparently did not change anything on the selected bugs.
                         
+  [% ELSIF error == "chart_too_large" %]
+    [% title = "Chart Too Large" %]
+    Sorry, but 2000 x 2000 is the maximum size for a chart.
+                        
   [% ELSIF error == "comment_required" %]
     [% title = "Comment Required" %]
     You have to specify a <b>comment</b> on this change.  
     [% title = "New Password Missing" %]
     You must enter a new password.
     
+  [% ELSIF error == "no_axes_defined" %]
+    [% title = "No Axes Defined" %]
+    You didn't define any axes to plot.
+
   [% ELSIF error == "no_bugs_chosen" %]
     [% title = "No Bugs Chosen" %]
     You apparently didn't choose any bugs to modify.
     [% title = "Value Out Of Range" %]
     Value is out of range for field <em>[% field_descs.$field %]</em>.
 
-  [% ELSIF error == "z_axis_defined_with_no_x_axis" %]
-    [% title = "Nonsensical Options" %]
-    You've defined a field for multiple tables without having defined
-    a horizontal axis for those tables.
-
   [% ELSIF error == "zero_length_file" %]
     [% title = "File Is Empty" %]
     The file you are trying to attach is empty!    
index d13ccbc438125483bead0d10128ff6adf0516ec0..9b34d15ee2756b8d121c56ace35173f7c04358ac 100644 (file)
@@ -55,7 +55,7 @@ function addSidebar() {
   <p>
   <a href="query.cgi">Query existing bug reports</a><br>
   <a href="enter_bug.cgi">Enter a new bug report</a><br>
-  <a href="reports.cgi">Get summary reports</a><br>
+  <a href="report.cgi">Summary reports and charts</a><br>
   </p><p>
 [% IF username %]
   <a href="userprefs.cgi">Change password or user preferences</a><br>
index d93717532274d4f91f3b1c1ccbdeba07d5431cea..4e21bf4d6229da83960752baa32c72619b1a9c3d 100644 (file)
@@ -29,7 +29,7 @@
 %]
 
 <p>
-  Bugzilla allows you to view and track the state of your bug database in
+  Bugzilla allows you to view and track the state of the bug database in
   all manner of exciting ways.
 </p>
 
     </strong> -
     tables of bug counts in 1, 2 or 3 dimensions, as HTML or CSV.
   </li>
+  <li>
+    <strong>
+      <a href="query.cgi?format=report-graph">Graphical reports</a>
+    </strong> -
+    line graphs, bar and pie charts.
+  </li>
 </ul>
 
 <h2>Change Over Time</h2>
diff --git a/template/en/default/reports/report-bar.png.tmpl b/template/en/default/reports/report-bar.png.tmpl
new file mode 100644 (file)
index 0000000..a3d73d4
--- /dev/null
@@ -0,0 +1,54 @@
+[%# 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>
+  #%]
+
+[% y_label = "Bugs" %]
+
+[% PROCESS "global/field-descs.html.tmpl" %]
+
+[% col_field_disp = field_descs.$col_field || col_field %]
+
+[% FILTER null;
+  USE graph = GD.Graph.bars(width, height);
+
+  graph.set(x_label          => col_field_disp,
+            y_label          => y_label,
+            y_tick_number    => 8, 
+            y_number_format  => "%d", 
+            x_label_position => 0.5,
+            bar_spacing      => 8,
+            shadow_depth     => 4, 
+            shadowclr        => 'dred',
+            show_values      => 1,    
+            legend_placement => "RT");
+  
+  graph.set(cumulate         => "true",
+            show_values      => 0) IF cumulate;
+  
+  # Workaround for the fact that set_legend won't take row_names directly, 
+  # because row_names is an array reference rather than an array.
+  graph.set_legend(row_names.0, row_names.1, row_names.2, row_names.3,
+                   row_names.4, row_names.5, row_names.6, row_names.7,
+                   row_names.8, row_names.9, row_names.10, row_names.11,
+                   row_names.12, row_names.13, row_names.14, row_names.15);
+                                            
+  graph.plot(data.0).png | stdout(1);
+  END;
+-%]
diff --git a/template/en/default/reports/report-line.png.tmpl b/template/en/default/reports/report-line.png.tmpl
new file mode 100644 (file)
index 0000000..d0c7b25
--- /dev/null
@@ -0,0 +1,53 @@
+[%# 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>
+  #%]
+
+[% y_label = "Bugs" %]
+
+[% PROCESS "global/field-descs.html.tmpl" %]
+
+[% col_field_disp = field_descs.$col_field || col_field %]
+
+[% IF cumulate %]
+  [% USE graph = GD.Graph.area(width, height) %]
+  [% graph.set(cumulate => "true") %]
+[% ELSE %]
+  [% USE graph = GD.Graph.lines(width, height) %]
+[% END %]
+
+[% FILTER null;
+  graph.set(x_label          => col_field_disp,
+            y_label          => y_label,
+            y_tick_number    => 8, 
+            x_label_position => 0.5,
+            legend_placement => "RT",
+            line_width       => 2);
+  
+  # Workaround for the fact that set_legend won't take row_names directly, 
+  # because row_names is an array reference rather than an array.
+  graph.set_legend(row_names.0, row_names.1, row_names.2, row_names.3,
+                   row_names.4, row_names.5, row_names.6, row_names.7,
+                   row_names.8, row_names.9, row_names.10, row_names.11,
+                   row_names.12, row_names.13, row_names.14, row_names.15);
+                      
+  graph.plot(data.0).png | stdout(1);
+  END;
+-%]
+
diff --git a/template/en/default/reports/report-pie.png.tmpl b/template/en/default/reports/report-pie.png.tmpl
new file mode 100644 (file)
index 0000000..f34397e
--- /dev/null
@@ -0,0 +1,35 @@
+[%# 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>
+  #%]
+
+[% PROCESS "global/field-descs.html.tmpl" %]
+
+[% col_field_disp = field_descs.$col_field || col_field %]
+
+[% FILTER null;
+  USE graph = GD.Graph.pie(width, height);
+    
+  graph.set(title       => col_field_disp,
+            pie_height  => 20,
+            start_angle => 180);
+    
+  graph.plot(data.0).png | stdout(1);
+  END;
+-%]
index a80a618c8580bd925a543b77a30b52018eb90aba..60f3a4eefdbf03fb30165c61c9d78eaa7f1a2a21 100644 (file)
   # Contributor(s): Gervase Markham <gerv@gerv.net>
   #%]
 [%# INTERFACE:
-  # See report.html.tmpl.
+  # See report-table.html.tmpl.
   #%]
-  
-[% tbl_names = names.tbl.keys.sort %]
-[% col_names = names.col.keys.sort %]
-[% row_names = names.row.keys.sort %]
+[% num_bugs = "Number of bugs" %]
+[% tbl_field_disp = field_descs.$tbl_field || tbl_field %]
+[% col_field_disp = field_descs.$col_field || col_field %]
+[% row_field_disp = field_descs.$row_field || row_field %]
 
-[% FOREACH tbl = tbl_names %]
-  [% IF tbl_field -%]
-    [% tbl FILTER html %]
-  [% END %]
-
-  [% row_field FILTER csv -%]
-
-  [% IF col_field -%]
- \ [% col_field FILTER csv -%],
-  [% FOREACH col = col_names -%]
-    [% col FILTER csv -%],
-  [% END -%]
-  [% ELSE -%]
-    [% -%],Number of bugs
-  [% END %]
+[% "$tbl_field_disp: $tbl\n" FILTER csv IF tbl_field %]
+[% row_field_disp FILTER csv IF row_field %]
+[% " / " IF col_field AND row_field %]
+[% col_field_disp FILTER csv %],
+[% IF col_field -%]
+[% FOREACH col = col_names -%]
+  [% col FILTER csv -%],
+[% END -%]
+[% ELSE -%]
+  [% num_bugs %],
+[% END %]
 
-  [% FOREACH row = row_names %]
-    [% row FILTER csv -%],
-    [% FOREACH col = col_names %]
-      [% IF data.$tbl AND data.$tbl.$col AND data.$tbl.$col.$row %]
-        [% data.$tbl.$col.$row -%],
-      [% ELSE %]
-        [% -%]0,
-      [% END %]
+[% FOREACH row = row_names %]
+  [% row FILTER csv -%],
+  [% FOREACH col = col_names %]
+    [% IF data.$tbl AND data.$tbl.$col AND data.$tbl.$col.$row %]
+      [% data.$tbl.$col.$row -%],
+    [% ELSE %]
+      [% -%]0,
     [% END %]
-
   [% END %]
-  
+
 [% END %]
index 9767f50301afce6dde2f0346706ad4246640c162..5074484c36833e5836c7d4a3b619ec3c3a8766eb 100644 (file)
   #%]
 
 [%# INTERFACE:
-  # querybase: The base query for this table, in URL form
-  # query: The query for this table, in URL form
-  # data: hash of hash of hash of numbers. Bug counts.
-  # names: hash of hash of strings. Names of tables, rows and columns.
+  # buglistbase: The base query for this table, in URL form
   # col_field: string. Name of the field being plotted as columns.
   # row_field: string. Name of the field being plotted as rows.
   # tbl_field: string. Name of the field being plotted as tables.
+  # col_names: array. List of values for the field being plotted as columns.
+  # row_names: array. List of values for the field being plotted as rows.
+  # data: <depends on format>. Data to plot. Only data.$tbl is accessed. 
+  # tbl: Name of a hash in data which is the table to be plotted.
   #%]
 
 [% PROCESS "global/field-descs.html.tmpl" %]
 
-[% tbl_field_disp = field_descs.$tbl_field || tbl_field %]
 [% col_field_disp = field_descs.$col_field || col_field %]
 [% row_field_disp = field_descs.$row_field || row_field %]
-
-[% title = BLOCK %]
-  Report: 
-  [% "$tbl_field_disp / " IF tbl_field %]
-  [% "$col_field_disp / " IF col_field %]
-  [% row_field_disp %]
-[% END %]
-
-[% PROCESS global/header.html.tmpl 
-  style = "
-    .t1     { background-color: #ffffff } /* white       */
-    .t2     { background-color: #dfefff } /* light blue  */
-    .t3     { background-color: #dddddd } /* grey        */
-    .t4     { background-color: #c3d3ed } /* darker blue */
-    .ttotal { background-color: #cfffdf } /* light green */
-  "
-%]
-
-<div align="right">
-  [% time2str("%Y-%m-%d %H:%M:%S", time) %]
-</div>
-
-[% tbl_names = names.tbl.keys.sort %]
-[% col_names = names.col.keys.sort %]
-[% row_names = names.row.keys.sort %]
-
-[% total_name = "Total" %]
-
-[% FOREACH tbl = tbl_names %]
-  [% FOREACH row = row_names %]
-    [% FOREACH col = col_names %]
-      [% data.$tbl.$col.$row  = (data.$tbl.$col.$row || 0) %]
-      
-      [% IF tbl_field %]
-        [%# Calculate values for the Total table %]
-        [% data.$total_name.$col.$row = 
-          (data.$total_name.$col.$row || 0) + data.$tbl.$col.$row %]
-      [% END %]
-    [% END %]
-  [% END %]
-[% END %]
   
-[% IF tbl_field %]
-  [% tbl_names.push(total_name) %]
+[% IF tbl == "-total-" %]
+  [% urlbase = BLOCK %]buglist.cgi?[% buglistbase %][% END %]
+[% ELSE %]
+  [% urlbase = BLOCK %]buglist.cgi?[% buglistbase %]&amp;
+  [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %][% END %]
 [% END %]
 
-<div align="center">
-
-[% FOREACH tbl = tbl_names %]
-  <table>
-    [% IF tbl_field %]
-      <tr>
-        <td>
-        </td>
-        <td align="center">
-          <h2>[% tbl FILTER html %]</h2>
-        </td>
-      </tr>
-    [% END %]  
+<table>
+  [% IF tbl_field %]
     <tr>
       <td>
       </td>
-      <td align="center">
-        <strong>[% col_field_disp FILTER html %]</strong>
+      <td align="center">        
+        <h2>[% tbl_disp %]</h2>
       </td>
     </tr>
+  [% END %]  
+  <tr>
+    <td>
+    </td>
+    <td align="center">
+      <strong>[% col_field_disp FILTER html %]</strong>
+    </td>
+  </tr>
 
-    <tr>
-      <td valign="middle">
-        <strong>[% row_field_disp FILTER html %]</strong>
-      </td>
-      <td>
+  <tr>
+    <td valign="middle">
+      <strong>[% row_field_disp FILTER html %]</strong>
+    </td>
+    <td>
 
 
 [% classes = [ [ "t1", "t2" ] , [ "t3", "t4" ] ] %]
         [% col_idx = 1 - col_idx %]
         <td class="[% classes.$row_idx.$col_idx %]" align="center">
           [% IF data.$tbl.$col.$row AND data.$tbl.$col.$row > 0 %]
-            <a href="buglist.cgi?[% querybase FILTER html %]&amp;
-              [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %]&amp;
+            <a href="[% urlbase %]&amp;
               [% row_field FILTER url_quote %]=[% row FILTER url_quote %]&amp;
               [% col_field FILTER url_quote %]=[% col FILTER url_quote %]">
               [% data.$tbl.$col.$row %]</a>
         </td>
       [% END %] 
       <td class="ttotal" align="right">
-        <a href="buglist.cgi?[% querybase FILTER html %]&amp;
-          [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %]&amp;
+        <a href="[% urlbase %]&amp;
           [% row_field FILTER url_quote %]=[% row FILTER url_quote %]">
         [% row_total %]</a>
         [% grand_total = grand_total + row_total %]
       [% NEXT IF col == "" %]
       
       <td class="ttotal" align="center">
-        <a href="buglist.cgi?[% querybase FILTER html %]&amp;
-          [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %]&amp;
+        <a href="[% urlbase %]&amp;
           [% col_field FILTER url_quote %]=[% col FILTER url_quote %]">
         [% col_totals.$col %]</a>
       <strong>
     [% END %]
     <td class="ttotal" align="right">
       <strong>
-        <a href="buglist.cgi?[% querybase FILTER html %]">[% grand_total %]</a>
+        <a href="buglist.cgi?[% urlbase %]">[% grand_total %]</a>
       </strong>
     </td>
   </tr>
 </table>
       
       
-      </td>
-    </tr>
-  </table>
-    
-  <br>
-
-[% END %]
-  
-  <a href="query.cgi?[% query FILTER html %]&amp;format=report-table">Edit this report</a>
-</div>
-
-<br>
-
-[% PROCESS global/footer.html.tmpl %]
+    </td>
+  </tr>
+</table>    
diff --git a/template/en/default/reports/report.csv.tmpl b/template/en/default/reports/report.csv.tmpl
new file mode 100644 (file)
index 0000000..727acb3
--- /dev/null
@@ -0,0 +1,26 @@
+[%# 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>
+  #%]
+[% PROCESS "global/field-descs.html.tmpl" %]
+[% FOREACH tbl = tbl_names %]
+  [% PROCESS "reports/report-table.csv.tmpl" %] 
+
+
+[% END %]  
diff --git a/template/en/default/reports/report.html.tmpl b/template/en/default/reports/report.html.tmpl
new file mode 100644 (file)
index 0000000..c4a3edd
--- /dev/null
@@ -0,0 +1,156 @@
+ <!-- 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:
+  # col_field: string. Name of the field being plotted as columns.
+  # row_field: string. Name of the field being plotted as rows.
+  # tbl_field: string. Name of the field being plotted as tables.
+  # tbl_names: array. List of values for the field being plotted as tables.
+  # time: integer. Seconds since the epoch.
+  # data: <depends on format>. Data to plot. 
+  # format: string. Format of the individual reports.
+  # width: integer. For image charts, height of the image.
+  # height: integer. For image charts, width of the image.
+  # switchbase: string. Base URL for format switching.
+  # cumulate: boolean. For bar/line charts, whether to cumulate data sets.
+  #%]
+
+[% DEFAULT width = 600
+           height = 350 
+%]
+
+[%# We ignore row_field for pie charts %]
+[% IF format == "pie" %]
+  [% row_field = "" %]
+[% END %]
+
+[% PROCESS "global/field-descs.html.tmpl" %]
+
+[% tbl_field_disp = field_descs.$tbl_field || tbl_field %]
+[% col_field_disp = field_descs.$col_field || col_field %]
+[% row_field_disp = field_descs.$row_field || row_field %]
+
+[% title = BLOCK %]
+  Report: 
+  [% tbl_field_disp IF tbl_field %]
+  [% " / " IF tbl_field AND (col_field OR row_field) %]
+  [% row_field_disp IF row_field %]
+  [% " / " IF col_field AND row_field %]
+  [% col_field_disp %]
+[% END %]
+
+[% PROCESS global/header.html.tmpl 
+  style = "
+    .t1     { background-color: #ffffff } /* white       */
+    .t2     { background-color: #dfefff } /* light blue  */
+    .t3     { background-color: #dddddd } /* grey        */
+    .t4     { background-color: #c3d3ed } /* darker blue */
+    .ttotal { background-color: #cfffdf } /* light green */
+  "
+  h3 = time2str("%Y-%m-%d %H:%M:%S", time)
+%]
+
+[% IF debug %]
+  <p>[% query FILTER html %]</p>
+[% END %]
+
+<div align="center">
+
+  [% FOREACH tbl = tbl_names %]    
+    [% IF tbl == "-total-" %]
+      [% tbl_disp = "Total" %]
+    [% ELSE %]
+      [% tbl_disp = tbl %]
+    [% END %]
+
+    [% IF format == "table" %]
+      [% PROCESS "reports/report-table.html.tmpl" %]
+    [% ELSE %]
+      [% IF tbl %]
+        <h2>[% tbl_disp FILTER html %]</h2>
+      [% END %]
+      
+      [% imageurl = BLOCK %]report.cgi?[% imagebase %]&amp;format=
+        [% format FILTER url_quote %]&amp;ctype=png&amp;action=plot&amp;
+        [% IF tbl_field AND tbl != "-total-" %]
+          [% tbl_field FILTER url_quote %]=[% tbl FILTER url_quote %]&amp;
+        [% END %]width=[% width %]&amp;height=[% height %]      
+      [% END %]
+      
+      <img src="[% imageurl %]" width="[% width %]" height="[% height %]">
+    [% END %]
+    <br>
+  [% END %]  
+
+  <table>
+    <tr>
+      <td>
+        [% formats = [ { name => "pie",   description => "Pie" },
+                       { name => "bar",   description => "Bar" },
+                       { name => "line",  description => "Line" },
+                       { name => "table", description => "Table" } ] %]
+
+        [% formaturl = "report.cgi?$switchbase&width=$width&height=$height" _ 
+                       "&action=wrap" %]
+        [% FOREACH other_format = formats %]
+          [% NEXT IF other_format.name == "pie" AND row_field %]
+          [% UNLESS other_format.name == format %]
+            <a href="[% formaturl %]&format=[% other_format.name %]">
+          [% END %]
+          [% other_format.description %]
+          [% "</a>" UNLESS other_format.name == format %] | 
+        [% END %]
+        <a href="[% formaturl %]&ctype=csv">CSV</a> 
+      </td>
+      
+      [% IF format != "table" %]
+        <td>
+          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+        </td>
+
+        [% sizeurl = "report.cgi?$switchbase&action=wrap&format=$format" %]
+        <td align="center">
+          <a href="[% sizeurl %]&width=[% width %]&height=
+                   [% height + 100 %]">Taller</a><br>
+          <a href="[% sizeurl %]&width=[% width - 100 %]&height=
+                   [% height %]">Thinner</a> * 
+          <a href="[% sizeurl %]&width=[% width + 100 %]&height=
+                   [% height %]">Fatter</a>&nbsp;&nbsp;&nbsp;&nbsp;<br>
+          <a href="[% sizeurl %]&width=[% width %]&height=
+                   [% height - 100 %]">Shorter</a><br>
+        </td>
+      [% END %]
+    <tr>
+  </table>
+
+  <p>
+    [% IF format == "table" %]
+      <a href="query.cgi?[% switchbase %]&format=report-table">Edit 
+      this report</a>
+    [% ELSE %]
+      <a href="query.cgi?[% switchbase %]&format=report-graph&chart_format=
+      [% format %]&cumulate=[% cumulate %]">Edit this report</a>
+    [% END %]
+  </p>
+</div>
+
+[% PROCESS global/footer.html.tmpl %]
diff --git a/template/en/default/search/search-report-graph.html.tmpl b/template/en/default/search/search-report-graph.html.tmpl
new file mode 100644 (file)
index 0000000..8280fb0
--- /dev/null
@@ -0,0 +1,112 @@
+<!-- 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']);"
+%]
+
+[% PROCESS "search/search-report-select.html.tmpl" %]
+
+<p>
+  Produce a pictorial graph of bug counts by choosing one or more fields as 
+  your axes, and then refining your set of bugs using the rest of the form. 
+  If you choose a third axis, it will be represented by multiple tables of data.
+  Note: vertical axis settings will be ignored for pie charts.
+</p>
+
+[% button_name = "Generate Report" %]
+
+<form method="get" action="report.cgi" name="reportform">
+
+<table align="center">
+  <tr>
+    <td valign="middle">
+      <b>Vertical Axis:</b><br>
+      [% PROCESS select name = 'y_axis_field' %]<br>
+      <br>
+        <b>Plot Data Sets:</b><br>
+        <input type="radio" name="cumulate" value="0"
+        [% " checked" IF default.cumulate.0 != "1" %]>
+        Individually<br>
+        <input type="radio" name="cumulate" value="1"
+        [% " checked" IF default.cumulate.0 == "1" %]>
+        Added
+        
+    </td>
+    <td width="150px" height="150px">
+      <table border="1" width="100%" height="100%">
+        <tr>
+          <td align="center" valign="middle">
+            <b>Multiple Images:</b><br>
+            [% PROCESS select name = 'z_axis_field' %]
+          </td>
+        </tr>
+      </table>
+    </td>
+    <td rowspan="2">
+      <b>Format:</b><br>
+      [% chart_formats = [
+        { name => "line", description => "Line Graph" },
+        { name => "bar",  description => "Bar Chart" },
+        { name => "pie",  description => "Pie Chart" } ] %]
+      [% default.chart_format.0 = default.chart_format.0 || "bar" %]
+      
+      [% FOREACH chart_format = chart_formats %]
+        <input type="radio" name="format" 
+               value="[% chart_format.name FILTER html %]" 
+          [% " checked" IF default.chart_format.0 == chart_format.name %]>
+          [% chart_format.description FILTER html %]<br>
+      [% END %]
+    </td>
+  </tr>
+  
+  <tr>
+    <td>
+    </td>
+    <td align="center">
+      <b>Horizontal Axis:</b>
+      [% PROCESS select name = 'x_axis_field' %]
+    </td>
+    <td>
+    </td>
+  </tr>
+</table>  
+
+<hr>
+
+[% PROCESS search/form.html.tmpl %]
+
+<br>
+<input type="submit" value="[% button_name %]">
+<input type="hidden" name="action" value="wrap">
+<hr>
+
+[% PROCESS "search/boolean-charts.html.tmpl" %]
+
+</form>
+
+[% PROCESS global/footer.html.tmpl %]
diff --git a/template/en/default/search/search-report-select.html.tmpl b/template/en/default/search/search-report-select.html.tmpl
new file mode 100644 (file)
index 0000000..75716ea
--- /dev/null
@@ -0,0 +1,47 @@
+<!-- 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:
+  # name: string. The name of the select block to output.
+  # default.$name.0: string. The default value for the block, if any.
+  #%]
+
+[% PROCESS "global/field-descs.html.tmpl" %]
+
+[% BLOCK select %]
+  [% fields = ["product", "component", "version", "rep_platform",  
+               "op_sys", "bug_status", "resolution", "bug_severity", 
+               "priority", "target_milestone", "keywords", "assigned_to",
+               "reporter", "qa_contact", "votes" ] %]
+
+  <select name="[% name FILTER html %]">
+    <option value="">&lt;none&gt;</option>
+    
+    [% FOREACH field = fields %]
+      [% NEXT IF field == "target_milestone" AND !Param('usetargetmilestone') %]
+      [% NEXT IF field == "qa_contact" AND !Param('useqacontact') %]
+      [% NEXT IF field == "votes" AND !Param('usevotes') %]      
+      <option value="[% field FILTER html %]" 
+        [% " selected" IF default.$name.0 == field %]>
+        [% field_descs.$field || field FILTER html %]</option>
+    [% END %]
+  </select>
+[% END %]
index 73d542124a534312aa23d399a76c95d8fc30a773..a26553602b1913e92bb578e86e96f74ba07cc01e 100644 (file)
   onload = "selectProduct(document.forms['reportform']);"
 %]
 
-[% PROCESS "global/field-descs.html.tmpl" %]
+[% PROCESS "search/search-report-select.html.tmpl" %]
 
 <p>
-  Produce a table of bug counts by choosing one or more fields to plot against
-  each other, and then refining your set of bugs using the rest of the form. If
-  you choose a third axis, it will be represented by multiple tables of data.
+  Produce a table of bug counts by choosing one or more fields as your axes, 
+  and then refining your set of bugs using the rest of the form. 
+  If you choose a third axis, it will be represented by multiple tables of data.
 </p>
 
 [% button_name = "Generate Report" %]
@@ -47,7 +47,7 @@
     </td>
     <td align="center">
       <b>Horizontal Axis:</b>
-      [% PROCESS select sel = { name => 'x_axis_field', noop = 1 } %]
+      [% PROCESS select name = 'x_axis_field' %]
     </td>
     <td>&nbsp;&nbsp;</td>
     <td rowspan="2">
   <tr>
     <td valign="middle" align="center">
       <b>Vertical Axis:</b><br>
-      [% PROCESS select sel = { name => 'y_axis_field' } %]
+      [% PROCESS select name = 'y_axis_field' %]
     </td>
     <td width="150px" height="150px">
       <table border="1" width="100%" height="100%">
         <tr>
           <td align="center" valign="middle">
             <b>Multiple Tables:</b><br>
-            [% PROCESS select sel = { name => 'z_axis_field', noop = 1 } %]
+            [% PROCESS select name = 'z_axis_field' %]
           </td>
         </tr>
       </table>
@@ -82,7 +82,7 @@
 <br>
 <input type="submit" value="[% button_name %]">
 <input type="hidden" name="format" value="table">
-<input type="hidden" name="action" value="plot">
+<input type="hidden" name="action" value="wrap">
 <hr>
 
 [% PROCESS "search/boolean-charts.html.tmpl" %]
 </form>
 
 [% PROCESS global/footer.html.tmpl %]
-
-[%############################################################################%]
-[%# Block for SELECT fields                                                  #%]
-[%############################################################################%]
-
-[% BLOCK select %]
-  [% fields = ["product", "component", "version", "rep_platform",  
-               "op_sys", "bug_status", "resolution", "bug_severity", 
-               "priority", "target_milestone", "keywords", "assigned_to",
-               "reporter", "qa_contact", "votes" ] %]
-
-  <select name="[% sel.name %]">
-    [% IF sel.noop %]
-      <option value="">&lt;none&gt;</option>
-    [% END %]
-    
-    [% FOREACH field = fields %]
-      [% NEXT IF field == "target_milestone" AND !Param('usetargetmilestone') %]
-      [% NEXT IF field == "qa_contact" AND !Param('useqacontact') %]
-      [% NEXT IF field == "votes" AND !Param('usevotes') %]
-      
-      <option value="[% field FILTER html %]" 
-        [% " selected" IF default.${sel.name}.0 == field %]>
-        [% field_descs.$field || field FILTER html %]</option>
-    [% END %]
-  </select>
-[% END %]