has 'check_open_state' =>
(is => 'ro', isa => CodeRef, default => sub { return \&is_open_state; },);
+has 'very_old_days' => (is => 'ro', isa => Int, default => 45);
+
has 'events' => (
is => 'lazy',
isa => ArrayRef [
Dict [
date => $DateTime,
bugs_by_team => HashRef [
- Dict [open => ArrayRef [Int], closed => ArrayRef [Int], median_age_open => Num]
+ Dict [open => ArrayRef [Int], closed => ArrayRef [Int], very_old_bugs => ArrayRef [Int]]
],
bugs_by_sec_keyword => HashRef [
- Dict [open => ArrayRef [Int], closed => ArrayRef [Int], median_age_open => Num]
+ Dict [open => ArrayRef [Int], closed => ArrayRef [Int], very_old_bugs => ArrayRef [Int]]
],
],
],
{
id => 'bugs_by_sec_keyword_age',
title => sprintf(
- 'Median age of open security bugs by severity (%s to %s)',
+ '# of open security bugs older than 45 days by severity (%s to %s)',
$self->start_date->ymd,
$self->end_date->ymd
),
- range_label => 'Median Age (days)',
+ range_label => 'Bug Count',
datasets => [
map {
my $keyword = $_;
name => $_,
keys => [map { $_->{date}->epoch } @{$self->results}],
values => [
- map { $_->{bugs_by_sec_keyword}->{$keyword}->{median_age_open} }
- @{$self->results}
+ map {
+ scalar @{$_->{bugs_by_sec_keyword}->{$keyword}->{very_old_bugs}}
+ } @{$self->results}
],
}
} @{$self->sec_keywords}
{
id => 'bugs_by_team_age',
title => sprintf(
- 'Median age of open security bugs by team (%s to %s)',
+ '# of open security bugs older than 45 days by team (%s to %s)',
$self->start_date->ymd,
$self->end_date->ymd
),
- range_label => 'Median Age (days)',
+ range_label => 'Bug Count',
datasets => [
map {
my $team = $_;
{
name => $_,
keys => [map { $_->{date}->epoch } @{$self->results}],
- values =>
- [map { $_->{bugs_by_team}->{$team}->{median_age_open} } @{$self->results}],
+ values => [
+ map {
+ scalar @{$_->{bugs_by_team}->{$team}->{very_old_bugs}}
+ }@{$self->results}
+ ],
}
} keys %{$self->teams}
],
foreach my $team (keys %{$self->teams}) {
my @open = map { $_->{id} } grep { ($_->{is_open}) } @{$groups->{$team}};
my @closed = map { $_->{id} } grep { !($_->{is_open}) } @{$groups->{$team}};
- my @ages = map {
- $_->{created_at}->subtract_datetime_absolute($report_date)->seconds / 86_400;
+ my @very_old_bugs = map { $_->{id} } grep {
+ $_->{created_at}->subtract_datetime_absolute($report_date)->seconds / 86_400 >= $self->very_old_days;
} grep { ($_->{is_open}) } @{$groups->{$team}};
$result->{$team} = {
open => \@open,
closed => \@closed,
- median_age_open => @ages ? _median(@ages) : 0,
+ very_old_bugs => \@very_old_bugs,
};
}
my @open = map { $_->{id} } grep { ($_->{is_open}) } @{$groups->{$sec_keyword}};
my @closed
= map { $_->{id} } grep { !($_->{is_open}) } @{$groups->{$sec_keyword}};
- my @ages = map {
- $_->{created_at}->subtract_datetime_absolute($report_date)->seconds / 86_400
+ my @very_old_bugs = map { $_->{id} } grep {
+ $_->{created_at}->subtract_datetime_absolute($report_date)->seconds / 86_400 >= $self->very_old_days;
} grep { ($_->{is_open}) } @{$groups->{$sec_keyword}};
$result->{$sec_keyword} = {
open => \@open,
closed => \@closed,
- median_age_open => @ages ? _median(@ages) : 0,
+ very_old_bugs => \@very_old_bugs,
};
}
return undef;
}
-sub _median {
-
- # From tlm @ https://www.perlmonks.org/?node_id=474564. Jul 14, 2005
- return sum((sort { $a <=> $b } @_)[int($#_ / 2), ceil($#_ / 2)]) / 2;
-}
-
1;
start_date => $start_date,
end_date => $end_date,
teams => $teams,
- sec_keywords => $sec_keywords
+ sec_keywords => $sec_keywords,
+ very_old_days => 45
);
my $bugs_by_team = $report->results->[-1]->{bugs_by_team};
deltas => $report->deltas,
missing_products => $report->missing_products,
missing_components => $report->missing_components,
+ very_old_days => $report->very_old_days,
build_bugs_link => \&build_bugs_link,
};
teams => decode_json($teams_json),
sec_keywords => ['sec-critical', 'sec-high'],
check_open_state => \&check_open_state_mock,
+ very_old_days => 45,
initial_bug_ids => [1, 2, 3, 4],
initial_bugs => {
1 => {
# Rewind the event that caused 1 to close.
open => [1],
closed => [],
- median_age_open => 8
+ very_old_bugs => []
},
'Backend' => {
# 2 wasn't a sec-critical bug on the report date.
open => [3],
closed => [],
- median_age_open => 4
+ very_old_bugs => []
}
},
bugs_by_sec_keyword => {
# 2 wasn't a sec-critical bug and 4 wasn't created yet on the report date.
open => [],
closed => [],
- median_age_open => 0
+ very_old_bugs => []
},
'sec-high' => {
# Rewind the event that caused 1 to close.
open => [1, 3],
closed => [],
- median_age_open => 6
+ very_old_bugs => []
}
},
},
{ # The report on 2000-01-16 matches the state of initial_bugs.
date => DateTime->new(year => 2000, month => 1, day => 16),
bugs_by_team => {
- 'Frontend' => {open => [4], closed => [1], median_age_open => 6},
- 'Backend' => {open => [3], closed => [2], median_age_open => 11}
+ 'Frontend' => {open => [4], closed => [1], very_old_bugs => []},
+ 'Backend' => {open => [3], closed => [2], very_old_bugs => []}
},
bugs_by_sec_keyword => {
- 'sec-critical' => {open => [4], closed => [2], median_age_open => 6},
- 'sec-high' => {open => [3], closed => [1], median_age_open => 11}
+ 'sec-critical' => {open => [4], closed => [2], very_old_bugs => []},
+ 'sec-high' => {open => [3], closed => [1], very_old_bugs => []}
},
},
];
<th style="padding: 0px 15px 10px 0px; text-align: right;">Closed<br />Last Week</th>
<th style="padding: 0px 15px 10px 0px; text-align: right;">Added<br />Last Week</th>
<th style="padding: 0px 15px 10px 0px; text-align: right; border-right: 1px solid grey;">
- Median Age<br/>of Open [% terms.Bugs %]<br>
+ Older than <br> [% very_old_days FILTER html %] Days<br>
</th>
[% FOREACH result IN results.reverse %]
[% NEXT IF loop.count < 2 %]
<tr>
<td style="padding: 0px 15px 10px 0px;">[% keyword FILTER html %]</td>
<td style="padding: 0px 15px 10px 0px; text-align: right;">
- [% IF results.reverse.0.bugs_by_sec_keyword.$keyword.open %]
+ [% IF results.reverse.0.bugs_by_sec_keyword.$keyword.open.size %]
<a style="text-decoration: none;" href="[% build_bugs_link(results.reverse.0.bugs_by_sec_keyword.$keyword.open) FILTER html %]">
[% results.reverse.0.bugs_by_sec_keyword.$keyword.open.size FILTER html %]
</a>
[% END %]
</td>
<td style="padding: 0px 15px 10px 0px; text-align: right; border-right: 1px solid grey; ">
- [% results.-1.bugs_by_sec_keyword.$keyword.median_age_open FILTER format("%.0f") FILTER html %] days
+ [% IF results.reverse.0.bugs_by_sec_keyword.$keyword.very_old_bugs.size %]
+ <a style="text-decoration: none;" href="[% build_bugs_link(results.reverse.0.bugs_by_sec_keyword.$keyword.very_old_bugs) FILTER html %]">
+ [% results.reverse.0.bugs_by_sec_keyword.$keyword.very_old_bugs.size FILTER html %]
+ </a>
+ [% ELSE %]
+ 0
+ [% END %]
</td>
[% FOREACH result IN results.reverse %]
[% NEXT IF loop.count < 2 %]