refactored copy targeter to use new JS copy tester; adjusted object relationships...
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Sat, 11 Feb 2006 05:20:31 +0000 (05:20 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Sat, 11 Feb 2006 05:20:31 +0000 (05:20 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@3029 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/asset.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/biblio.pm
Open-ILS/src/perlmods/OpenILS/Utils/PermitHold.pm
OpenSRF/src/perlmods/OpenSRF/AppSession.pm

index ec9f234..ad9cdad 100644 (file)
@@ -351,7 +351,7 @@ sub update {
 
        $log->debug("Calling Class::DBI->update on modified object $self", DEBUG);
 
-       debug_object($self);
+       #debug_object($self);
 
        return $self->SUPER::update if ($self->is_changed);
        return 0;
@@ -362,8 +362,8 @@ sub modify_from_fieldmapper {
        my $fm = shift;
        my $orig = $obj;
 
-       debug_object($obj);
-       debug_object($fm);
+       #debug_object($obj);
+       #debug_object($fm);
 
        $log->debug("Modifying object using fieldmapper", DEBUG);
 
@@ -373,7 +373,7 @@ sub modify_from_fieldmapper {
 
        if (!ref($obj)) {
                $obj = $class->retrieve($fm);
-               debug_object($obj);
+               #debug_object($obj);
                unless ($obj) {
                        $log->debug("Retrieve of $class using $fm (".$fm->id.") failed! -- ".shift(), ERROR);
                        throw OpenSRF::EX::WARN ("No $class with id of ".$fm->id."!!");
@@ -394,7 +394,7 @@ sub modify_from_fieldmapper {
        my $au = $obj->autoupdate;
        $obj->autoupdate(0);
        
-       debug_object($obj);
+       #debug_object($obj);
 
        for my $field ( keys %hash ) {
                $obj->$field( $hash{$field} ) if ($obj->$field ne $hash{$field});
@@ -603,10 +603,11 @@ sub modify_from_fieldmapper {
        action::hold_request->has_a(  requestor => 'actor::user' );
        action::hold_request->has_a(  usr => 'actor::user' );
        action::hold_request->has_a(  pickup_lib => 'actor::org_unit' );
+       action::hold_request->has_a(  request_lib => 'actor::org_unit' );
 
        action::hold_request->has_many(  notifications => 'action::hold_notification' );
-       action::hold_request->has_many(  copy_maps => 'action::hold_copy_map' );
+       action::hold_request->has_many(  eligible_copies => [ 'action::hold_copy_map' => 'target_copy' ] );
 
-       asset::copy->has_many(  hold_maps => 'action::hold_copy_map' );
+       asset::copy->has_many(  holds => [ 'action::hold_copy_map' => 'hold' ] );
 
 1;
index 0df3437..4ae2f1c 100644 (file)
@@ -5,6 +5,7 @@ use OpenSRF::Utils qw/:datetime/;
 use OpenSRF::AppSession;
 use OpenSRF::EX qw/:try/;
 use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::PermitHold;
 use DateTime;
 use DateTime::Format::ISO8601;
 
@@ -383,6 +384,231 @@ __PACKAGE__->register_method(
 
 
 
+sub new_hold_copy_targeter {
+       my $self = shift;
+       my $client = shift;
+       my $check_expire = shift;
+       my $one_hold = shift;
+
+       my $time = time;
+       $check_expire ||= '12h';
+       $check_expire = interval_to_seconds( $check_expire );
+
+       my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time() - $check_expire);
+       $year += 1900;
+       $mon += 1;
+       my $expire_threshold = sprintf(
+               '%s-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d-00',
+               $year, $mon, $mday, $hour, $min, $sec
+       );
+
+
+       my $holds;
+
+       try {
+               if ($one_hold) {
+                       $holds = [ action::hold_request->search(id => $one_hold) ];
+               } else {
+                       $holds = [ action::hold_request->search_where(
+                                                       { capture_time => undef,
+                                                         fulfillment_time => undef,
+                                                         prev_check_time => { '<=' => $expire_threshold },
+                                                       },
+                                                       { order_by => 'request_time,prev_check_time' } ) ];
+                       push @$holds, action::hold_request->search(
+                                                       capture_time => undef,
+                                                       fulfillment_time => undef,
+                                                       prev_check_time => undef,
+                                                       { order_by => 'request_time' } );
+               }
+       } catch Error with {
+               my $e = shift;
+               die "Could not retrieve uncaptured hold requests:\n\n$e\n";
+       };
+
+       my @successes;
+       for my $hold (@$holds) {
+               try {
+                       #action::hold_request->db_Main->begin_work;
+                       if ($self->method_lookup('open-ils.storage.transaction.current')->run) {
+                               $log->debug("Cleaning up after previous transaction\n");
+                               $self->method_lookup('open-ils.storage.transaction.rollback')->run;
+                       }
+                       $self->method_lookup('open-ils.storage.transaction.begin')->run( $client );
+                       $log->info("Processing hold ".$hold->id."...\n");
+
+                       action::hold_copy_map->search( { hold => $hold->id } )->delete_all;
+       
+                       my $all_copies = [];
+
+                       # find all the potential copies
+                       if ($hold->hold_type eq 'M') {
+                               for my $r ( map
+                                               {$_->record}
+                                               metabib::record_descriptor
+                                                       ->search(
+                                                               record => [ map { $_->id } metabib::metarecord
+                                                                                       ->retrieve($hold->target)
+                                                                                       ->source_records ],
+                                                               item_type => [split '', $hold->holdable_formats]
+                                                       )
+                               ) {
+                                       my ($rtree) = $self
+                                               ->method_lookup( 'open-ils.storage.biblio.record_entry.ranged_tree')
+                                               ->run( $r->id, $hold->request_lib->id, $hold->selection_depth );
+
+                                       for my $cn ( @{ $rtree->call_numbers } ) {
+                                               push @$all_copies,
+                                                       asset::copy->search( id => [map {$_->id} @{ $cn->copies }] );
+                                       }
+                               }
+                       } elsif ($hold->hold_type eq 'T') {
+                               my ($rtree) = $self
+                                       ->method_lookup( 'open-ils.storage.biblio.record_entry.ranged_tree')
+                                       ->run( $hold->target, $hold->request_lib->id, $hold->selection_depth );
+
+                               unless ($rtree) {
+                                       push @successes, { hold => $hold->id, eligible_copies => 0, error => 'NO_RECORD' };
+                                       die 'OK';
+                               }
+
+                               for my $cn ( @{ $rtree->call_numbers } ) {
+                                       push @$all_copies,
+                                               asset::copy->search( id => [map {$_->id} @{ $cn->copies }] );
+                               }
+                       } elsif ($hold->hold_type eq 'V') {
+                               my ($vtree) = $self
+                                       ->method_lookup( 'open-ils.storage.asset.call_number.ranged_tree')
+                                       ->run( $hold->target, $hold->request_lib->id, $hold->selection_depth );
+
+                               push @$all_copies,
+                                       asset::copy->search( id => [map {$_->id} @{ $vtree->copies }] );
+                                       
+                       } elsif  ($hold->hold_type eq 'C') {
+
+                               $all_copies = [asset::copy->retrieve($hold->target)];
+                       }
+
+                       # let 'em know we're still working
+                       $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+                       
+                       if (!ref $all_copies || !@$all_copies) {
+                               $log->info("\tNo copies available for targeting at all!\n");
+                               $self->method_lookup('open-ils.storage.transaction.commit')->run;
+                               push @successes, { hold => $hold->id, eligible_copies => 0, error => 'NO_COPIES' };
+                               die 'OK';
+                       }
+
+                       my $copies = [];
+                       for my $c ( @$all_copies ) {
+                               push @$copies, $c
+                                       if ( OpenILS::Utils::PermitHold::permit_copy_hold(
+                                               { title => $c->call_number->record->to_fieldmapper,
+                                                 title_descriptor => $c->call_number->record->record_descriptor->next->to_fieldmapper,
+                                                 patron => $hold->usr->to_fieldmapper,
+                                                 copy => $c->to_fieldmapper,
+                                                 requestor => $hold->requestor->to_fieldmapper,
+                                                 request_lib => $hold->request_lib->to_fieldmapper,
+                                               } ));
+                       }
+                       my $copy_count = @$copies;
+                       $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+
+                       # map the potentials, so that we can pick up checkins
+                       $log->debug( "\tMapping ".scalar(@copies)." potential copies for hold ".$hold->id);
+                       action::hold_copy_map->create( { hold => $hold->id, target_copy => $_->id } ) for (@copies);
+
+                       my @good_copies;
+                       for my $c (@$copies) {
+                               next if ($c->id == $hold->current_copy);
+                               push @good_copies, $c if ($c);
+                       }
+
+                       $log->debug("\t".scalar(@good_copies)." (non-current) copies available for targeting...");
+
+                       my $old_best = $hold->current_copy;
+                       $hold->update({ current_copy => undef });
+       
+                       if (!scalar(@good_copies)) {
+                               $log->info("\tNo (non-current) copies eligible to fill the hold.");
+                               if ( $old_best && grep { $old_best == $_ } @$copies ) {
+                                       $log->debug("\tPushing current_copy back onto the targeting list");
+                                       push @good_copies, $old_best;
+                               } else {
+                                       $log->debug("\tcurrent_copy is no longer available for targeting... NEXT HOLD, PLEASE!");
+                                       $self->method_lookup('open-ils.storage.transaction.commit')->run;
+                                       push @successes, { hold => $hold->id, eligible_copies => 0, error => 'NO_TARGETS' };
+                                       die 'OK';
+                               }
+                       }
+
+                       $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+                       my $prox_list = [];
+                       $$prox_list[0] =
+                       [
+                               grep {
+                                       $_->circ_lib == $hold->pickup_lib && 
+                                       ( $_->status == 0 || $_->status == 7 )
+                               } @good_copies
+                       ];
+
+                       $copies = [grep {$_->circ_lib != $hold->pickup_lib } @good_copies];
+
+                       my $best = $self->choose_nearest_copy($hold, $prox_list);
+
+                       if (!$best) {
+                               $prox_list = $self->create_prox_list( $hold->pickup_lib, $copies );
+                               $best = $self->choose_nearest_copy($hold, $prox_list);
+                       }
+
+                       $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+                       if ($old_best) {
+                               # hold wasn't fulfilled, record the fact
+                       
+                               $log->info("\tHold was not (but should have been) fulfilled by ".$old_best->id);
+                               action::unfulfilled_hold_list->create(
+                                               { hold => ''.$hold->id,
+                                                 current_copy => ''.$old_best->id,
+                                                 circ_lib => ''.$old_best->circ_lib,
+                                               });
+                       }
+
+                       if ($best) {
+                               $hold->update( { current_copy => ''.$best->id } );
+                               $log->debug("\tTargeting copy ".$best->id." for hold fulfillment.");
+                       } else {
+                               $log->debug( "\tThere were no targetable copies for the hold" );
+                       }
+
+                       $hold->update( { prev_check_time => 'now' } );
+                       $log->info("\tUpdating hold [".$hold->id."] with new 'current_copy' [".$best->id."] for hold fulfillment.");
+
+                       $self->method_lookup('open-ils.storage.transaction.commit')->run;
+                       $log->info("\tProcessing of hold ".$hold->id." complete.");
+
+                       push @successes,
+                               { hold => $hold->id,
+                                 old_target => ($old_best ? $old_best->id : undef),
+                                 eligible_copies => $copy_count,
+                                 target => ($best ? $best->id : undef) };
+
+               } otherwise {
+                       my $e = shift;
+                       if ($e !~ /^OK/o) {
+                               $log->error("Processing of hold failed:  $e");
+                               $self->method_lookup('open-ils.storage.transaction.rollback')->run;
+                       }
+               };
+       }
+
+       return \@successes;
+}
+__PACKAGE__->register_method(
+       api_name        => 'open-ils.storage.action.hold_request.copy_targeter',
+       api_level       => 1,
+       method          => 'new_hold_copy_targeter',
+);
+
 my $locations;
 my $statuses;
 my %cache = (titles => {}, cns => {});
@@ -536,13 +762,12 @@ sub hold_copy_targeter {
 }
 __PACKAGE__->register_method(
        api_name        => 'open-ils.storage.action.hold_request.copy_targeter',
-       api_level       => 1,
+       api_level       => 0,
        stream          => 1,
        method          => 'hold_copy_targeter',
 );
 
 
-
 sub copy_hold_capture {
        my $self = shift;
        my $hold = shift;
@@ -557,10 +782,10 @@ sub copy_hold_capture {
                };
        }
 
-       my @copies = grep { $_->holdable and !$_->ref } @$cps;
+       my @copies = grep { $_->holdable } @$cps;
 
-       for (my $i = 0; $i < @copies; $i++) {
-               next unless $copies[$i];
+       for (my $i = 0; $i < @$cps; $i++) {
+               next unless $$cps[$i];
                
                my $cn = $cache{cns}{$copies[$i]->call_number};
                my $rec = $cache{titles}{$cn->record};
index 5ae84c7..7a9872b 100644 (file)
@@ -555,5 +555,50 @@ __PACKAGE__->register_method(
 );
 
 
+sub cn_ranged_tree {
+       my $self = shift;
+       my $client = shift;
+       my $cn = shift;
+       my $ou = shift;
+       my $depth = shift || 0;
+
+       my $ou_list =
+               actor::org_unit
+                       ->db_Main
+                       ->selectcol_arrayref(
+                               'SELECT id FROM actor.org_unit_descendants(?,?)',
+                               {},
+                               $ou,
+                               $depth
+                       );
+
+       return undef unless ($ou_list and @$ou_list);
+
+       $cn = asset::call_number->retrieve( $cn );
+       return undef unless ($cn);
+
+       my $call_number = $cn->to_fieldmapper;
+       $call_number->copies([]);
+
+       $call_number->record( $cn->record->to_fieldmapper );
+       $call_number->record->fixed_fields( $cn->record->record_descriptor->next->to_fieldmapper );
+
+       for my $cp ( $cn->copies(circ_lib => $ou_list) ) {
+               my $copy = $cp->to_fieldmapper;
+               $copy->status( $cp->status->to_fieldmapper );
+               $copy->location( $cp->status->to_fieldmapper );
+
+               push @{ $call_number->copies }, $copy;
+       }
+
+       return $call_number;
+}
+__PACKAGE__->register_method(
+       api_name        => 'open-ils.storage.asset.call_number.ranged_tree',
+       method          => 'cn_ranged_tree',
+       argc            => 1,
+       api_level       => 1,
+);
+
 
 1;
index f58b3f3..bd52190 100644 (file)
@@ -58,6 +58,58 @@ __PACKAGE__->register_method(
        cachable        => 1,
 );
 
+sub record_ranged_tree {
+       my $self = shift;
+       my $client = shift;
+       my $r = shift;
+       my $ou = shift;
+       my $depth = shift || 0;
+
+       my $ou_list =
+               actor::org_unit
+                       ->db_Main
+                       ->selectcol_arrayref(
+                               'SELECT id FROM actor.org_unit_descendants(?,?)',
+                               {},
+                               $ou,
+                               $depth
+                       );
+
+       return undef unless ($ou_list and @$ou_list);
+
+       $r = biblio::record_entry->retrieve( $r );
+       return undef unless ($r);
+
+       my $rec = $r->to_fieldmapper;
+       $rec->call_numbers([]);
+
+       $rec->fixed_fields( $r->record_descriptor->next->to_fieldmapper );
+
+       for my $cn ( $r->call_numbers  ) {
+               my $call_number = $cn->to_fieldmapper;
+               $call_number->copies([]);
+
+
+               for my $cp ( $cn->copies(circ_lib => $ou_list) ) {
+                       my $copy = $cp->to_fieldmapper;
+                       $copy->status( $cp->status->to_fieldmapper );
+                       $copy->location( $cp->status->to_fieldmapper );
+
+                       push @{ $call_number->copies }, $copy;
+               }
+
+               push @{ $rec->call_numbers }, $call_number if (@{ $call_number->copies });
+       }
+
+       return $rec;
+}
+__PACKAGE__->register_method(
+       api_name        => 'open-ils.storage.biblio.record_entry.ranged_tree',
+       method          => 'record_ranged_tree',
+       argc            => 1,
+       api_level       => 1,
+);
+
 sub record_by_barcode {
        my $self = shift;
        my $client = shift;
index 4d368cc..a415b53 100644 (file)
@@ -2,6 +2,7 @@ package OpenILS::Utils::PermitHold;
 use strict; use warnings;
 use Data::Dumper;
 use OpenSRF::Utils;
+use OpenSRF::Utils::SettingsClient;
 use OpenILS::Utils::ScriptRunner;
 use OpenILS::Application::AppUtils;
 use OpenSRF::Utils::Logger qw(:logger);
index b694a29..97a6170 100644 (file)
@@ -1015,7 +1015,9 @@ sub respond {
 sub respond_complete { respond(@_); }
 
 sub new {
-       return bless({resp => []}, 'OpenSRF::AppSubrequest');
+       my $class = shift;
+       $class = ref($class) || $class;
+       return bless({resp => [], @_}, $class);
 }
 
 sub responses { @{$_[0]->{resp}} }