diff options
author | tara%tequilarista.org <> | 2000-10-31 08:02:41 +0100 |
---|---|---|
committer | tara%tequilarista.org <> | 2000-10-31 08:02:41 +0100 |
commit | c6e881d0814e9a2527bd61a3f863e12aaa5abf22 (patch) | |
tree | deb9dc58a8da7cf61b493ec8ef3eccd54a73b9cd | |
parent | ff238d36024ea1e58d8cfa1c797487c313b4d8d7 (diff) | |
download | bugzilla-c6e881d0814e9a2527bd61a3f863e12aaa5abf22.tar.gz bugzilla-c6e881d0814e9a2527bd61a3f863e12aaa5abf22.tar.xz |
Landing Gerv and Adam's changes for bug #6682
-rwxr-xr-x | collectstats.pl | 55 | ||||
-rwxr-xr-x | reports.cgi | 1160 |
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> Links to Bugs<br> <input type=checkbox name=banner checked value=1> Banner<br> FIN - if (Param('usequip')) { - print "<input type=checkbox name=quip value=1> Quip<br>"; - } else { - print "<input type=hidden name=quip value=0>"; - } - print <<FIN; + + if (Param('usequip')) { + print "<input type=checkbox name=quip value=1> 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> 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 = " " 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 = " " 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"; + +} |