Commit aaf6ccb9 authored by gerv%gerv.net's avatar gerv%gerv.net

Bug 12282 - General summary reports. Patch by gerv; r=joel.

parent be7edaf9
...@@ -813,6 +813,10 @@ sub init { ...@@ -813,6 +813,10 @@ sub init {
$suppseen{$str} = 1; $suppseen{$str} = 1;
} }
} }
# Make sure we create a legal SQL query.
@andlist = ("1 = 1") if !@andlist;
my $query = ("SELECT DISTINCT " . my $query = ("SELECT DISTINCT " .
join(', ', @fields) . join(', ', @fields) .
", COUNT(DISTINCT ugmap.group_id) AS cntuseringroups, " . ", COUNT(DISTINCT ugmap.group_id) AS cntuseringroups, " .
......
...@@ -563,6 +563,7 @@ $contenttypes = { ...@@ -563,6 +563,7 @@ $contenttypes = {
"rdf" => "application/xml" , "rdf" => "application/xml" ,
"xml" => "text/xml" , "xml" => "text/xml" ,
"js" => "application/x-javascript" , "js" => "application/x-javascript" ,
"csv" => "text/plain" ,
}; };
'); ');
...@@ -932,6 +933,7 @@ END ...@@ -932,6 +933,7 @@ END
js => sub { return $_; }, js => sub { return $_; },
html_linebreak => sub { return $_; }, html_linebreak => sub { return $_; },
url_quote => sub { return $_; }, url_quote => sub { return $_; },
csv => sub { return $_; },
}, },
}) || die ("Could not create Template: " . Template->error() . "\n"); }) || die ("Could not create Template: " . Template->error() . "\n");
......
...@@ -1564,6 +1564,18 @@ $::template ||= Template->new( ...@@ -1564,6 +1564,18 @@ $::template ||= Template->new(
# filter should be used for a full URL that may have # filter should be used for a full URL that may have
# characters that need encoding. # characters that need encoding.
url_quote => \&url_quote , 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()); ) || die("Template creation failed: " . Template->error());
......
...@@ -130,7 +130,9 @@ sub PrefillForm { ...@@ -130,7 +130,9 @@ sub PrefillForm {
"long_desc", "long_desc_type", "bug_file_loc", "long_desc", "long_desc_type", "bug_file_loc",
"bug_file_loc_type", "status_whiteboard", "bug_file_loc_type", "status_whiteboard",
"status_whiteboard_type", "bug_id", "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 # This is a bit of a hack. The default, empty list has
# three entries to accommodate the needs of the email fields - # three entries to accommodate the needs of the email fields -
# we use each position to denote the relevant field. Array # we use each position to denote the relevant field. Array
......
#!/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());
...@@ -81,6 +81,7 @@ my $template = Template->new( ...@@ -81,6 +81,7 @@ my $template = Template->new(
js => sub { return $_ } , js => sub { return $_ } ,
strike => sub { return $_ } , strike => sub { return $_ } ,
url_quote => sub { return $_ } , url_quote => sub { return $_ } ,
csv => sub { return $_ } ,
}, },
} }
); );
......
...@@ -78,6 +78,10 @@ ...@@ -78,6 +78,10 @@
[% ELSIF error == "no_bug_data" %] [% ELSIF error == "no_bug_data" %]
No data when fetching bug [% bug_id %]. 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" %] [% ELSIF error == "template_error" %]
[% template_error_msg %] [% template_error_msg %]
......
[%# 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 %]
<!-- 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 %]
<!-- 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 %]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment