From faf2e2dcec78484e261a70df3257fc011eeb75f7 Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Thu, 11 Apr 2002 05:29:57 +0000 Subject: Bug 199635 - templatise duplicates.cgi. Patch by gerv, r=myk, bbaetz. --- duplicates.cgi | 354 ++++++++++----------------- template/default/report/duplicates.html.tmpl | 275 +++++++++++++++++++++ 2 files changed, 400 insertions(+), 229 deletions(-) create mode 100644 template/default/report/duplicates.html.tmpl diff --git a/duplicates.cgi b/duplicates.cgi index 78f29829f..a85eb6bb7 100755 --- a/duplicates.cgi +++ b/duplicates.cgi @@ -25,7 +25,7 @@ use diagnostics; use strict; -use CGI "param"; + use AnyDBM_File; use lib qw(.); @@ -33,260 +33,156 @@ use lib qw(.); require "globals.pl"; require "CGI.pl"; +# Use global templatisation variables. +use vars qw($template $vars); + ConnectToDatabase(1); GetVersionTable(); quietly_check_login(); -# Silly used-once warnings -$::userid = $::userid; -$::usergroupset = $::usergroupset; +use vars qw (%FORM $userid $usergroupset @legal_product); my %dbmcount; my %count; -my $dobefore = 0; -my $before = ""; my %before; # Get params from URL - -my $changedsince = 7; # default one week -my $maxrows = 100; # arbitrary limit on max number of rows -my $sortby = "dup_count"; # default to sorting by dup count - -if (defined(param("sortby"))) -{ - $sortby = param("sortby"); -} - -# Check for changedsince param, and see if it's a positive integer -if (defined(param("changedsince")) && param("changedsince") =~ /^\d{1,4}$/) -{ - $changedsince = param("changedsince"); +sub formvalue { + my ($name, $default) = (@_); + return $FORM{$name} || $default || ""; } -# check for max rows param, and see if it's a positive integer -if (defined(param("maxrows")) && param("maxrows") =~ /^\d{1,4}$/) -{ - $maxrows = param("maxrows"); -} +my $sortby = formvalue("sortby"); +my $changedsince = formvalue("changedsince", 7); +my $maxrows = formvalue("maxrows", 100); +my $openonly = formvalue("openonly"); +my $reverse = formvalue("reverse"); +my $product = formvalue("product"); +my $sortvisible = formvalue("sortvisible"); +my @buglist = (split(/[:,]/, formvalue("bug_id"))); -# Start the page -print "Content-type: text/html\n"; -print "\n"; -PutHeader("Most Frequently Reported Bugs"); +# Small backwards-compatibility hack, dated 2002-04-10. +$sortby = "count" if $sortby eq "dup_count"; # Open today's record of dupes -my $today = &days_ago(0); +my $today = days_ago(0); +my $yesterday = days_ago(1); -if () -{ - dbmopen(%dbmcount, "data/duplicates/dupes$today", 0644) || - &die_politely("Can't open today's dupes file: $!"); +if () { + dbmopen(%dbmcount, "data/duplicates/dupes$today", 0644) + || DisplayError("Can't open today ($today)'s dupes file: $!") + && exit; +} +elsif () { + dbmopen(%dbmcount, "data/duplicates/dupes$yesterday", 0644) + || DisplayError("Can't open yesterday ($yesterday)'s dupes file: $!") + && exit; } -else -{ - # Try yesterday's, then (in case today's hasn't been created yet) - $today = &days_ago(1); - if () - { - dbmopen(%dbmcount, "data/duplicates/dupes$today", 0644) || - &die_politely("Can't open yesterday's dupes file: $!"); - } - else - { - &die_politely("There are no duplicate statistics for today ($today) or yesterday."); - } +else { + DisplayError("There are no duplicate statistics for today ($today) or + yesterday."); + exit; } # Copy hash (so we don't mess up the on-disk file when we remove entries) %count = %dbmcount; -my $key; -my $value; + +# Remove all those dupes under the threshold parameter. +# We do this, before the sorting, for performance reasons. my $threshold = Param("mostfreqthreshold"); -# Remove all those dupes under the threshold (for performance reasons) -while (($key, $value) = each %count) -{ - if ($value < $threshold) - { - delete $count{$key}; - } +while (my ($key, $value) = each %count) { + delete $count{$key} if ($value < $threshold); } # Try and open the database from "changedsince" days ago -$before = &days_ago($changedsince); - -if () -{ - dbmopen(%before, "data/duplicates/dupes$before", 0644) && ($dobefore = 1); -} - -print Param("mostfreqhtml"); - +my $dobefore = 0; my %delta; - -if ($dobefore) -{ - # Calculate the deltas if we are doing a "before" - foreach (keys(%count)) - { - $delta{$_} = $count{$_} - $before{$_}; - } -} - -# Sort, if required -my @sortedcount; - -if ($sortby eq "delta") -{ - @sortedcount = sort by_delta keys(%count); -} -elsif ($sortby eq "bug_no") -{ - @sortedcount = reverse sort by_bug_no keys(%count); -} -elsif ($sortby eq "dup_count") -{ - @sortedcount = sort by_dup_count keys(%count); -} - -my $i = 0; - -# Produce a string of bug numbers for a Bugzilla buglist. -my $commabugs = ""; -foreach (@sortedcount) -{ - last if ($i == $maxrows); - - $commabugs .= ($_ . ","); - $i++; -} - -# Avoid having a comma at the end - Bad Things happen. -chop $commabugs; - -print qq| - -
- - -Give this to me as a . (Note: the order may not be the same.) -
- - - - - -\n|; - -if ($dobefore) -{ - print ""; -} - -print " - - - - - -\n\n"; - -$i = 0; - -foreach (@sortedcount) -{ - my $id = $_; - SendSQL(SelectVisible("SELECT component, bug_severity, op_sys, target_milestone, short_desc, bug_status, resolution" . - " FROM bugs WHERE bugs.bug_id = $id", $::userid, $::usergroupset)); - next unless MoreSQLData(); - my ($component, $severity, $op_sys, $milestone, $summary, $bug_status, $resolution) = FetchSQLData(); - $summary = html_quote($summary); - - # Show all bugs except those CLOSED _OR_ VERIFIED but not INVALID or WONTFIX. - # We want to see VERIFIED INVALID and WONTFIX because common "bugs" which aren't - # bugs end up in this state. - unless ( ($bug_status eq "CLOSED") || ( ($bug_status eq "VERIFIED") && - ! ( ($resolution eq "INVALID") || ($resolution eq "WONTFIX") ) ) ) { - print ""; - print '"; - print ""; - if ($dobefore) - { - print ""; - } - print "\n "; - print ""; - print ""; - print ""; - print ""; - print "\n"; - - $i++; - } - - if ($i == $maxrows) - { - last; - } -} - -print "
-Bug # -
-Dupe
Count
-
- Change in - last
$changedsince day(s)
Component
Severity
Op Sys
Target
Milestone
Summary
'; - if ( ($bug_status eq "RESOLVED") || ($bug_status eq "VERIFIED") ) { - print ""; - } - print ""; - print $id . ""; - if ( ($bug_status eq "RESOLVED") || ($bug_status eq "VERIFIED") ) { - print ""; - } - print "
$count{$id}
$delta{$id}
$component
$severity
$op_sys
$milestone
$summary


"; -PutFooter(); - - -sub by_bug_no -{ - return ($a <=> $b); -} - -sub by_dup_count -{ - return -($count{$a} <=> $count{$b}); -} - -sub by_delta -{ - return -($delta{$a} <=> $delta{$b}); -} - -sub days_ago -{ - my ($dom, $mon, $year) = (localtime(time - ($_[0]*24*60*60)))[3, 4, 5]; - return sprintf "%04d-%02d-%02d", 1900 + $year, ++$mon, $dom; -} - -sub die_politely { - my $msg = shift; - - print < - - - - -
-$msg -
-

-FIN - - PutFooter(); - exit; +my $whenever = days_ago($changedsince); + +if () { + dbmopen(%before, "data/duplicates/dupes$whenever", 0644) + || DisplayError("Can't open $changedsince days ago ($whenever)'s " . + "dupes file: $!"); + + # Calculate the deltas + ($delta{$_} = $count{$_} - $before{$_}) foreach (keys(%count)); + + $dobefore = 1; +} + +# Don't add CLOSED, and don't add VERIFIED unless they are INVALID or +# WONTFIX. We want to see VERIFIED INVALID and WONTFIX because common +# "bugs" which aren't bugs end up in this state. +my $generic_query = " + SELECT component, bug_severity, op_sys, target_milestone, + short_desc, bug_status, resolution + FROM bugs + WHERE (bug_status != 'CLOSED') + AND ((bug_status = 'VERIFIED' AND resolution IN ('INVALID', 'WONTFIX')) + OR (bug_status != 'VERIFIED')) + AND "; + +# Limit to a single product if requested +$generic_query .= (" product = " . SqlQuote($product) . " AND ") if $product; + +my @bugs; +my @bug_ids; +my $loop = 0; + +foreach my $id (keys(%count)) { + # Maximum row count is dealt with in the template. + # If there's a buglist, restrict the bugs to that list. + next if $sortvisible && $buglist[0] && (lsearch(\@buglist, $id) == -1); + + SendSQL(SelectVisible("$generic_query bugs.bug_id = $id", + $userid, + $usergroupset)); + + next unless MoreSQLData(); + my ($component, $bug_severity, $op_sys, $target_milestone, + $short_desc, $bug_status, $resolution) = FetchSQLData(); + + # Limit to open bugs only if requested + next if $openonly && ($resolution ne ""); + + push (@bugs, { id => $id, + count => $count{$id}, + delta => $delta{$id}, + component => $component, + bug_severity => $bug_severity, + op_sys => $op_sys, + target_milestone => $target_milestone, + short_desc => $short_desc, + bug_status => $bug_status, + resolution => $resolution }); + push (@bug_ids, $id); + $loop++; +} + +$vars->{'bugs'} = \@bugs; +$vars->{'bug_ids'} = \@bug_ids; + +$vars->{'dobefore'} = $dobefore; +$vars->{'sortby'} = $sortby; +$vars->{'sortvisible'} = $sortvisible; +$vars->{'changedsince'} = $changedsince; +$vars->{'maxrows'} = $maxrows; +$vars->{'openonly'} = $openonly; +$vars->{'reverse'} = $reverse; +$vars->{'product'} = $product; +$vars->{'products'} = \@::legal_product; + +print "Content-type: text/html\n\n"; + +# Generate and return the UI (HTML page) from the appropriate template. +$template->process("report/duplicates.html.tmpl", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; + + +sub days_ago { + my ($dom, $mon, $year) = (localtime(time - ($_[0]*24*60*60)))[3, 4, 5]; + return sprintf "%04d-%02d-%02d", 1900 + $year, ++$mon, $dom; } diff --git a/template/default/report/duplicates.html.tmpl b/template/default/report/duplicates.html.tmpl new file mode 100644 index 000000000..1f606664a --- /dev/null +++ b/template/default/report/duplicates.html.tmpl @@ -0,0 +1,275 @@ +[%# 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 + #%] + +[%# INTERFACE: + # bugs: list of hashes. May be empty. Each hash has nine members: + # id: integer. The bug number + # count: integer. The number of dupes + # delta: integer. The change in count in the last $changedsince days + # component: string. The bug's component + # bug_severity: string. The bug's severity. + # op_sys: string. The bug's reported OS. + # target_milestone: string. The bug's TM. + # short_desc: string. The bug's summary. + # bug_status: string. The bug's status. + # + # bug_ids: list of integers. May be empty. The IDs of the bugs in $bugs. + # products: list of strings. The products this user can see. + # + # sortby: string. the column on which we are sorting the buglist. + # reverse: boolean. True if we are reversing the current sort. + # maxrows: integer. Max number of rows to display. + # changedsince: integer. The number of days ago for the changedsince column. + # openonly: boolean. True if we are only showing open bugs. + # product: string. Restrict to this product only. + #%] + +[% IF product %] + [% title = "Most Frequently Reported Bugs for $product" %] +[% ELSE %] + [% title = "Most Frequently Reported Bugs" %] +[% END%] + +[% INCLUDE global/header %] + +

+ What is this data? +
+ Change parameters +

+ +[%# *** Column Headers *** %] + + + + [% FOREACH column = [ { name => "id", description => "Bug #" }, + { name => "count", description => "Dupe
Count" }, + { name => "delta", + description => "Change in last
$changedsince day(s)" }, + { name => "component", description => "Component" }, + { name => "bug_severity", description => "Severity" }, + { name => "op_sys", description => "Op Sys" }, + { name => "target_milestone", + description => "Target
Milestone" }, + { name => "short_desc", description => "Summary" } ] + %] + + [%# Small hack to keep delta column out if we don't need it %] + [% NEXT IF column.name == "delta" AND NOT dobefore %] + + + [% END %] + + +[% IF NOT sortby %] + [% sortby = "count"; reverse = "1" %] +[% END %] + +[% IF sortby == "id" OR sortby == "count" OR sortby == "delta" %] + [%# Numeric sort %] + [% sortedbugs = bugs.nsort(sortby) %] +[% ELSE %] + [% sortedbugs = bugs.sort(sortby) %] +[% END %] + +[% IF reverse %] + [% bugs = sortedbugs.reverse %] +[% ELSE %] + [% bugs = sortedbugs %] +[% END %] + +[%# *** Buglist *** %] + +[%# We need to keep track of the bug IDs we are actually displaying, because + # if the user decides to sort the visible list, we need to know what that + # list actually is. %] +[% vis_bug_ids = [] %] + +[% FOREACH bug = bugs %] + [% LAST IF loop.index() >= maxrows %] + [% vis_bug_ids.push(bug.id) %] + + + + + + + [% IF dobefore %] + + [% END %] + + + + + + + +[% END %] + +
+
+ + [% bug_ids_string = bug_ids.join(',') %] + + [% column.description %] + +
+
+
+ [% "" IF bug.resolution != "" %] + [% bug.id %] + [% "" IF bug.resolution != "" %] +
+
+
+ [% bug.count %] +
+
[% bug.delta %]
[% bug.component %]
[% bug.bug_severity %]
[% bug.op_sys %]
[% bug.target_milestone %]
[% bug.short_desc FILTER html %]
+ +
+
+ +[%# *** Parameters *** %] + +[% bug_ids_string = vis_bug_ids.join(',') %] + +

Change Parameters

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
When sorting or restricting, + work with: + + +
+ + +
Restrict to products: + +
Max rows: + +
Change column is change in the last: + days +
+ + + +
+ + +
+ +
+ + + Or just give this to me as a . + (Note: the order may not be the same.) +
+ +
+ + + What are "Most Frequently Reported Bugs"? + + +
+ The Most Frequent Bugs page lists the known open bugs which + are reported most frequently in recent builds of Mozilla. It is + automatically generated from the Bugzilla database every 24 hours, by + counting the number of direct and indirect duplicates of bugs. + This information is provided in order to assist in minimizing + the amount of duplicate bugs entered into Bugzilla which in turn cuts down + on development time. +
+ +How do I use this list? + +
    +
  • Review the most frequent bugs list.
  • +
  • If problem is listed:
  • + +
      +
    • Click on Bug # link to confirm that you have found the same bug and + comment if you have additional information. Or move on with your testing + of the product. +
    • +
    + +
  • If problem not listed:
  • + +
      +
    • Go to the Bugzilla Search + page to try and locate a similar bug that has already been written.
    • +
    • If you find your bug in Bugzilla, feel free to comment with any new or + additional data you may have.
    • +
    • If you cannot find your problem already documented in Bugzilla, go to + the + Bugzilla + Helper + and post a new bug.
    • +
    +
+ +[% INCLUDE global/footer %] -- cgit v1.2.1