RSS

(root)/bugzilla/trunk : /reports.cgi (revision 8182)

To get this branch, use:
bzr branch /bugzilla/trunk
Line Revision Contents
1 2107
#!/usr/bin/perl -wT
2 8075
# This Source Code Form is subject to the terms of the Mozilla Public
3
# License, v. 2.0. If a copy of the MPL was not distributed with this
4
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
#
6
# This Source Code Form is "Incompatible With Secondary Licenses", as
7
# defined by the Mozilla Public License, v. 2.0.
8 77
9
use strict;
10 739
11 5304
use lib qw(. lib);
12 1243
13 4296
use Bugzilla;
14
use Bugzilla::Constants;
15
use Bugzilla::Util;
16
use Bugzilla::Error;
17 5285
use Bugzilla::Status;
18 77
19 7589
use File::Basename;
20
use Digest::MD5 qw(md5_hex);
21
22 6778
# If we're using bug groups for products, we should apply those restrictions
23
# to viewing reports, as well.  Time to check the login in that case.
24
my $user = Bugzilla->login();
25 7589
my $cgi = Bugzilla->cgi;
26
my $template = Bugzilla->template;
27
my $vars = {};
28 6778
29
if (!Bugzilla->feature('old_charts')) {
30
    ThrowCodeError('feature_disabled', { feature => 'old_charts' });
31
}
32 1819
33 4419
my $dir       = bz_locations()->{'datadir'} . "/mining";
34 7589
my $graph_dir = bz_locations()->{'graphsdir'};
35
my $graph_url = basename($graph_dir);
36
my $product_name = $cgi->param('product') || '';
37 739
38 2054
Bugzilla->switch_to_shadow_db();
39 2026
40 7589
if (!$product_name) {
41 4419
    # Can we do bug charts?
42
    (-d $dir && -d $graph_dir) 
43
      || ThrowCodeError('chart_dir_nonexistent',
44
                        {dir => $dir, graph_dir => $graph_dir});
45
46 5018
    my %default_sel = map { $_ => 1 } BUG_STATE_OPEN;
47 4419
48
    my @datasets;
49
    my @data = get_data($dir);
50
51
    foreach my $dataset (@data) {
52
        my $datasets = {};
53
        $datasets->{'value'} = $dataset;
54
        $datasets->{'selected'} = $default_sel{$dataset} ? 1 : 0;
55
        push(@datasets, $datasets);
56
    }
57
58 7589
    # We only want those products that the user has permissions for.
59
    my @myproducts = ('-All-');
60
    # Extract product names from objects and add them to the list.
61
    push( @myproducts, map { $_->name } @{$user->get_selectable_products} );
62
63 4419
    $vars->{'datasets'} = \@datasets;
64
    $vars->{'products'} = \@myproducts;
65
66
    print $cgi->header();
67
}
68
else {
69 885
    # For security and correctness, validate the value of the "product" form variable.
70
    # Valid values are those products for which the user has permissions which appear
71
    # in the "product" drop-down menu on the report generation form.
72 7589
    my ($product) = grep { $_->name eq $product_name } @{$user->get_selectable_products};
73
    ($product || $product_name eq '-All-')
74
      || ThrowUserError('invalid_product_name', {product => $product_name});
75
76
    # Product names can change over time. Their ID cannot; so use the ID
77
    # to generate the filename.
78
    my $prod_id = $product ? $product->id : 0;
79
80
    # Make sure there is something to plot.
81
    my @datasets = $cgi->param('datasets');
82
    scalar(@datasets) || ThrowUserError('missing_datasets');
83
84
    if (grep { $_ !~ /^[A-Za-z0-9:_-]+$/ } @datasets) {
85
        ThrowUserError('invalid_datasets', {'datasets' => \@datasets});
86
    }
87
88
    # Filenames must not be guessable as they can point to products
89
    # you are not allowed to see. Also, different projects can have
90
    # the same product names.
91
    my $key = Bugzilla->localconfig->{'site_wide_secret'};
92
    my $project = bz_locations()->{'project'} || '';
93
    my $image_file =  join(':', ($key, $project, $prod_id, @datasets));
94
    # Wide characters cause md5_hex() to die.
95
    if (Bugzilla->params->{'utf8'}) {
96
        utf8::encode($image_file) if utf8::is_utf8($image_file);
97
    }
98
    $image_file = md5_hex($image_file) . '.png';
99
    trick_taint($image_file);
100 739
101 744
    if (! -e "$graph_dir/$image_file") {
102 7589
        generate_chart($dir, "$graph_dir/$image_file", $product, \@datasets);
103 137
    }
104 4419
105 7589
    $vars->{'url_image'} = "$graph_url/$image_file";
106 4419
107
    print $cgi->header(-Content_Disposition=>'inline; filename=bugzilla_report.html');
108
}
109
110 7589
$template->process('reports/old-charts.html.tmpl', $vars)
111
  || ThrowTemplateError($template->error());
112
113 4419
#####################
114
#    Subroutines    #
115
#####################
116
117
sub get_data {
118
    my $dir = shift;
119
120
    my @datasets;
121 7589
    open(DATA, '<', "$dir/-All-")
122
      || ThrowCodeError('chart_file_open_fail', {filename => "$dir/-All-"});
123 4419
124
    while (<DATA>) {
125
        if (/^# fields?: (.+)\s*$/) {
126
            @datasets = grep ! /date/i, (split /\|/, $1);
127
            last;
128
        }
129
    }
130
    close(DATA);
131
    return @datasets;
132
}
133
134 739
sub generate_chart {
135 7589
    my ($dir, $image_file, $product, $datasets) = @_;
136
    $product = $product ? $product->name : '-All-';
137
    my $data_file = $product;
138
    $data_file =~ s/\//-/gs;
139
    $data_file = $dir . '/' . $data_file;
140 4419
141 739
    if (! open FILE, $data_file) {
142 3657
        if ($product eq '-All-') {
143
            $product = '';
144
        }
145 4419
        ThrowCodeError('chart_data_not_generated', {'product' => $product});
146 739
    }
147
148
    my @fields;
149
    my @labels = qw(DATE);
150 7589
    my %datasets = map { $_ => 1 } @$datasets;
151 739
152
    my %data = ();
153
    while (<FILE>) {
154
        chomp;
155
        next unless $_;
156
        if (/^#/) {
157
            if (/^# fields?: (.*)\s*$/) {
158 759
                @fields = split /\||\r/, $1;
159 6351
                $data{$_} ||= [] foreach @fields;
160 3762
                unless ($fields[0] =~ /date/i) {
161 4419
                    ThrowCodeError('chart_datafile_corrupt', {'file' => $data_file});
162 3762
                }
163 739
                push @labels, grep($datasets{$_}, @fields);
164
            }
165
            next;
166
        }
167
168 3762
        unless (@fields) {
169 4419
            ThrowCodeError('chart_datafile_corrupt', {'file' => $data_file});
170 3762
        }
171 4419
172 739
        my @line = split /\|/;
173
        my $date = $line[0];
174
        my ($yy, $mm, $dd) = $date =~ /^\d{2}(\d{2})(\d{2})(\d{2})$/;
175
        push @{$data{DATE}}, "$mm/$dd/$yy";
176
        
177
        for my $i (1 .. $#fields) {
178
            my $field = $fields[$i];
179
            if (! defined $line[$i] or $line[$i] eq '') {
180
                # no data point given, don't plot (this will probably
181
                # generate loads of Chart::Base warnings, but that's not
182 744
                # our fault.)
183 739
                push @{$data{$field}}, undef;
184
            }
185
            else {
186
                push @{$data{$field}}, $line[$i];
187
            }
188
        }
189
    }
190
    
191
    shift @labels;
192
193
    close FILE;
194
195
    if (! @{$data{DATE}}) {
196 4419
        ThrowUserError('insufficient_data_points');
197 739
    }
198 4419
199 739
    my $img = Chart::Lines->new (800, 600);
200
    my $i = 0;
201
202
    my $MAXTICKS = 20;      # Try not to show any more x ticks than this.
203
    my $skip = 1;
204
    if (@{$data{DATE}} > $MAXTICKS) {
205
        $skip = int((@{$data{DATE}} + $MAXTICKS - 1) / $MAXTICKS);
206
    }
207
208
    my %settings =
209
        (
210 2533
         "title" => "Status Counts for $product",
211 739
         "x_label" => "Dates",
212
         "y_label" => "Bug Counts",
213
         "legend_labels" => \@labels,
214
         "skip_x_ticks" => $skip,
215
         "y_grid_lines" => "true",
216
         "grey_background" => "false",
217
         "colors" => {
218
                      # default dataset colours are too alike
219
                      dataset4 => [0, 0, 0], # black
220
                     },
221
        );
222
    
223
    $img->set (%settings);
224 7529
    $img->png($image_file, [ @data{('DATE', @labels)} ]);
225 739
}

Loggerhead 1.18.1 is a web-based interface for Bazaar branches