+=head2 count_bugs
+
+ count_bugs(function => sub {...})
+
+Uses a subroutine to classify bugs into categories and return the
+number of bugs which fall into those categories
+
+=cut
+
+sub count_bugs {
+ my %param = validate_with(params => \@_,
+ spec => {function => {type => CODEREF,
+ },
+ archive => {type => BOOLEAN,
+ default => 0,
+ },
+ },
+ );
+ my $flatfile;
+ if ($param{archive}) {
+ $flatfile = IO::File->new("$config{spool_dir}/index.archive", 'r')
+ or die "Unable to open $config{spool_dir}/index.archive for reading: $!";
+ }
+ else {
+ $flatfile = IO::File->new("$config{spool_dir}/index.db", 'r')
+ or die "Unable to open $config{spool_dir}/index.db for reading: $!";
+ }
+ my %count = ();
+ while(<$flatfile>) {
+ if (m/^(\S+)\s+(\d+)\s+(\d+)\s+(\S+)\s+\[\s*([^]]*)\s*\]\s+(\w+)\s+(.*)$/) {
+ my @x = $param{function}->(pkg => $1,
+ bug => $2,
+ status => $4,
+ submitter => $5,
+ severity => $6,
+ tags => $7,
+ );
+ local $_;
+ $count{$_}++ foreach @x;
+ }
+ }
+ close $flatfile;
+ return %count;
+}
+
+=head2 newest_bug
+
+ my $bug = newest_bug();
+
+Returns the bug number of the newest bug, which is nextnumber-1.
+
+=cut
+
+sub newest_bug {
+ my $nn_fh = IO::File->new("$config{spool_dir}/nextnumber",'r')
+ or die "Unable to open $config{spool_dir}nextnumber for reading: $!";
+ local $/;
+ my $next_number = <$nn_fh>;
+ close $nn_fh;
+ chomp $next_number;
+ return $next_number-1;
+}
+
+=head2 bug_filter
+
+ bug_filter
+
+Allows filtering bugs on commonly used criteria
+
+
+
+=cut
+
+sub bug_filter {
+ my %param = validate_with(params => \@_,
+ spec => {bug => {type => ARRAYREF|SCALAR,
+ optional => 1,
+ },
+ status => {type => HASHREF|ARRAYREF,
+ optional => 1,
+ },
+ seen_merged => {type => HASHREF,
+ optional => 1,
+ },
+ repeat_merged => {type => BOOLEAN,
+ default => 1,
+ },
+ include => {type => HASHREF,
+ optional => 1,
+ },
+ exclude => {type => HASHREF,
+ optional => 1,
+ },
+ min_days => {type => SCALAR,
+ optional => 1,
+ },
+ max_days => {type => SCALAR,
+ optional => 1,
+ },
+ },
+ );
+ if (exists $param{repeat_merged} and
+ not $param{repeat_merged} and
+ not defined $param{seen_merged}) {
+ croak "repeat_merged false requires seen_merged to be passed";
+ }
+ if (not exists $param{bug} and not exists $param{status}) {
+ croak "one of bug or status must be passed";
+ }
+
+ if (not exists $param{status}) {
+ my $location = getbuglocation($param{bug}, 'summary');
+ return 0 if not defined $location or not length $location;
+ $param{status} = readbug( $param{bug}, $location );
+ return 0 if not defined $param{status};
+ }
+
+ if (exists $param{include}) {
+ return 1 if (!__bug_matches($param{include}, $param{status}));
+ }
+ if (exists $param{exclude}) {
+ return 1 if (__bug_matches($param{exclude}, $param{status}));
+ }
+ if (exists $param{repeat_merged} and not $param{repeat_merged}) {
+ my @merged = sort {$a<=>$b} $param{bug}, split(/ /, $param{status}{mergedwith});
+ return 1 if first {defined $_} @{$param{seen_merged}}{@merged};
+ @{$param{seen_merged}}{@merged} = (1) x @merged;
+ }
+ my $daysold = int((time - $param{status}{date}) / 86400); # seconds to days
+ if (exists $param{min_days}) {
+ return 1 unless $param{min_days} <= $daysold;
+ }
+ if (exists $param{max_days}) {
+ return 1 unless $param{max_days} == -1 or
+ $param{max_days} >= $daysold;
+ }
+ return 0;
+}
+
+