duplicates.cgi 6.57 KB
Newer Older
1
#!/usr/bonsaitools/bin/perl -wT
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# -*- 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>
#
# Generates mostfreq list from data collected by collectstats.pl.

25

26
use strict;
27

28
use AnyDBM_File;
29 30 31

use lib qw(.);

32 33 34
require "globals.pl";
require "CGI.pl";

35 36 37
# Use global templatisation variables.
use vars qw($template $vars);

38
ConnectToDatabase(1);
39 40
GetVersionTable();

41 42
quietly_check_login();

43
use vars qw (%FORM $userid @legal_product);
44

45
my %dbmcount;
46 47 48
my %count;
my %before;

49
# Get params from URL
50 51 52
sub formvalue {
    my ($name, $default) = (@_);
    return $FORM{$name} || $default || "";
53
}
54

55 56 57 58 59 60 61 62
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")));
63

64 65 66 67
my $product_id;
if ($product) {
    $product_id = get_product_id($product);
    if (!$product_id) {
68 69
        $vars->{'product'} = $product;
        ThrowUserError("invalid_product_name");
70 71 72
    }
}

73 74
# Small backwards-compatibility hack, dated 2002-04-10.
$sortby = "count" if $sortby eq "dup_count";
75

76
# Open today's record of dupes
77 78
my $today = days_ago(0);
my $yesterday = days_ago(1);
79

80 81 82 83 84 85 86 87 88 89 90 91 92 93
# We don't know the exact file name, because the extention depends on the
# underlying dbm library, which could be anything. We can't glob, because
# perl < 5.6 considers if (<*>) { ... } to be tainted
# Instead, just check the return value for today's data and yesterday's,
# and ignore file not found errors

use Errno;
use Fcntl;

if (!tie(%dbmcount, 'AnyDBM_File', "data/duplicates/dupes$today",
         O_RDONLY, 0644)) {
    if ($!{ENOENT}) {
        if (!tie(%dbmcount, 'AnyDBM_File', "data/duplicates/dupes$yesterday",
                 O_RDONLY, 0644)) {
94
            $vars->{'today'} = $today;
95
            if ($!{ENOENT}) {
96
                ThrowUserError("no_dupe_stats");
97
            } else {
98 99
                $vars->{'error_msg'} = $!;
                ThrowUserError("no_dupe_stats_error_yesterday");
100 101 102
            }
        }
    } else {
103 104
        $vars->{'error_msg'} = $!;
        ThrowUserError("no_dupe_stats_error_today");
105
    }
106 107 108 109
}

# Copy hash (so we don't mess up the on-disk file when we remove entries)
%count = %dbmcount;
110 111 112

# Remove all those dupes under the threshold parameter. 
# We do this, before the sorting, for performance reasons.
113
my $threshold = Param("mostfreqthreshold");
114

115 116
while (my ($key, $value) = each %count) {
    delete $count{$key} if ($value < $threshold);
117 118 119
    
    # If there's a buglist, restrict the bugs to that list.
    delete $count{$key} if $sortvisible && (lsearch(\@buglist, $key) == -1);
120 121
}

122
# Try and open the database from "changedsince" days ago
123
my $dobefore = 0;
124
my %delta;
125 126
my $whenever = days_ago($changedsince);    

127 128 129 130
if (!tie(%before, 'AnyDBM_File', "data/duplicates/dupes$whenever",
         O_RDONLY, 0644)) {
    # Ignore file not found errors
    if (!$!{ENOENT}) {
131 132 133 134
        $vars->{'error_msg'} = $!;
        $vars->{'changedsince'} = $changedsince;
        $vars->{'whenever'} = $whenever;    
        ThrowUserError("no_dupe_stats_error_whenever");
135 136
    }
} else {
137 138 139 140 141 142 143 144 145
    # Calculate the deltas
    ($delta{$_} = $count{$_} - $before{$_}) foreach (keys(%count));

    $dobefore = 1;
}

my @bugs;
my @bug_ids; 

146 147 148 149 150
if (scalar(%count)) {
    # 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 $query = "
151 152 153 154 155 156
      SELECT bugs.bug_id, components.name, bug_severity, op_sys,
             target_milestone, short_desc, bug_status, resolution
      FROM bugs, components
      WHERE (bugs.component_id = components.id)
      AND   (bug_status != 'CLOSED') 
      AND   ((bug_status = 'VERIFIED' AND resolution IN ('INVALID', 'WONTFIX'))
157 158 159
             OR (bug_status != 'VERIFIED'))
      AND bugs.bug_id IN (" . join(", ", keys %count) . ")";

160 161
    # Limit to a single product if requested
    $query .= (" AND bugs.product_id = " . $product_id) if $product_id;
162

163
    SendSQL($query);
164 165 166 167 168 169 170

    while (MoreSQLData()) {
        # Note: maximum row count is dealt with in the template.

        my ($id, $component, $bug_severity, $op_sys, $target_milestone, 
            $short_desc, $bug_status, $resolution) = FetchSQLData();

171
        next if (!CanSeeBug($id, $::userid));
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
        # 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); 
    }
187 188 189 190 191 192 193 194 195 196 197 198
}

$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;
199
$vars->{'format'} = $::FORM{'format'};
200 201 202
$vars->{'product'} = $product;
$vars->{'products'} = \@::legal_product;

203

204 205
my $format = 
  GetFormat("reports/duplicates", $::FORM{'format'}, $::FORM{'ctype'});
206
 
207
print "Content-Type: $format->{'ctype'}\n\n";
208 209

# Generate and return the UI (HTML page) from the appropriate template.
210
$template->process($format->{'template'}, $vars)
211
  || ThrowTemplateError($template->error());
212 213 214 215 216


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;
217
}