From 3ca291c2c43d3237c5b591fe302e62c22449e97c Mon Sep 17 00:00:00 2001
From: Dan Wells <dbw2@calvin.edu>
Date: Fri, 13 Apr 2012 09:53:32 -0400
Subject: [PATCH] Better Prediction for Incomplete Chronologies

While relatively rare, some journals record only the year on the
cover, even if they come out monthly or weekly.  This commit
expands our previous support for serials with no chronology at
all to cover serials with partial chronology.

Signed-off-by: Dan Wells <dbw2@calvin.edu>
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
---
 .../src/perlmods/lib/OpenILS/Application/Serial.pm | 53 ++++++++++++++--------
 1 file changed, 34 insertions(+), 19 deletions(-)

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
index 4c19954a69..8333f3b3d3 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm
@@ -886,10 +886,13 @@ sub make_predictions {
     foreach my $scap (@$scaps) {
         my $caption_field = _revive_caption($scap);
         $caption_field->update('8' => $link_id);
-        my $using_fake_chron = 0;
-        # if we have no chronology, add one for prediction puposes
-        if (!$caption_field->subfield('i') and !$caption_field->enumeration_is_chronology) {
-            $using_fake_chron = 1;
+        my $fake_chron_needed = 0;
+        # if we have missing chron pieces, we will add them later for prediction purposes
+        if (!$caption_field->enumeration_is_chronology) {
+            if (!$caption_field->subfield('i') # no year
+                or !$caption_field->subfield('j')) { # we had a year, but no month or season
+                $fake_chron_needed = '1';
+            }
         }
         $mfhd->append_fields($caption_field);
         my $options = {
@@ -899,9 +902,9 @@ sub make_predictions {
                 'end_date' => defined $args->{end_date} ?
                     $_strp_date->parse_datetime($args->{end_date}) : undef
                 };
+        my $predict_from_siss;
         if ($args->{base_issuance}) { # predict from a given issuance
-            $options->{predict_from} = _revive_holding($args->{base_issuance}->holding_code, $caption_field, 1); # fresh MFHD Record, so we simply default to 1 for seqno
-            $options->{faked_chron_date} = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($args->{base_issuance}->date_published)) if $using_fake_chron;
+            $predict_from_siss = $args->{base_issuance}->holding_code;
         } else { # default to predicting from last published
             my $last_published = $editor->search_serial_issuance([
                     {'caption_and_pattern' => $scap->id,
@@ -909,8 +912,8 @@ sub make_predictions {
                 {limit => 1, order_by => { siss => "date_published DESC" }}]
                 );
             if ($last_published->[0]) {
-                my $last_siss = $last_published->[0];
-                unless ($last_siss->holding_code) {
+                $predict_from_siss = $last_published->[0];
+                unless ($predict_from_siss->holding_code) {
                     $editor->disconnect;
                     # XXX TODO new event type
                     return new OpenILS::Event(
@@ -918,8 +921,6 @@ sub make_predictions {
                             "Last issuance has no holding code. Can't predict."
                     );
                 }
-                $options->{predict_from} = _revive_holding($last_siss->holding_code, $caption_field, 1);
-                $options->{faked_chron_date} = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($last_siss->date_published)) if $using_fake_chron;
             } else {
                 $editor->disconnect;
                 # XXX TODO make a new event type instead of hijacking this one
@@ -928,6 +929,10 @@ sub make_predictions {
                 );
             }
         }
+        $options->{predict_from} = _revive_holding($predict_from_siss->holding_code, $caption_field, 1); # fresh MFHD Record, so we simply default to 1 for seqno
+        if ($fake_chron_needed) {
+            $options->{faked_chron_date} = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($predict_from_siss->date_published));
+        }
         push( @predictions, _generate_issuance_values($mfhd, $options) );
         $link_id++;
     }
@@ -991,8 +996,8 @@ sub _generate_issuance_values {
     my $scap_id = $options->{scap_id};
     my $num_to_predict = $options->{num_to_predict};
     my $end_date = $options->{end_date};
-    my $predict_from = $options->{predict_from};   # issuance to predict from
-    my $faked_chron_date = $options->{faked_chron_date};   # serial does not have a chronology caption, so add one (temporarily) based on this date 
+    my $predict_from = $options->{predict_from};   # MFHD::Holding to predict from
+    my $faked_chron_date = $options->{faked_chron_date};   # serial does not have a (complete) chronology caption, so add one (temporarily) based on this date 
 
 
 # Only needed for 'real' MFHD records, not our temp records
@@ -1015,14 +1020,24 @@ sub _generate_issuance_values {
     $predict_from->notes('private', ['AUTOGEN']);
 
     # our basic method for dealing with 'faked' chronologies will be to add it in, do the predicting, then take it back out
-    my $orig_caption;
-    my $faked_caption;
+    my @faked_subfield_chars;
     if ($faked_chron_date) {
-        $orig_caption = $predict_from->caption;
+        my $faked_caption = new MARC::Field($caption->tag, $caption->indicator(1), $caption->indicator(2), $caption->subfields_list);
+
+        my %mfhd_chron_labels = ('i' => 'year', 'j' => 'month', 'k' => 'day');
+        foreach my $subfield_char ('i', 'j', 'k') {
+            if (!$caption->subfield($subfield_char)) { # if we are missing a piece, add it
+                push(@faked_subfield_chars, $subfield_char);
+                my $chron_name = $mfhd_chron_labels{$subfield_char};
+                $faked_caption->add_subfields($subfield_char => "($chron_name)");
+                my $method = $mfhd_chron_labels{$subfield_char};
+                $predict_from->add_subfields($subfield_char => $faked_chron_date->$chron_name);
+            }
+        }
         # because of the way MFHD::Caption and Holding work, it is simplest
         # to recreate rather than try to update
-        $faked_caption = new MFHD::Caption(new MARC::Field($orig_caption->tag, $orig_caption->indicator(1), $orig_caption->indicator(2), $orig_caption->subfields_list, 'i' => '(year)', 'j' => '(month)', 'k' => '(day)'));
-        $predict_from = new MFHD::Holding($predict_from->seqno, new MARC::Field($predict_from->tag, $predict_from->indicator(1), $predict_from->indicator(2), $predict_from->subfields_list, 'i' => $faked_chron_date->year, 'j' => $faked_chron_date->month, 'k' => $faked_chron_date->day), $faked_caption);
+        $faked_caption = new MFHD::Caption($faked_caption);
+        $predict_from = new MFHD::Holding($predict_from->seqno, new MARC::Field($predict_from->tag, $predict_from->indicator(1), $predict_from->indicator(2), $predict_from->subfields_list), $faked_caption);
     }
 
     my @predictions = $mfhd->generate_predictions({'base_holding' => $predict_from, 'num_to_predict' => $num_to_predict, 'end_date' => $end_date});
@@ -1032,8 +1047,8 @@ sub _generate_issuance_values {
     foreach my $prediction (@predictions) {
         $pub_date = $_strp_date->parse_datetime($prediction->chron_to_date);
         if ($faked_chron_date) { # get rid of the chronology portions and restore original caption
-            $prediction->delete_subfield(code => ['i', 'j', 'k']);
-            $prediction = new MFHD::Holding($prediction->seqno, new MARC::Field($prediction->tag, $prediction->indicator(1), $prediction->indicator(2), $prediction->subfields_list), $orig_caption);
+            $prediction->delete_subfield(code => \@faked_subfield_chars);
+            $prediction = new MFHD::Holding($prediction->seqno, new MARC::Field($prediction->tag, $prediction->indicator(1), $prediction->indicator(2), $prediction->subfields_list), $caption);
         }
         push(
                 @issuance_values,
-- 
2.11.0