]> git.donarmstrong.com Git - debbugs.git/blobdiff - Debbugs/Status.pm
get_bug_status can now accept a schema to pull from DB
[debbugs.git] / Debbugs / Status.pm
index 6fad22e5f2d6a04359546f41041a44161fd4378c..7af7fcf1dfd057dd5a5fa3311c8a0f0216bb6c4c 100644 (file)
@@ -34,7 +34,7 @@ use warnings;
 use strict;
 
 use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
-use base qw(Exporter);
+use Exporter qw(import);
 
 use Params::Validate qw(validate_with :types);
 use Debbugs::Common qw(:util :lock :quit :misc);
@@ -49,7 +49,8 @@ use File::Copy qw(copy);
 use Encode qw(decode encode is_utf8);
 
 use Storable qw(dclone);
-use List::Util qw(min max);
+use List::AllUtils qw(min max uniq);
+use DateTime::Format::Pg;
 
 use Carp qw(croak);
 
@@ -70,6 +71,7 @@ BEGIN{
                                  qw(removefoundversions removefixedversions)
                                 ],
                     hook     => [qw(bughook bughook_archive)],
+                     indexdb  => [qw(generate_index_db_line)],
                     fields   => [qw(%fields)],
                    );
      @EXPORT_OK = ();
@@ -190,6 +192,7 @@ sub read_bug{
     my $status;
     my $log;
     my $location;
+    my $report;
     if (not defined $param{summary}) {
         my $lref;
         ($lref,$location) = @param{qw(bug location)};
@@ -199,14 +202,18 @@ sub read_bug{
         }
         $status = getbugcomponent($lref, 'summary', $location);
         $log    = getbugcomponent($lref, 'log'    , $location);
+        $report    = getbugcomponent($lref, 'report'    , $location);
         return undef unless defined $status;
         return undef if not -e $status;
     }
     else {
         $status = $param{summary};
         $log = $status;
+        $report = $status;
         $log =~ s/\.summary$/.log/;
+        $report =~ s/\.summary$/.report/;
         ($location) = $status =~ m/(db-h|db|archive)/;
+         ($param{bug}) = $status =~ m/(\d+)\.summary$/;
     }
     if ($param{lock}) {
        filelock("$config{spool_dir}/lock/$param{bug}",exists $param{locks}?$param{locks}:());
@@ -268,18 +275,34 @@ sub read_bug{
         # create the found/fixed hashes which indicate when a
         # particular version was marked found or marked fixed.
         @{$data{$field}}{@{$data{"${field}_versions"}}} =
-             (('') x (@{$data{"${field}_date"}} - @{$data{"${field}_versions"}}),
+             (('') x (@{$data{"${field}_versions"}} - @{$data{"${field}_date"}}),
               @{$data{"${field}_date"}});
     }
 
     my $status_modified = (stat($status))[9];
     # Add log last modified time
-    $data{log_modified} = (stat($log))[9];
+    $data{log_modified} = (stat($log))[9] // (stat("${log}.gz"))[9];
+    my $report_modified = (stat($report))[9] // $data{log_modified};
     $data{last_modified} = max($status_modified,$data{log_modified});
+    # if the date isn't set (ancient bug), use the smallest of any of the modified
+    if (not defined $data{date} or not length($data{date})) {
+        $data{date} = min($report_modified,$status_modified,$data{log_modified});
+    }
     $data{location} = $location;
     $data{archived} = (defined($location) and ($location eq 'archive'))?1:0;
     $data{bug_num} = $param{bug};
 
+    # mergedwith occasionally is sorted badly. Fix it to always be sorted by <=>
+    # and not include this bug
+    if (defined $data{mergedwith} and
+       $data{mergedwith}) {
+       $data{mergedwith} =
+           join(' ',
+                grep { $_ != $data{bug_num}}
+                sort { $a <=> $b }
+                split / /, $data{mergedwith}
+               );
+    }
     return \%data;
 }
 
@@ -301,20 +324,42 @@ our $ditch_empty = sub{
     return grep {length $_} map {split $splitter} @t;
 };
 
-my $ditch_empty_space = sub {return &{$ditch_empty}(' ',@_)};
+our $sort_and_unique = sub {
+    my @v;
+    my %u;
+    my $all_numeric = 1;
+    for my $v (@_) {
+        if ($all_numeric and $v =~ /\D/) {
+            $all_numeric = 0;
+        }
+        next if exists $u{$v};
+        $u{$v} = 1;
+        push @v, $v;
+    }
+    if ($all_numeric) {
+        return sort {$a <=> $b} @v;
+    } else {
+        return sort @v;
+    }
+};
+
+my $ditch_space_unique_and_sort = sub {return &{$sort_and_unique}(&{$ditch_empty}(' ',@_))};
 my %split_fields =
     (package        => \&splitpackages,
      affects        => \&splitpackages,
-     blocks         => $ditch_empty_space,
-     blockedby      => $ditch_empty_space,
+     # Ideally we won't have to split source, but because some consumers of
+     # get_bug_status cannot handle arrayref, we will split it here.
+     source         => \&splitpackages,
+     blocks         => $ditch_space_unique_and_sort,
+     blockedby      => $ditch_space_unique_and_sort,
      # this isn't strictly correct, but we'll split both of them for
      # the time being until we ditch all use of keywords everywhere
      # from the code
-     keywords       => $ditch_empty_space,
-     tags           => $ditch_empty_space,
-     found_versions => $ditch_empty_space,
-     fixed_versions => $ditch_empty_space,
-     mergedwith     => $ditch_empty_space,
+     keywords       => $ditch_space_unique_and_sort,
+     tags           => $ditch_space_unique_and_sort,
+     found_versions => $ditch_space_unique_and_sort,
+     fixed_versions => $ditch_space_unique_and_sort,
+     mergedwith     => $ditch_space_unique_and_sort,
     );
 
 sub split_status_fields {
@@ -414,7 +459,6 @@ data.
 =cut
 
 sub lockreadbugmerge {
-     my ($bug_num,$location) = @_;
      my $data = lockreadbug(@_);
      if (not defined $data) {
          return (0,undef);
@@ -510,12 +554,17 @@ sub lock_read_all_merged_bugs {
            push @data,$newdata;
            # perform a sanity check to make sure that the merged bugs
            # are all merged with eachother
-           my $expectmerge= join(' ',grep {$_ != $bug } sort { $a <=> $b } @bugs);
+        # We do a cmp sort instead of an <=> sort here, because that's
+        # what merge does
+           my $expectmerge=
+               join(' ',grep {$_ != $bug }
+                    sort { $a <=> $b }
+                    @bugs);
            if ($newdata->{mergedwith} ne $expectmerge) {
                for (1..$locks) {
                    unfilelock(exists $param{locks}?$param{locks}:());
                }
-               die "Bug $param{bug} differs from bug $bug: ($newdata->{bug_num}: '$newdata->{mergedwith}') vs. ('$expectmerge') (".join(' ',@bugs).")";
+               die "Bug $param{bug} mergedwith differs from bug $bug: ($newdata->{bug_num}: '$newdata->{mergedwith}') vs. ('$expectmerge') (".join(' ',@bugs).")";
            }
        }
     }
@@ -728,9 +777,9 @@ sub addfoundversions {
     my $version = shift;
     my $isbinary = shift;
     return unless defined $version;
-    undef $package if $package =~ m[(?:\s|/)];
+    undef $package if defined $package and $package =~ m[(?:\s|/)];
     my $source = $package;
-    if ($package =~ s/^src://) {
+    if (defined $package and $package =~ s/^src://) {
        $isbinary = 0;
        $source = $package;
     }
@@ -1200,6 +1249,9 @@ sub get_bug_status {
                                          indicatesource => {type => BOOLEAN,
                                                             default => 1,
                                                            },
+                                         schema => {type => OBJECT,
+                                                     optional => 1,
+                                                   },
                                         },
                              );
      my %status;
@@ -1212,7 +1264,64 @@ sub get_bug_status {
          return \%status;
      }
      if (defined $param{status}) {
-         %status = %{$param{status}};
+        %status = %{$param{status}};
+     }
+     elsif (defined $param{schema}) {
+        my $b = $param{schema}->resultset('Bug')->
+            search_rs({'me.id' => $param{bug}},
+                     {prefetch => [{'bug_tags'=>'tag'},
+                                   'severity',
+                                  {'bug_binpackages'=> 'bin_pkg'},
+                                  {'bug_srcpackages'=> 'src_pkg'},
+                                  {'bug_user_tags'=>{'user_tag'=>'correspondent'}},
+                                  {owner => 'correspondent_full_names'},
+                                  {submitter => 'correspondent_full_names'},
+                                    'bug_merged_bugs',
+                                    'bug_mergeds_merged',
+                                    'bug_blocks_blocks',
+                                    'bug_blocks_bugs',
+                                   {'bug_vers' => ['src_pkg','src_ver']},
+                                  ],
+                      '+columns' => [qw(subject log_modified creation last_modified)],
+                      collapse => 1,
+                      result_class => 'DBIx::Class::ResultClass::HashRefInflator',
+                     })->first();
+        $status{keywords} =
+            join(' ',map {$_->{tag}{tag}} @{$b->{bug_tags}});
+        $status{tags} = $status{keywords};
+        $status{subject} = $b->{subject};
+        $status{bug_num} = $b->{id};
+        $status{severity} = $b->{severity}{severity};
+        $status{package} =
+            join(' ',
+                 (map {$_->{bin_pkg}{pkg}} @{$b->{bug_binpackages}//[]}),
+                 (map {$_->{src_pkg}{pkg}} @{$b->{bug_srcpackages}//[]}));
+         $status{originator} = $b->{submitter_full};
+        $status{log_modified} =
+            DateTime::Format::Pg->parse_datetime($b->{log_modified})->epoch;
+        $status{date} =
+            DateTime::Format::Pg->parse_datetime($b->{creation})->epoch;
+        $status{last_modified} =
+            DateTime::Format::Pg->parse_datetime($b->{last_modified})->epoch;
+         $status{blocks} =
+             join(' ',
+                  uniq(sort(map {$_->{block}}
+                            @{$b->{bug_blocks_block}},
+                           )));
+         $status{blockedby} =
+             join(' ',
+                  uniq(sort(map {$_->{bug}}
+                            @{$b->{bug_blocks_bug}},
+                           )));
+         $status{mergedwith} =
+             join(' ',uniq(sort(map {$_->{bug},$_->{merged}}
+                                @{$b->{bug_merged_bugs}},
+                                @{$b->{bug_mergeds_merged}},
+                               )));
+         $status{fixed_versions} =
+             [map {$_->{found}?():$_->{ver_string}} @{$b->{bug_vers}}];
+         $status{found_versions} =
+             [map {$_->{found}?$_->{ver_string}:()} @{$b->{bug_vers}}];
      }
      else {
          my $location = getbuglocation($param{bug}, 'summary');
@@ -1599,6 +1708,39 @@ sub isstrongseverity {
     return grep { $_ eq $severity } @{$config{strong_severities}};
 }
 
+=head1 indexdb
+
+=head2 generate_index_db_line
+
+       my $data = read_bug(bug => $bug,
+                           location => $initialdir);
+        # generate_index_db_line hasn't been written yet at all.
+        my $line = generate_index_db_line($data);
+
+Returns a line for a bug suitable to be written out to index.db.
+
+=cut
+
+sub generate_index_db_line {
+    my ($data,$bug) = @_;
+
+    # just in case someone has given us a split out data
+    $data = join_status_fields($data);
+
+    my $whendone = "open";
+    my $severity = $config{default_severity};
+    (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g;
+    $pkglist =~ s/^,+//;
+    $pkglist =~ s/,+$//;
+    $whendone = "forwarded" if defined $data->{forwarded} and length $data->{forwarded};
+    $whendone = "done" if defined $data->{done} and length $data->{done};
+    $severity = $data->{severity} if length $data->{severity};
+    return sprintf "%s %d %d %s [%s] %s %s\n",
+        $pkglist, $data->{bug_num}//$bug, $data->{date}, $whendone,
+            $data->{originator}, $severity, $data->{keywords};
+}
+
+
 
 =head1 PRIVATE FUNCTIONS
 
@@ -1681,19 +1823,7 @@ sub bughook {
             my $data = $bugs_temp{$bug};
             appendfile("$config{spool_dir}/debbugs.trace","$type $bug\n",makestatus($data, 1));
 
-            my $whendone = "open";
-            my $severity = $config{default_severity};
-            (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g;
-            $pkglist =~ s/^,+//;
-            $pkglist =~ s/,+$//;
-            $whendone = "forwarded" if defined $data->{forwarded} and length $data->{forwarded};
-            $whendone = "done" if defined $data->{done} and length $data->{done};
-            $severity = $data->{severity} if length $data->{severity};
-
-            my $k = sprintf "%s %d %d %s [%s] %s %s\n",
-                 $pkglist, $bug, $data->{date}, $whendone,
-                      $data->{originator}, $severity, $data->{keywords};
-            $bugs{$bug} = $k;
+            $bugs{$bug} = generate_index_db_line($data,$bug);
        }
        update_realtime("$config{spool_dir}/index.db.realtime", %bugs);