summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortara%tequilarista.org <>2000-10-31 08:02:41 +0100
committertara%tequilarista.org <>2000-10-31 08:02:41 +0100
commitc6e881d0814e9a2527bd61a3f863e12aaa5abf22 (patch)
treedeb9dc58a8da7cf61b493ec8ef3eccd54a73b9cd
parentff238d36024ea1e58d8cfa1c797487c313b4d8d7 (diff)
downloadbugzilla-c6e881d0814e9a2527bd61a3f863e12aaa5abf22.tar.gz
bugzilla-c6e881d0814e9a2527bd61a3f863e12aaa5abf22.tar.xz
Landing Gerv and Adam's changes for bug #6682
-rwxr-xr-xcollectstats.pl55
-rwxr-xr-xreports.cgi1160
2 files changed, 639 insertions, 576 deletions
diff --git a/collectstats.pl b/collectstats.pl
index 47ba0cb5e..313004fdf 100755
--- a/collectstats.pl
+++ b/collectstats.pl
@@ -27,11 +27,16 @@
use DB_File;
use diagnostics;
use strict;
-use vars @::legal_product,
- @::legal_bug_status;
+use vars @::legal_product;
require "globals.pl";
+# tidy up after graphing module
+chdir("data/mining");
+unlink <*.gif>;
+unlink <*.png>;
+chdir("../..");
+
ConnectToDatabase();
GetVersionTable();
@@ -71,32 +76,36 @@ sub collect_stats {
if (open DATA, ">>$file") {
push my @row, &today;
- foreach my $status (@::legal_bug_status) {
- if( $product eq "-All-" ) {
- SendSQL("select count(bug_status) from bugs where bug_status='$status'");
- } else {
- SendSQL("select count(bug_status) from bugs where bug_status='$status' and product='$product'");
+ foreach my $status ('NEW', 'ASSIGNED', 'REOPENED', 'UNCONFIRMED', 'RESOLVED', 'VERIFIED', 'CLOSED') {
+ if( $product eq "-All-" ) {
+ SendSQL("select count(bug_status) from bugs where bug_status='$status'");
+ } else {
+ SendSQL("select count(bug_status) from bugs where bug_status='$status' and product='$product'");
+ }
+
+ push @row, FetchOneColumn();
+ }
+
+ foreach my $resolution ('FIXED', 'INVALID', 'WONTFIX', 'LATER', 'REMIND', 'DUPLICATE', 'WORKSFORME', 'MOVED') {
+ if( $product eq "-All-" ) {
+ SendSQL("select count(resolution) from bugs where resolution='$resolution'");
+ } else {
+ SendSQL("select count(resolution) from bugs where resolution='$resolution' and product='$product'");
+ }
+ push @row, FetchOneColumn();
}
- push @row, FetchOneColumn();
- }
- if (! $exists)
- {
- print DATA <<FIN;
-# Bugzilla daily bug stats
+ if (! $exists) {
+ print DATA <<FIN;
+# Bugzilla Daily Bug Stats
#
-# do not edit me! this file is generated.
+# Do not edit me! This file is generated.
#
-# product: $product
-# created: $when
+# Fields: DATE|NEW|ASSIGNED|REOPENED|UNCONFIRMED|FIXED|INVALID|WONTFIX|LATER|REMIND|DUPLICATE|WORKSFORME|MOVED
+# Product: $product
+# Created: $when
FIN
- print DATA "# field: DATE";
- foreach my $status (@::legal_bug_status) {
- print DATA "|$status";
- }
- print DATA "\n";
- }
-
+ }
print DATA (join '|', @row) . "\n";
close DATA;
} else {
diff --git a/reports.cgi b/reports.cgi
index b48ad7133..68a00e6f0 100755
--- a/reports.cgi
+++ b/reports.cgi
@@ -28,19 +28,28 @@
# Joe Robins <jmrobins@tgix.com>,
# If using the usebuggroups parameter, users shouldn't be able to see
# reports for products they don't have access to.
+# Gervase Markham <gerv@gerv.net> and Adam Spiers <adam@spiers.net>
+# Added ability to chart any combination of resolutions/statuses.
+# Derive the choice of resolutions/statuses from the -All- data file
+# Removed hardcoded order of resolutions/statuses when reading from
+# daily stats file, so now works independently of collectstats.pl
+# version
+# Added image caching by date and datasets
use diagnostics;
use strict;
+
use GD;
eval "use Chart::Lines";
require "CGI.pl";
-require "globals.pl";
+use vars qw(%FORM); # globals from CGI.pl
-use vars @::legal_product;
+require "globals.pl";
+use vars qw(@legal_product); # globals from er, globals.pl
my $dir = "data/mining";
-my $week = 60 * 60 * 24 * 7;
+
my @status = qw (NEW ASSIGNED REOPENED);
my %bugsperperson;
@@ -48,12 +57,12 @@ my %bugsperperson;
# functions differently than the value passed in
my %reports =
- (
- "most_doomed" => \&most_doomed,
- "most_doomed_for_milestone" => \&most_doomed_for_milestone,
- "most_recently_doomed" => \&most_recently_doomed,
- "show_chart" => \&show_chart,
- );
+ (
+ "most_doomed" => \&most_doomed,
+ "most_doomed_for_milestone" => \&most_doomed_for_milestone,
+ "most_recently_doomed" => \&most_recently_doomed,
+ "show_chart" => \&show_chart,
+ );
# If we're using bug groups for products, we should apply those restrictions
# to viewing reports, as well. Time to check the login in that case.
@@ -66,14 +75,12 @@ print "Content-type: text/html\n";
print "Content-disposition: inline; filename=bugzilla_report.html\n\n";
# If we're here for the first time, give a banner. Else respect the banner flag.
-if ( (!defined $::FORM{'product'}) || ($::FORM{'banner'}) )
- {
- PutHeader ("Bug Reports")
- }
-else
- {
- print("<html><head><title>Bug Reports</title></head><body bgcolor=\"#FFFFFF\">");
- }
+if ( (!defined $FORM{'product'}) || ($FORM{'banner'}) ) {
+ PutHeader ("Bug Reports")
+}
+else {
+ print("<html><head><title>Bug Reports</title></head><body bgcolor=\"#FFFFFF\">");
+}
GetVersionTable();
@@ -81,84 +88,79 @@ GetVersionTable();
# We only want those products that the user has permissions for.
my @myproducts;
if(Param("usebuggroups")) {
- push( @myproducts, "-All-");
- foreach my $this_product (@::legal_product) {
- if(GroupExists($this_product) && !UserInGroup($this_product)) {
- next;
- } else {
- push( @myproducts, $this_product )
+ push( @myproducts, "-All-");
+ foreach my $this_product (@legal_product) {
+ if(GroupExists($this_product) && !UserInGroup($this_product)) {
+ next;
+ } else {
+ push( @myproducts, $this_product )
+ }
}
- }
} else {
- push( @myproducts, "-All-", @::legal_product );
+ push( @myproducts, "-All-", @legal_product );
}
-$::FORM{'output'} = $::FORM{'output'} || "most_doomed"; # a reasonable default
-
-if (! defined $::FORM{'product'})
- {
- &choose_product;
- }
-else
- {
- # If usebuggroups is on, we don't want people to be able to view
- # reports for products they don't have permissions for...
- if(Param("usebuggroups") &&
- GroupExists($::FORM{'product'}) &&
- !UserInGroup($::FORM{'product'})) {
- print "<H1>Permission denied.</H1>\n";
- print "Sorry; you do not have the permissions necessary to view\n";
- print "reports for this product.\n";
- print "<P>\n";
- PutFooter();
- exit;
- }
+$FORM{'output'} ||= "most_doomed"; # a reasonable default
+
+if (! defined $FORM{'product'}) {
+ &choose_product;
+}
+else {
+ # If usebuggroups is on, we don't want people to be able to view
+ # reports for products they don't have permissions for...
+ if(Param("usebuggroups") &&
+ GroupExists($FORM{'product'}) &&
+ !UserInGroup($FORM{'product'}))
+ {
+ print "<H1>Permission denied.</H1>\n";
+ print "Sorry; you do not have the permissions necessary to view\n";
+ print "reports for this product.\n";
+ print "<P>\n";
+ PutFooter();
+ exit;
+ }
- # we want to be careful about what subroutines
- # can be called from outside. modify %reports
- # accordingly when a new report type is added
-
- if (! exists $reports{$::FORM{'output'}})
- {
- $::FORM{'output'} = "most_doomed"; # a reasonable default
- }
-
- my $f = $reports{$::FORM{'output'}};
-
- if (! defined $f)
- {
- print "start over, your form data was all messed up.<p>\n";
- foreach (keys %::FORM)
- {
- print "<font color=blue>$_</font> : " .
- ($::FORM{$_} ? $::FORM{$_} : "undef") . "<br>\n";
- }
- PutFooter() if $::FORM{banner};
- exit;
- }
-
- &{$f};
- }
+ # we want to be careful about what subroutines
+ # can be called from outside. modify %reports
+ # accordingly when a new report type is added
+
+ if (! exists $reports{$FORM{'output'}}) {
+ $FORM{'output'} = "most_doomed"; # a reasonable default
+ }
+
+ my $f = $reports{$FORM{'output'}};
+
+ if (! defined $f) {
+ print "start over, your form data was all messed up.<p>\n";
+ foreach (keys %::FORM) {
+ print "<font color=blue>$_</font> : " .
+ ($FORM{$_} ? $FORM{$_} : "undef") . "<br>\n";
+ }
+ PutFooter() if $FORM{banner};
+ exit;
+ }
+
+ &{$f};
+}
print <<FIN;
<p>
FIN
-PutFooter() if $::FORM{banner};
+PutFooter() if $FORM{banner};
##################################
# user came in with no form data #
##################################
-sub choose_product
- {
- my $product_popup = make_options (\@myproducts, $myproducts[0]);
- my $charts = defined $Chart::Lines::VERSION && -d $dir ? "<option value=\"show_chart\">Bug Charts" : "";
- # get rid of warning:
- $Chart::Lines::VERSION = $Chart::Lines::VERSION;
+sub choose_product {
+ my $product_popup = make_options (\@myproducts, $myproducts[0]);
+ my $charts = defined $Chart::Lines::VERSION && -d $dir ? "<option value=\"show_chart\">Bug Charts" : "";
+ # get rid of warning:
+ $Chart::Lines::VERSION = $Chart::Lines::VERSION;
- print <<FIN;
+ print <<FIN;
<center>
<h1>Welcome to the Bugzilla Query Kitchen</h1>
<form method=get action=reports.cgi>
@@ -177,25 +179,59 @@ $product_popup
<select name="output">
<option value="most_doomed">Bug Counts
FIN
- if (Param('usetargetmilestone')) {
- print "<option value=\"most_doomed_for_milestone\">Most Doomed";
- }
- print "<option value=\"most_recently_doomed\">Most Recently Doomed";
- print <<FIN;
+ if (Param('usetargetmilestone')) {
+ print "<option value=\"most_doomed_for_milestone\">Most Doomed";
+ }
+ print "<option value=\"most_recently_doomed\">Most Recently Doomed";
+ print <<FIN;
$charts
</select>
<tr>
+<td align=center><b>Chart datasets:</b></td>
+<td align=center>
+<select name="datasets" multiple size=5>
+FIN
+
+ my @datasets = ();
+
+ my $datafile = daily_stats_filename('-All-');
+ if (! open(DATA, "$dir/$datafile")) {
+ die_politely("Couldn't read daily statistics file");
+ }
+
+ while (<DATA>) {
+ if (/^# fields?: (.+)\s*$/) {
+ @datasets = grep ! /date/i, (split /\|/, $1);
+ last;
+ }
+ }
+
+ close(DATA);
+
+ my %default_sel = map { $_ => 1 }
+ qw/UNCONFIRMED NEW ASSIGNED REOPENED/;
+ foreach my $dataset (@datasets) {
+ my $sel = $default_sel{$dataset} ? ' selected' : '';
+ print qq{<option value="$dataset:"$sel>$dataset</option>\n};
+ }
+
+ print <<FIN;
+</select>
+</td>
+</tr>
+<tr>
<td align=center><b>Switches:</b></td>
<td align=left>
<input type=checkbox name=links checked value=1>&nbsp;Links to Bugs<br>
<input type=checkbox name=banner checked value=1>&nbsp;Banner<br>
FIN
- if (Param('usequip')) {
- print "<input type=checkbox name=quip value=1>&nbsp;Quip<br>";
- } else {
- print "<input type=hidden name=quip value=0>";
- }
- print <<FIN;
+
+ if (Param('usequip')) {
+ print "<input type=checkbox name=quip value=1>&nbsp;Quip<br>";
+ } else {
+ print "<input type=hidden name=quip value=0>";
+ }
+ print <<FIN;
</td>
</tr>
<tr>
@@ -209,30 +245,29 @@ FIN
FIN
#Add this above to get a control for showing the SQL query:
#<input type=checkbox name=showsql value=1>&nbsp;Show SQL<br>
- PutFooter();
- }
+ PutFooter();
+}
-sub most_doomed
- {
- my $when = localtime (time);
+sub most_doomed {
+ my $when = localtime (time);
- print <<FIN;
+ print <<FIN;
<center>
<h1>
-Bug Report for $::FORM{'product'}
+Bug Report for $FORM{'product'}
</h1>
$when<p>
FIN
# Build up $query string
- my $query;
- $query = <<FIN;
+ my $query;
+ $query = <<FIN;
select
- bugs.bug_id, bugs.assigned_to, bugs.bug_severity,
- bugs.bug_status, bugs.product,
- assign.login_name,
- report.login_name,
- unix_timestamp(date_format(bugs.creation_ts, '%Y-%m-%d %h:%m:%s'))
+ bugs.bug_id, bugs.assigned_to, bugs.bug_severity,
+ bugs.bug_status, bugs.product,
+ assign.login_name,
+ report.login_name,
+ unix_timestamp(date_format(bugs.creation_ts, '%Y-%m-%d %h:%m:%s'))
from bugs,
profiles assign,
@@ -242,78 +277,75 @@ where bugs.assigned_to = assign.userid
and bugs.reporter = report.userid
FIN
- if( $::FORM{'product'} ne "-All-" ) {
- $query .= "and bugs.product=".SqlQuote($::FORM{'product'});
- }
-
- $query .= <<FIN;
-and
- (
- bugs.bug_status = 'NEW' or
- bugs.bug_status = 'ASSIGNED' or
- bugs.bug_status = 'REOPENED'
- )
+ if ($FORM{'product'} ne "-All-" ) {
+ $query .= "and bugs.product=".SqlQuote($FORM{'product'});
+ }
+
+ $query .= <<FIN;
+and
+ (
+ bugs.bug_status = 'NEW' or
+ bugs.bug_status = 'ASSIGNED' or
+ bugs.bug_status = 'REOPENED'
+ )
FIN
# End build up $query string
- print "<font color=purple><tt>$query</tt></font><p>\n"
- unless (! exists $::FORM{'showsql'});
-
- SendSQL ($query);
-
- my $c = 0;
-
- my $quip = "Summary";
- my $bugs_count = 0;
- my $bugs_new_this_week = 0;
- my $bugs_reopened = 0;
- my %bugs_owners;
- my %bugs_summary;
- my %bugs_status;
- my %bugs_totals;
- my %bugs_lookup;
-
- #############################
- # suck contents of database #
- #############################
-
- while (my ($bid, $a, $sev, $st, $prod, $who, $rep, $ts) = FetchSQLData())
- {
- next if (exists $bugs_lookup{$bid});
-
- $bugs_lookup{$bid} ++;
- $bugs_owners{$who} ++;
- $bugs_new_this_week ++ if (time - $ts <= $week);
- $bugs_status{$st} ++;
- $bugs_count ++;
-
- push @{$bugs_summary{$who}{$st}}, $bid;
-
- $bugs_totals{$who}{$st} ++;
- }
-
- if ($::FORM{'quip'})
- {
- if (open (COMMENTS, "<data/comments"))
- {
- my @cdata;
- while (<COMMENTS>)
- {
- push @cdata, $_;
- }
- close COMMENTS;
- $quip = "<i>" . $cdata[int(rand($#cdata + 1))] . "</i>";
- }
- }
-
- #########################
- # start painting report #
- #########################
-
- $bugs_status{'NEW'} ||= '0';
- $bugs_status{'ASSIGNED'} ||= '0';
- $bugs_status{'REOPENED'} ||= '0';
- print <<FIN;
+ print "<font color=purple><tt>$query</tt></font><p>\n"
+ unless (! exists $FORM{'showsql'});
+
+ SendSQL ($query);
+
+ my $c = 0;
+
+ my $quip = "Summary";
+ my $bugs_count = 0;
+ my $bugs_new_this_week = 0;
+ my $bugs_reopened = 0;
+ my %bugs_owners;
+ my %bugs_summary;
+ my %bugs_status;
+ my %bugs_totals;
+ my %bugs_lookup;
+
+ #############################
+ # suck contents of database #
+ #############################
+
+ my $week = 60 * 60 * 24 * 7;
+ while (my ($bid, $a, $sev, $st, $prod, $who, $rep, $ts) = FetchSQLData()) {
+ next if (exists $bugs_lookup{$bid});
+
+ $bugs_lookup{$bid} ++;
+ $bugs_owners{$who} ++;
+ $bugs_new_this_week ++ if (time - $ts <= $week);
+ $bugs_status{$st} ++;
+ $bugs_count ++;
+
+ push @{$bugs_summary{$who}{$st}}, $bid;
+
+ $bugs_totals{$who}{$st} ++;
+ }
+
+ if ($FORM{'quip'}) {
+ if (open (COMMENTS, "<data/comments")) {
+ my @cdata;
+ while (<COMMENTS>) {
+ push @cdata, $_;
+ }
+ close COMMENTS;
+ $quip = "<i>" . $cdata[int(rand($#cdata + 1))] . "</i>";
+ }
+ }
+
+ #########################
+ # start painting report #
+ #########################
+
+ $bugs_status{'NEW'} ||= '0';
+ $bugs_status{'ASSIGNED'} ||= '0';
+ $bugs_status{'REOPENED'} ||= '0';
+ print <<FIN;
<h1>$quip</h1>
<table border=1 cellpadding=5>
<tr>
@@ -345,14 +377,13 @@ FIN
<p>
FIN
- if ($bugs_count == 0)
- {
- print "No bugs found!\n";
- PutFooter() if $::FORM{banner};
- exit;
- }
-
- print <<FIN;
+ if ($bugs_count == 0) {
+ print "No bugs found!\n";
+ PutFooter() if $FORM{banner};
+ exit;
+ }
+
+ print <<FIN;
<h1>Bug Count by Engineer</h1>
<table border=3 cellpadding=5>
<tr>
@@ -364,39 +395,37 @@ FIN
</tr>
FIN
- foreach my $who (sort keys %bugs_summary)
- {
- my $bugz = 0;
- print <<FIN;
+ foreach my $who (sort keys %bugs_summary) {
+ my $bugz = 0;
+ print <<FIN;
<tr>
<td align=left><tt>$who</tt></td>
FIN
-
- foreach my $st (@status)
- {
- $bugs_totals{$who}{$st} += 0;
- print <<FIN;
+
+ foreach my $st (@status) {
+ $bugs_totals{$who}{$st} += 0;
+ print <<FIN;
<td align=center>$bugs_totals{$who}{$st}
FIN
- $bugz += $#{$bugs_summary{$who}{$st}} + 1;
- }
-
- print <<FIN;
+ $bugz += $#{$bugs_summary{$who}{$st}} + 1;
+ }
+
+ print <<FIN;
<td align=center>$bugz</td>
</tr>
FIN
- }
-
- print <<FIN;
+ }
+
+ print <<FIN;
</table>
<p>
FIN
- ###############################
- # individual bugs by engineer #
- ###############################
+ ###############################
+ # individual bugs by engineer #
+ ###############################
- print <<FIN;
+ print <<FIN;
<h1>Individual Bugs by Engineer</h1>
<table border=1 cellpadding=5>
<tr>
@@ -407,426 +436,451 @@ FIN
</tr>
FIN
- foreach my $who (sort keys %bugs_summary)
- {
- print <<FIN;
+ foreach my $who (sort keys %bugs_summary) {
+ print <<FIN;
<tr>
<td align=left><tt>$who</tt></td>
FIN
- foreach my $st (@status)
- {
- my @l;
-
- foreach (sort { $a <=> $b } @{$bugs_summary{$who}{$st}})
- {
- if ($::FORM{'links'})
- {
- push @l, "<a href=\"show_bug.cgi?id=$_\">$_</a>\n";
- }
- else
- {
- push @l, $_;
- }
- }
-
- my $bugz = join ' ', @l;
- $bugz = "&nbsp;" unless ($bugz);
-
- print <<FIN
+ foreach my $st (@status) {
+ my @l;
+
+ foreach (sort { $a <=> $b } @{$bugs_summary{$who}{$st}}) {
+ if ($FORM{'links'}) {
+ push @l, "<a href=\"show_bug.cgi?id=$_\">$_</a>\n";
+ }
+ else {
+ push @l, $_;
+ }
+ }
+
+ my $bugz = join ' ', @l;
+ $bugz = "&nbsp;" unless ($bugz);
+
+ print <<FIN
<td align=left>$bugz</td>
FIN
- }
+ }
- print <<FIN;
+ print <<FIN;
</tr>
FIN
- }
+ }
- print <<FIN;
+ print <<FIN;
</table>
<p>
FIN
- }
+}
-sub is_legal_product
- {
- my $product = shift;
- return grep { $_ eq $product} @myproducts;
- }
+sub is_legal_product {
+ my $product = shift;
+ return grep { $_ eq $product} @myproducts;
+}
-sub show_chart
- {
- my $when = localtime (time);
+sub daily_stats_filename {
+ my ($prodname) = @_;
+ $prodname =~ s/\//-/gs;
+ return $prodname;
+}
- if (! is_legal_product ($::FORM{'product'}))
- {
- &die_politely ("Unknown product: $::FORM{'product'}");
- }
+sub show_chart {
+ if (! is_legal_product ($FORM{'product'})) {
+ &die_politely ("Unknown product: $FORM{'product'}");
+ }
+
+ if (! $FORM{datasets}) {
+ die_politely("You didn't select any datasets to plot");
+ }
print <<FIN;
<center>
FIN
-
- my @dates;
- my @open; my @assigned; my @reopened;
- my @resolved; my @verified; my @closed;
-
- my $prodname = $::FORM{'product'};
-
- $prodname =~ s/\//-/gs;
-
- my $testimg = Chart::Lines->new(2,2);
- my $x = '$testimg->gif()';
- eval $x;
- my $type = ($@ =~ /Can't locate object method/) ? "png" : "gif";
-
- my $file = join '/', $dir, $prodname;
- my $image = "$file.$type";
- my $url_image = $dir . "/" . url_quote($prodname) . ".$type";
-
- if (! open FILE, $file)
- {
- &die_politely ("The tool which gathers bug counts has not been run yet.");
- }
-
- while (<FILE>)
- {
- chomp;
- next if ($_ =~ /^#/ or ! $_);
- my ($date, $open, $assigned, $reopened,
- $resolved, $verified, $closed) = split /\|/, $_;
- my ($yy, $mm, $dd) = $date =~ /^\d{2}(\d{2})(\d{2})(\d{2})$/;
-
- push @dates, "$mm/$dd/$yy";
- push @open, $open;
- push @assigned, $assigned;
- push @reopened, $reopened;
- push @resolved, $resolved;
- push @verified, $verified;
- push @closed, $closed;
- }
-
- close FILE;
-
- if ($#dates < 1)
- {
- &die_politely ("We don't have enough data points to make a graph (yet)");
- }
-
- my $img = Chart::Lines->new (800, 600);
- my @labels = qw (New Assigned Reopened Resolved Verified Closed);
- my @when;
- my $i = 0;
- my @data;
-
- push @data, \@dates;
- push @data, \@open;
- push @data, \@assigned;
- push @data, \@reopened;
- push @data, \@resolved;
- push @data, \@verified;
- push @data, \@closed;
- my $MAXTICKS = 20; # Try not to show any more x ticks than this.
- my $skip = 1;
- if (@dates > $MAXTICKS) {
- $skip = int((@dates + $MAXTICKS - 1) / $MAXTICKS);
- }
-
- my %settings =
- (
- "title" => "Status Counts for $::FORM{'product'}",
- "x_label" => "Dates",
- "y_label" => "Bug Counts",
- "legend_labels" => \@labels,
- "skip_x_ticks" => $skip,
- "y_grid_lines" => "true",
- "grey_background" => "false"
- );
-
- $img->set (%settings);
-
- open IMAGE, ">$image" or die "$image: $!";
- $img->$type (*IMAGE, \@data);
- close IMAGE;
-
- print <<FIN;
+ my $type = chart_image_type();
+ my $data_file = daily_stats_filename($FORM{product});
+ my $image_file = chart_image_name($data_file, $type);
+ my $url_image = "$dir/" . url_quote($image_file);
+
+ if (! -e "$dir/$image_file") {
+ generate_chart("$dir/$data_file", "$dir/$image_file", $type);
+ }
+
+ print <<FIN;
<img src="$url_image">
<br clear=left>
<br>
FIN
- }
+}
-sub die_politely
- {
- my $msg = shift;
+sub chart_image_type {
+ # what chart type should we be generating?
+ my $testimg = Chart::Lines->new(2,2);
+ my $type = $testimg->can('gif') ? "gif" : "png";
+ undef $testimg;
+ return $type;
+}
- print <<FIN;
+sub chart_image_name {
+ my ($data_file, $type) = @_;
+
+ my $id = datasets_id($FORM{datasets});
+ my $doy = day_of_year();
+
+ return "${data_file}_${id}.$type";
+}
+
+# Cache charts by generating a unique filename based on what they
+# show. Charts should be deleted by collectstats.pl nightly.
+sub datasets_id {
+ # Current method is very long filenames...
+ my $longname = "";
+ foreach (@_) {
+ $longname .= $_;
+ }
+ return $longname;
+}
+
+sub day_of_year {
+ my ($mday, $month, $year) = (localtime())[3 .. 5];
+ $month += 1;
+ $year += 1900;
+ my $date = sprintf "%02d%02d%04d", $mday, $month, $year;
+}
+
+sub generate_chart {
+ my ($data_file, $image_file, $type) = @_;
+
+ if (! open FILE, $data_file) {
+ &die_politely ("The tool which gathers bug counts has not been run yet.");
+ }
+
+ my @fields;
+ my @labels = qw(DATE);
+ my %datasets = map { $_ => 1 } split /:/, $FORM{datasets};
+
+ my %data = ();
+ while (<FILE>) {
+ chomp;
+ next unless $_;
+ if (/^#/) {
+ if (/^# fields?: (.*)\s*$/) {
+ @fields = split /\|/, $1;
+ &die_politely("`# fields: ' line didn't start with DATE, but with $fields[0]")
+ unless $fields[0] =~ /date/i;
+ push @labels, grep($datasets{$_}, @fields);
+ }
+ next;
+ }
+
+ &die_politely("`# fields: ' line was not found before start of data")
+ unless @fields;
+
+ my @line = split /\|/;
+ my $date = $line[0];
+ my ($yy, $mm, $dd) = $date =~ /^\d{2}(\d{2})(\d{2})(\d{2})$/;
+ push @{$data{DATE}}, "$mm/$dd/$yy";
+
+ for my $i (1 .. $#fields) {
+ my $field = $fields[$i];
+ if (! defined $line[$i] or $line[$i] eq '') {
+ # no data point given, don't plot (this will probably
+ # generate loads of Chart::Base warnings, but that's not
+ # our fault.
+ push @{$data{$field}}, undef;
+ }
+ else {
+ push @{$data{$field}}, $line[$i];
+ }
+ }
+ }
+
+ shift @labels;
+
+ close FILE;
+
+ if (! @{$data{DATE}}) {
+ &die_politely ("We don't have enough data points to make a graph (yet)");
+ }
+
+ my $img = Chart::Lines->new (800, 600);
+ my $i = 0;
+
+ my $MAXTICKS = 20; # Try not to show any more x ticks than this.
+ my $skip = 1;
+ if (@{$data{DATE}} > $MAXTICKS) {
+ $skip = int((@{$data{DATE}} + $MAXTICKS - 1) / $MAXTICKS);
+ }
+
+ my %settings =
+ (
+ "title" => "Status Counts for $FORM{'product'}",
+ "x_label" => "Dates",
+ "y_label" => "Bug Counts",
+ "legend_labels" => \@labels,
+ "skip_x_ticks" => $skip,
+ "y_grid_lines" => "true",
+ "grey_background" => "false",
+ "colors" => {
+ # default dataset colours are too alike
+ dataset4 => [0, 0, 0], # black
+ },
+ );
+
+ $img->set (%settings);
+ $img->$type($image_file, [ @data{('DATE', @labels)} ]);
+}
+
+sub die_politely {
+ my $msg = shift;
+
+ print <<FIN;
<p>
<table border=1 cellpadding=10>
<tr>
<td align=center>
<font color=blue>Sorry, but ...</font>
<p>
-There is no graph available for <b>$::FORM{'product'}</b><p>
+There is no graph available for <b>$FORM{'product'}</b><p>
-<font size=-1>
$msg
<p>
-</font>
</td>
</tr>
</table>
<p>
FIN
-
- PutFooter() if $::FORM{banner};
- exit;
- }
+
+ PutFooter() if $FORM{banner};
+ exit;
+}
sub bybugs {
$bugsperperson{$a} <=> $bugsperperson{$b}
- }
+}
-sub most_doomed_for_milestone
- {
- my $when = localtime (time);
- my $ms = "M" . Param("curmilestone");
- my $quip = "Summary";
+sub most_doomed_for_milestone {
+ my $when = localtime (time);
+ my $ms = "M" . Param("curmilestone");
+ my $quip = "Summary";
- print "<center>\n<h1>";
- if( $::FORM{'product'} ne "-All-" ) {
- print "Most Doomed for $ms ($::FORM{'product'})";
- } else {
- print "Most Doomed for $ms";
- }
- print "</h1>\n$when<p>\n";
-
- #########################
- # start painting report #
- #########################
-
- if ($::FORM{'quip'})
- {
- if (open (COMMENTS, "<data/comments"))
- {
- my @cdata;
- while (<COMMENTS>)
- {
- push @cdata, $_;
- }
- close COMMENTS;
- $quip = "<i>" . $cdata[int(rand($#cdata + 1))] . "</i>"; }
- }
+ print "<center>\n<h1>";
+ if( $FORM{'product'} ne "-All-" ) {
+ print "Most Doomed for $ms ($FORM{'product'})";
+ } else {
+ print "Most Doomed for $ms";
+ }
+ print "</h1>\n$when<p>\n";
+ #########################
+ # start painting report #
+ #########################
- # Build up $query string
- my $query;
- $query = "select distinct assigned_to from bugs where target_milestone=\"$ms\"";
- if( $::FORM{'product'} ne "-All-" ) {
- $query .= "and bugs.product=".SqlQuote($::FORM{'product'});
- }
- $query .= <<FIN;
-and
- (
- bugs.bug_status = 'NEW' or
- bugs.bug_status = 'ASSIGNED' or
- bugs.bug_status = 'REOPENED'
- )
+ if ($FORM{'quip'}) {
+ if (open (COMMENTS, "<data/comments")) {
+ my @cdata;
+ while (<COMMENTS>) {
+ push @cdata, $_;
+ }
+ close COMMENTS;
+ $quip = "<i>" . $cdata[int(rand($#cdata + 1))] . "</i>";
+ }
+ }
+
+ # Build up $query string
+ my $query;
+ $query = "select distinct assigned_to from bugs where target_milestone=\"$ms\"";
+ if ($FORM{'product'} ne "-All-" ) {
+ $query .= "and bugs.product=".SqlQuote($FORM{'product'});
+ }
+ $query .= <<FIN;
+and
+ (
+ bugs.bug_status = 'NEW' or
+ bugs.bug_status = 'ASSIGNED' or
+ bugs.bug_status = 'REOPENED'
+ )
FIN
# End build up $query string
- SendSQL ($query);
- my @people = ();
- while (my ($person) = FetchSQLData())
- {
- push @people, $person;
- }
+ SendSQL ($query);
+ my @people = ();
+ while (my ($person) = FetchSQLData()) {
+ push @people, $person;
+ }
- #############################
- # suck contents of database #
- #############################
- my $person = "";
- my $bugtotal = 0;
- foreach $person (@people)
- {
- my $query = "select count(bug_id) from bugs,profiles where target_milestone=\"$ms\" and userid=assigned_to and userid=\"$person\"";
- if( $::FORM{'product'} ne "-All-" ) {
- $query .= "and bugs.product=".SqlQuote($::FORM{'product'});
- }
- $query .= <<FIN;
-and
- (
- bugs.bug_status = 'NEW' or
- bugs.bug_status = 'ASSIGNED' or
- bugs.bug_status = 'REOPENED'
- )
+ #############################
+ # suck contents of database #
+ #############################
+ my $person = "";
+ my $bugtotal = 0;
+ foreach $person (@people) {
+ my $query = "select count(bug_id) from bugs,profiles where target_milestone=\"$ms\" and userid=assigned_to and userid=\"$person\"";
+ if( $FORM{'product'} ne "-All-" ) {
+ $query .= "and bugs.product=".SqlQuote($FORM{'product'});
+ }
+ $query .= <<FIN;
+and
+ (
+ bugs.bug_status = 'NEW' or
+ bugs.bug_status = 'ASSIGNED' or
+ bugs.bug_status = 'REOPENED'
+ )
FIN
- SendSQL ($query);
- my $bugcount = FetchSQLData();
- $bugsperperson{$person} = $bugcount;
- $bugtotal += $bugcount;
- }
+ SendSQL ($query);
+ my $bugcount = FetchSQLData();
+ $bugsperperson{$person} = $bugcount;
+ $bugtotal += $bugcount;
+ }
-# sort people by the number of bugs they have assigned to this milestone
- @people = sort bybugs @people;
- my $totalpeople = @people;
+# sort people by the number of bugs they have assigned to this milestone
+ @people = sort bybugs @people;
+ my $totalpeople = @people;
- print "<TABLE>\n";
- print "<TR><TD COLSPAN=2>\n";
- print "$totalpeople engineers have $bugtotal $ms bugs and features.\n";
- print "</TD></TR>\n";
-
- while (@people)
- {
- $person = pop @people;
- print "<TR><TD>\n";
- SendSQL("select login_name from profiles where userid=$person");
- my $login_name= FetchSQLData();
- print("<A HREF=\"buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&target_milestone=$ms&assigned_to=$login_name");
- if( $::FORM{'product'} ne "-All-" ) {
- print "&product=" . url_quote($::FORM{'product'});
- }
- print("\">\n");
- print("$bugsperperson{$person} bugs and features");
- print("</A>");
- print(" for \n");
- print("<A HREF=\"mailto:$login_name\">");
- print("$login_name");
- print("</A>\n");
- print("</TD><TD>\n");
-
- $person = pop @people;
- if ($person) {
- SendSQL("select login_name from profiles where userid=$person");
- my $login_name= FetchSQLData();
- print("<A HREF=\"buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&target_milestone=$ms&assigned_to=$login_name\">\n");
- print("$bugsperperson{$person} bugs and features");
- print("</A>");
- print(" for \n");
- print("<A HREF=\"mailto:$login_name\">");
- print("$login_name");
- print("</A>\n");
- print("</TD></TR>\n\n");
- }
- }
- print "</TABLE>\n";
-
+ print "<TABLE>\n";
+ print "<TR><TD COLSPAN=2>\n";
+ print "$totalpeople engineers have $bugtotal $ms bugs and features.\n";
+ print "</TD></TR>\n";
+
+ while (@people) {
+ $person = pop @people;
+ print "<TR><TD>\n";
+ SendSQL("select login_name from profiles where userid=$person");
+ my $login_name= FetchSQLData();
+ print("<A HREF=\"buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&target_milestone=$ms&assigned_to=$login_name");
+ if ($FORM{'product'} ne "-All-" ) {
+ print "&product=" . url_quote($FORM{'product'});
+ }
+ print("\">\n");
+ print("$bugsperperson{$person} bugs and features");
+ print("</A>");
+ print(" for \n");
+ print("<A HREF=\"mailto:$login_name\">");
+ print("$login_name");
+ print("</A>\n");
+ print("</TD><TD>\n");
+
+ $person = pop @people;
+ if ($person) {
+ SendSQL("select login_name from profiles where userid=$person");
+ my $login_name= FetchSQLData();
+ print("<A HREF=\"buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&target_milestone=$ms&assigned_to=$login_name\">\n");
+ print("$bugsperperson{$person} bugs and features");
+ print("</A>");
+ print(" for \n");
+ print("<A HREF=\"mailto:$login_name\">");
+ print("$login_name");
+ print("</A>\n");
+ print("</TD></TR>\n\n");
}
+ }
+ print "</TABLE>\n";
+}
+sub most_recently_doomed {
+ my $when = localtime (time);
+ my $ms = "M" . Param("curmilestone");
+ my $quip = "Summary";
+
+ print "<center>\n<h1>";
+ if( $FORM{'product'} ne "-All-" ) {
+ print "Most Recently Doomed ($FORM{'product'})";
+ } else {
+ print "Most Recently Doomed";
+ }
+ print "</h1>\n$when<p>\n";
-sub most_recently_doomed
- {
- my $when = localtime (time);
- my $ms = "M" . Param("curmilestone");
- my $quip = "Summary";
+ #########################
+ # start painting report #
+ #########################
- print "<center>\n<h1>";
- if( $::FORM{'product'} ne "-All-" ) {
- print "Most Recently Doomed ($::FORM{'product'})";
- } else {
- print "Most Recently Doomed";
+ if ($FORM{'quip'}) {
+ if (open (COMMENTS, "<data/comments")) {
+ my @cdata;
+ while (<COMMENTS>) {
+ push @cdata, $_;
}
- print "</h1>\n$when<p>\n";
-
- #########################
- # start painting report #
- #########################
-
- if ($::FORM{'quip'})
- {
- if (open (COMMENTS, "<data/comments"))
- {
- my @cdata;
- while (<COMMENTS>)
- {
- push @cdata, $_;
- }
- close COMMENTS;
- $quip = "<i>" . $cdata[int(rand($#cdata + 1))] . "</i>"; }
- }
+ close COMMENTS;
+ $quip = "<i>" . $cdata[int(rand($#cdata + 1))] . "</i>";
+ }
+ }
- # Build up $query string
- my $query;
- $query = "select distinct assigned_to from bugs where bugs.bug_status='NEW' and target_milestone='' and bug_severity!='enhancement' and status_whiteboard='' and (product='Browser' or product='MailNews')";
- if( $::FORM{'product'} ne "-All-" ) {
- $query .= "and bugs.product=".SqlQuote($::FORM{'product'});
- }
+ # Build up $query string
+ my $query = "select distinct assigned_to from bugs where bugs.bug_status='NEW' and target_milestone='' and bug_severity!='enhancement' and status_whiteboard='' and (product='Browser' or product='MailNews')";
+ if ($FORM{'product'} ne "-All-" ) {
+ $query .= "and bugs.product=".SqlQuote($FORM{'product'});
+ }
# End build up $query string
- SendSQL ($query);
- my @people = ();
- while (my ($person) = FetchSQLData())
- {
- push @people, $person;
- }
+ SendSQL ($query);
+ my @people = ();
+ while (my ($person) = FetchSQLData()) {
+ push @people, $person;
+ }
- #############################
- # suck contents of database #
- #############################
- my $person = "";
- my $bugtotal = 0;
- foreach $person (@people)
- {
- my $query = "select count(bug_id) from bugs,profiles where bugs.bug_status='NEW' and userid=assigned_to and userid='$person' and target_milestone='' and bug_severity!='enhancement' and status_whiteboard='' and (product='Browser' or product='MailNews')";
- if( $::FORM{'product'} ne "-All-" ) {
- $query .= "and bugs.product='$::FORM{'product'}'";
- }
- SendSQL ($query);
- my $bugcount = FetchSQLData();
- $bugsperperson{$person} = $bugcount;
- $bugtotal += $bugcount;
- }
+ #############################
+ # suck contents of database #
+ #############################
+ my $person = "";
+ my $bugtotal = 0;
+ foreach $person (@people) {
+ my $query = "select count(bug_id) from bugs,profiles where bugs.bug_status='NEW' and userid=assigned_to and userid='$person' and target_milestone='' and bug_severity!='enhancement' and status_whiteboard='' and (product='Browser' or product='MailNews')";
+ if( $FORM{'product'} ne "-All-" ) {
+ $query .= "and bugs.product='$FORM{'product'}'";
+ }
+ SendSQL ($query);
+ my $bugcount = FetchSQLData();
+ $bugsperperson{$person} = $bugcount;
+ $bugtotal += $bugcount;
+ }
-# sort people by the number of bugs they have assigned to this milestone
- @people = sort bybugs @people;
- my $totalpeople = @people;
-
- if ($totalpeople > 20) {
- splice @people, 0, $totalpeople-20;
- }
+# sort people by the number of bugs they have assigned to this milestone
+ @people = sort bybugs @people;
+ my $totalpeople = @people;
+
+ if ($totalpeople > 20) {
+ splice @people, 0, $totalpeople-20;
+ }
- print "<TABLE>\n";
- print "<TR><TD COLSPAN=2>\n";
- print "$totalpeople engineers have $bugtotal untouched new bugs.\n";
- if ($totalpeople > 20) {
- print "These are the 20 most doomed.";
- }
- print "</TD></TR>\n";
-
- while (@people)
- {
- $person = pop @people;
- print "<TR><TD>\n";
- SendSQL("select login_name from profiles where userid=$person");
- my $login_name= FetchSQLData();
- print("<A HREF=\"buglist.cgi?bug_status=NEW&email1=$login_name&emailtype1=substring&emailassigned_to1=1&product=Browser&product=MailNews&target_milestone=---&status_whiteboard=.&status_whiteboard_type=notregexp&bug_severity=blocker&bug_severity=critical&bug_severity=major&bug_severity=normal&bug_severity=minor&bug_severity=trivial\">\n");
- print("$bugsperperson{$person} bugs");
- print("</A>");
- print(" for \n");
- print("<A HREF=\"mailto:$login_name\">");
- print("$login_name");
- print("</A>\n");
- print("</TD><TD>\n");
-
- $person = pop @people;
- if ($person) {
- SendSQL("select login_name from profiles where userid=$person");
- my $login_name= FetchSQLData();
- print("<A HREF=\"buglist.cgi?bug_status=NEW&email1=$login_name&emailtype1=substring&emailassigned_to1=1&product=Browser&product=MailNews&target_milestone=---&status_whiteboard=.&status_whiteboard_type=notregexp&bug_severity=blocker&bug_severity=critical&bug_severity=major&bug_severity=normal&bug_severity=minor&bug_severity=trivial\">\n");
- print("$bugsperperson{$person} bugs");
- print("</A>");
- print(" for \n");
- print("<A HREF=\"mailto:$login_name\">");
- print("$login_name");
- print("</A>\n");
- print("</TD></TR>\n\n");
- }
- }
- print "</TABLE>\n";
-
+ print "<TABLE>\n";
+ print "<TR><TD COLSPAN=2>\n";
+ print "$totalpeople engineers have $bugtotal untouched new bugs.\n";
+ if ($totalpeople > 20) {
+ print "These are the 20 most doomed.";
+ }
+ print "</TD></TR>\n";
+
+ while (@people) {
+ $person = pop @people;
+ print "<TR><TD>\n";
+ SendSQL("select login_name from profiles where userid=$person");
+ my $login_name= FetchSQLData();
+ print("<A HREF=\"buglist.cgi?bug_status=NEW&email1=$login_name&emailtype1=substring&emailassigned_to1=1&product=Browser&product=MailNews&target_milestone=---&status_whiteboard=.&status_whiteboard_type=notregexp&bug_severity=blocker&bug_severity=critical&bug_severity=major&bug_severity=normal&bug_severity=minor&bug_severity=trivial\">\n");
+ print("$bugsperperson{$person} bugs");
+ print("</A>");
+ print(" for \n");
+ print("<A HREF=\"mailto:$login_name\">");
+ print("$login_name");
+ print("</A>\n");
+ print("</TD><TD>\n");
+
+ $person = pop @people;
+ if ($person) {
+ SendSQL("select login_name from profiles where userid=$person");
+ my $login_name= FetchSQLData();
+ print("<A HREF=\"buglist.cgi?bug_status=NEW&email1=$login_name&emailtype1=substring&emailassigned_to1=1&product=Browser&product=MailNews&target_milestone=---&status_whiteboard=.&status_whiteboard_type=notregexp&bug_severity=blocker&bug_severity=critical&bug_severity=major&bug_severity=normal&bug_severity=minor&bug_severity=trivial\">\n");
+ print("$bugsperperson{$person} bugs");
+ print("</A>");
+ print(" for \n");
+ print("<A HREF=\"mailto:$login_name\">");
+ print("$login_name");
+ print("</A>\n");
+ print("</TD></TR>\n\n");
}
+ }
+ print "</TABLE>\n";
+
+}