]> git.donarmstrong.com Git - debbugs.git/blobdiff - Debbugs/Bugs.pm
* Move bugfilter out to Debbugs::Bugs
[debbugs.git] / Debbugs / Bugs.pm
index 2399102d5e496cc2fed72e84a4483eee6b825904..848931f79b8fcca9799324a814630ee2d77666a8 100644 (file)
@@ -46,18 +46,20 @@ BEGIN{
 
      @EXPORT = ();
      %EXPORT_TAGS = ();
-     @EXPORT_OK = (qw(get_bugs count_bugs newest_bug));
+     @EXPORT_OK = (qw(get_bugs count_bugs newest_bug bug_filter));
      $EXPORT_TAGS{all} = [@EXPORT_OK];
 }
 
 use Debbugs::Config qw(:config);
 use Params::Validate qw(validate_with :types);
 use IO::File;
-use Debbugs::Status qw(splitpackages);
+use Debbugs::Status qw(splitpackages get_bug_status);
 use Debbugs::Packages qw(getsrcpkgs);
 use Debbugs::Common qw(getparsedaddrs getmaintainers getmaintainers_reverse make_list);
 use Fcntl qw(O_RDONLY);
 use MLDBM qw(DB_File Storable);
+use List::Util qw(first);
+use Carp;
 
 =head2 get_bugs
 
@@ -104,7 +106,8 @@ searches are performed.
 =over
 
 =item archive -- whether to search archived bugs or normal bugs;
-defaults to false.
+defaults to false. As a special case, if archive is 'both', but
+archived and unarchived bugs are returned.
 
 =item usertags -- set of usertags and the bugs they are applied to
 
@@ -180,7 +183,7 @@ sub get_bugs{
                                          bugs      => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
-                                         archive   => {type => BOOLEAN,
+                                         archive   => {type => BOOLEAN|SCALAR,
                                                        default => 0,
                                                       },
                                          usertags  => {type => HASHREF,
@@ -192,6 +195,13 @@ sub get_bugs{
      # Normalize options
      my %options = %param;
      my @bugs;
+     if ($options{archive} eq 'both') {
+         push @bugs, get_bugs(%options,archive=>0);
+         push @bugs, get_bugs(%options,archive=>1);
+         my %bugs;
+         @bugs{@bugs} = @bugs;
+         return keys %bugs;
+     }
      # A configuration option will set an array that we'll use here instead.
      for my $routine (qw(Debbugs::Bugs::get_bugs_by_idx Debbugs::Bugs::get_bugs_flatfile)) {
          my ($package) = $routine =~ m/^(.+)\:\:/;
@@ -283,6 +293,77 @@ sub newest_bug {
      return $next_number+0;
 }
 
+=head2 bug_filter
+
+     bug_filter
+
+Allows filtering bugs on commonly used criteria
+
+=cut
+
+sub bug_filter {
+     my %param = validate_with(params => \@_,
+                              spec   => {bug => {type  => SCALAR,
+                                                 regex => qr/^\d+$/,
+                                                },
+                                         status => {type => HASHREF,
+                                                    optional => 1,
+                                                   },
+                                         seen_merged => {type => HASHREF,
+                                                         optional => 1,
+                                                        },
+                                         repeat_merged => {type => BOOLEAN,
+                                                           optional => 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{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;
+}
+
 
 =head2 get_bugs_by_idx
 
@@ -318,6 +399,12 @@ sub get_bugs_by_idx{
                                          maint     => {type => SCALAR|ARRAYREF,
                                                        optional => 1,
                                                       },
+                                         bugs      => {type => SCALAR|ARRAYREF,
+                                                       optional => 1,
+                                                      },
+                                         usertags  => {type => HASHREF,
+                                                       optional => 1,
+                                                      },
                                         },
                              );
      my %bugs = ();
@@ -328,6 +415,13 @@ sub get_bugs_by_idx{
      my @packages = __handle_pkg_src_and_maint(map {exists $param{$_}?($_,$param{$_}):()}
                                               qw(package src maint)
                                              );
+     my %usertag_bugs;
+     if (exists $param{tag} and exists $param{usertags}) {
+         # This complex slice makes a hash with the bugs which have the
+          # usertags passed in $param{tag} set.
+         @usertag_bugs{make_list(@{$param{usertags}}{make_list($param{tag})})
+                       } = (1) x make_list(@{$param{usertags}}{make_list($param{tag})});
+     }
      if (exists $param{package} or
         exists $param{src} or
         exists $param{maint}) {
@@ -338,21 +432,37 @@ sub get_bugs_by_idx{
      die "Need at least 1 key to search by" unless $keys;
      my $arc = $param{archive} ? '-arc':'';
      my %idx;
-     for my $key (grep {$_ ne 'archive'} keys %param) {
+     for my $key (grep {$_ !~ /^(archive|usertags|bugs)$/} keys %param) {
          my $index = $key;
          $index = 'submitter-email' if $key eq 'submitter';
          $index = "$config{spool_dir}/by-${index}${arc}.idx";
          tie(%idx, MLDBM => $index, O_RDONLY)
               or die "Unable to open $index: $!";
+         my %bug_matching = ();
          for my $search (make_list($param{$key})) {
               next unless defined $idx{$search};
               for my $bug (keys %{$idx{$search}}) {
+                   next if $bug_matching{$bug};
                    # increment the number of searches that this bug matched
                    $bugs{$bug}++;
+                   $bug_matching{$bug}=1;
+              }
+         }
+         if ($key eq 'tag' and exists $param{usertags}) {
+              for my $bug (make_list(grep {defined $_ } @{$param{usertags}}{make_list($param{tag})})) {
+                   next if $bug_matching{$bug};
+                   $bugs{$bug}++;
+                   $bug_matching{$bug}=1;
               }
          }
          untie %idx or die 'Unable to untie %idx';
      }
+     if ($param{bugs}) {
+         $keys++;
+         for my $bug (make_list($param{bugs})) {
+              $bugs{$bug}++;
+         }
+     }
      # Throw out results that do not match all of the search specifications
      return map {$keys <= $bugs{$_}?($_):()} keys %bugs;
 }
@@ -417,12 +527,10 @@ sub get_bugs_flatfile{
      }
      my %usertag_bugs;
      if (exists $param{tag} and exists $param{usertags}) {
-
          # This complex slice makes a hash with the bugs which have the
           # usertags passed in $param{tag} set.
-         @usertag_bugs{map {@{$_}}
-                            @{$param{usertags}}{make_list($param{tag})}
-                       } = (1) x @{$param{usertags}}{make_list($param{tag})}
+         @usertag_bugs{make_list(@{$param{usertags}}{make_list($param{tag})})
+                       } = (1) x make_list(@{$param{usertags}}{make_list($param{tag})});
      }
      # We handle src packages, maint and maintenc by mapping to the
      # appropriate binary packages, then removing all packages which
@@ -495,6 +603,18 @@ sub get_bugs_flatfile{
      return @bugs;
 }
 
+=head1 PRIVATE FUNCTIONS
+
+=head2 __handle_pkg_src_and_maint
+
+     my @packages = __handle_pkg_src_and_maint(map {exists $param{$_}?($_,$param{$_}):()}
+                                              qw(package src maint)
+                                             );
+
+Turn package/src/maint into a list of packages
+
+=cut
+
 sub __handle_pkg_src_and_maint{
      my %param = validate_with(params => \@_,
                               spec   => {package   => {type => SCALAR|ARRAYREF,
@@ -538,6 +658,51 @@ sub __handle_pkg_src_and_maint{
      return grep {$packages{$_} >= $package_keys} keys %packages;
 }
 
+my %field_match = (
+    'subject' => \&__contains_field_match,
+    'tags' => sub {
+        my ($field, $values, $status) = @_; 
+       my %values = map {$_=>1} @$values;
+       foreach my $t (split /\s+/, $status->{$field}) {
+            return 1 if (defined $values{$t});
+        }
+        return 0;
+    },
+    'severity' => \&__exact_field_match,
+    'pending' => \&__exact_field_match,
+    'originator' => \&__contains_field_match,
+    'forwarded' => \&__contains_field_match,
+    'owner' => \&__contains_field_match,
+);
+
+sub __bug_matches {
+    my ($hash, $status) = @_;
+    foreach my $key( keys( %$hash ) ) {
+        my $value = $hash->{$key};
+       my $sub = $field_match{$key};
+       return 1 if ($sub->($key, $value, $status));
+    }
+    return 0;
+}
+
+sub __exact_field_match {
+    my ($field, $values, $status) = @_; 
+    my @values = @$values;
+    my @ret = grep {$_ eq $status->{$field} } @values;
+    $#ret != -1;
+}
+
+sub __contains_field_match {
+    my ($field, $values, $status) = @_; 
+    foreach my $data (@$values) {
+        return 1 if (index($status->{$field}, $data) > -1);
+    }
+    return 0;
+}
+
+
+
+
 
 1;