hold targeter reify experiment
authorBill Erickson <berickxx@gmail.com>
Tue, 7 Jun 2016 22:08:23 +0000 (18:08 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 7 Jun 2016 22:08:23 +0000 (18:08 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/Utils/HoldTargeter.pm
Open-ILS/src/support-scripts/test-scripts/hold_targeter.pl

index 792732d..14d91e8 100644 (file)
@@ -17,10 +17,11 @@ my $dt_parser = DateTime::Format::ISO8601->new;
 #  -- better up-front copy filtering when finding potential copies
 
 sub new {
-    my $class = shift;
+    my ($class, %args) = @_;
     my $self = {
         editor => new_editor(),
-        ou_setting_cache => {}
+        ou_setting_cache => {},
+        %args,
     };
     return bless($self, $class);
 }
@@ -51,36 +52,37 @@ sub get_ou_setting {
     return $c->{$org_id}->{$setting};
 }
 
-# TODO: stream messages back to caller
 sub exit_targeter {
     my ($self, $msg) = @_;
+    $self->{exit} = 1;
+    $self->{exit_msg} = $msg;
     $self->{editor}->rollback; # no-op if commit already occurred.
     my $hold_id = $self->{hold_id};
     $logger->info("targeter: exiting hold targeter for $hold_id : $msg");
     return 0;
 }
 
-# Cancel expired holds and kick off the A/T no-target event.
-# Returns true if the hold is expired or an error occured.
-# False otherwise.
+# Cancel expired holds and kick off the A/T no-target event.  Returns
+# true (i.e. keep going) if the hold is not expired.  Returns false if
+# the hold is canceled or a non-recoverable error occcurred.
 sub handle_expired_hold {
     my $self = shift;
     my $hold = $self->{hold};
 
-    return 0 unless $hold->expire_time;
+    return 1 unless $hold->expire_time;
 
     my $ex_time =
         $dt_parser->parse_datetime(cleanse_ISO8601($hold->expire_time));
-    return 0 unless DateTime->compare($ex_time, DateTime->now) < 0;
+    return 1 unless DateTime->compare($ex_time, DateTime->now) < 0;
 
-    # Hold is expired
+    # Hold is expired --
 
     $hold->cancel_time('now');
     $hold->cancel_cause(1); # == un-targeted expiration
 
     if (!$self->{editor}->update_action_hold_request($hold)) {
-        $self->exit_targeter("Error canceling hold");
-        return 1; # Tell the caller to exit
+        my $evt = $self->{editor}->die_event;
+        return $self->exit_targeter("Error canceling hold: ".$evt->{textcode});
     }
 
     $self->{editor}->commit;
@@ -92,21 +94,27 @@ sub handle_expired_hold {
         $hold, $hold->pickup_lib
     );
 
-    return 1;
+    return $self->exit_targeter("Hold is expired");
 }
 
 # Hold copy maps are only automatically deleted when the hold
 # is fulfilled or canceled.  Here, they have to be manually removed.
-# Returns event on error, undef on success.
+# Returns true on success, false on error.
 sub remove_copy_maps {
     my $self = shift;
     my $e = $self->{editor};
     my $prev_maps = 
         $e->search_action_hold_copy_map({hold => $self->{hold_id}});
+
     for my $map (@$prev_maps) {
-        $e->delete_action_hold_copy_map($map) or return $e->die_event;
+        if (!$e->delete_action_hold_copy_map($map)) {
+            my $evt = $e->die_event;
+            return $self->exit_targeter(
+                "Error deleting copy maps: ".$evt->{textcode});
+        }
     }
-    return undef;
+
+    return 1;
 }
 
 sub get_hold_copies {
@@ -245,10 +253,22 @@ sub get_hold_copies {
     return map {$_->{id}} @$res;
 }
 
-sub handle_no_copies {
-    my $self = shift;
+sub inspect_potential_copies {
+    my ($self, @copy_ids) = @_;
+
     my $e = $self->{editor};
     my $hold = $self->{hold};
+    my $hold_id = $self->{hold_id};
+
+    $logger->info("targeter: Hold $hold_id has ".
+        scalar(@copy_ids)." potential copies");
+    
+    # Let the caller know we found the copy they were interested in.
+    $self->{found_copy} = 1 if $self->{find_copy} 
+        && grep {$_ eq $self->{find_copy}} @copy_ids;
+
+    # We have copies.  Nothing left to do here.
+    return 1 if @copy_ids;
 
     $hold->prev_check_time('now');
     $hold->clear_current_copy;
@@ -264,11 +284,38 @@ sub handle_no_copies {
 }
 
 
+sub build_copy_maps {
+    my ($self, @copy_ids) = @_;
+    my $e = $self->{editor};
+
+    for my $copy_id (@copy_ids) {
+        my $map = Fieldmapper::action::hold_copy_map->new;
+
+        $map->hold($self->{hold_id});
+        $map->target_copy($copy_id);
+
+        if (!$e->create_action_hold_copy_map($map)) {
+            my $evt = $e->die_event;
+            return $self->exit_targeter(
+                "Error creating hold copy map: ".$evt->{textcode});
+        }
+    }
+
+    # TODO: collect proximities
+
+    return 1;
+}
+
+
 # Targets a single hold request
 sub target_hold {
     my ($self, $hold_id) = @_;
     my $e = $self->{editor};
+
+    # reset hold-specific toggles
+    $self->{exit} = 0;
     $self->{hold_id} = $hold_id;
+    $self->{found_copy} = 0;
 
     $e->xact_begin;
 
@@ -279,20 +326,15 @@ sub target_hold {
     return $self->exit_targeter("Hold is not eligible for targeting")
         if $hold->capture_time || $hold->cancel_time;
 
-    # Remove any existing hold copy maps so they can be replaced.
-    my $evt = $self->remove_copy_maps;
-    return $self->exit_targeter(
-        "Error deleting copy maps: ".$evt->{textcode}) if $evt;
-
-    return $self->exit_targeter("Hold is expired")
-        if $self->handle_expired_hold;
+    return unless $self->remove_copy_maps;
+    return unless $self->handle_expired_hold;
 
     my @copy_ids = $self->get_hold_copies;
+    return unless $self->inspect_potential_copies(@copy_ids);
 
-    return $self->handle_no_copies unless @copy_ids;
+    return unless $self->build_copy_maps(@copy_ids);
 
-    $logger->info("targeter: Hold $hold_id has ".
-        scalar(@copy_ids)." potential copies");
+    $e->commit;
 }
 
 
index 54fa863..c1b2acd 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 #----------------------------------------------------------------
-# Simple cstore example
+# Simple hold targeter test script
 #----------------------------------------------------------------
 
 require '../oils_header.pl';