]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/offline/offline-execute.pl
allowing clients to create their own session names
[Evergreen.git] / Open-ILS / src / offline / offline-execute.pl
1 #!/usr/bin/perl
2 use strict; use warnings;
3 use Time::HiRes;
4 use OpenSRF::Transport::PeerHandle;
5 use OpenSRF::System;
6 use OpenSRF::EX qw/:try/;
7
8 our $U;
9 our $logger;
10
11 require 'offline-lib.pl';
12
13 $SIG{CHLD} = 'IGNORE'; # - we don't care what happens to our child process
14
15 &execute();
16
17
18 # --------------------------------------------------------------------
19 # Loads the offline script files for a given org, sorts and runs the 
20 # scripts, and returns the exception list
21 # --------------------------------------------------------------------
22 sub execute {
23
24         # --------------------------------------------------------------------
25         # Make sure the caller has the right permissions
26         # --------------------------------------------------------------------
27         my $evt = $U->check_perms(&offline_requestor->id, &offline_org, 'OFFLINE_EXECUTE');
28         handle_event($evt) if $evt;
29         
30         
31         # --------------------------------------------------------------------
32         # First make sure the data is there and in a good state
33         # --------------------------------------------------------------------
34         my $data = &sort_data( &collect_data );
35         
36         
37         # --------------------------------------------------------------------
38         # Note that we must disconnect from opensrf before forking or the 
39         # connection will be borked...
40         # --------------------------------------------------------------------
41         my $con = OpenSRF::Transport::PeerHandle->retrieve;
42         $con->disconnect if $con;
43
44
45         if( safe_fork() ) {
46
47                 # --------------------------------------------------------------------
48                 # Tell the client all is well
49                 # --------------------------------------------------------------------
50                 handle_event(OpenILS::Event->new('SUCCESS')); # - this exits
51
52         } else {
53
54                 # --------------------------------------------------------------------
55                 # close stdout/stderr or apache will wait on the child to finish
56                 # --------------------------------------------------------------------
57                 close(STDOUT);
58                 close(STDERR);
59
60                 $logger->debug("offline: child $$ processing data...");
61
62                 # --------------------------------------------------------------------
63                 # The child re-connects to the opensrf network and processes
64                 # the script requests 
65                 # --------------------------------------------------------------------
66                 my %config = &offline_config;
67                 OpenSRF::System->bootstrap_client(config_file => $config{bootstrap});
68         
69                 try {
70                         &process_data( $data );
71                         &archive_files;
72                 } catch Error with {
73                         my $e = shift;
74                         $logger->error("offline: child process error $e");
75                 }
76         }
77 }
78
79
80
81 # --------------------------------------------------------------------
82 # Collects all of the script logs into an in-memory structure that
83 # can be sorted, etc.
84 # Returns a blob like -> { $ws1 => [ commands... ], $ws2 => [ commands... ] }
85 # --------------------------------------------------------------------
86 sub collect_data {
87
88         my $dir = &offline_pending_dir;
89         my $lock = &offline_lock_file;
90
91         handle_event(OpenILS::Event->new('OFFLINE_SESSION_NOT_FOUND')) unless  -e $dir;
92         handle_event(OpenILS::Event->new('OFFLINE_PARAM_ERROR')) unless &offline_org;
93         handle_event(OpenILS::Event->new('OFFLINE_SESSION_ACTIVE')) if ( -e $lock );
94
95         # - create the lock file
96         qx/touch $lock/;
97
98         my $file;
99         my %data;
100
101         # Load the data from the list of files
102         while( ($file = <$dir/*.log>) ) {
103                 $logger->debug("offline: Loading script file $file");
104                 open(F, $file) or handle_event(
105                         OpenILS::Event->new('OFFLINE_FILE_ERROR'));
106                 my $ws = log_to_wsname($file);
107                 $data{$ws} = [];
108                 push(@{$data{$ws}}, , <F>);
109         }
110
111         return \%data;
112 }
113
114
115 # --------------------------------------------------------------------
116 # Sorts the commands
117 # --------------------------------------------------------------------
118 sub sort_data {
119         my $data = shift;
120         my @parsed;
121
122         $logger->debug("offline: Sorting data");
123         my $meta = &offline_read_meta;
124         
125         # cycle through the workstations
126         for my $ws (keys %$data) {
127
128                 # find the meta line for this ws.
129                 my ($m) = grep { $_->{workstation} eq $ws } @$meta;
130                 
131                 $logger->debug("offline: Sorting workstations $ws with a time delta of ".$m->{delta});
132
133                 my @scripts = @{$$data{$ws}};
134
135                 # cycle through the scripts for the current workstation
136                 for my $s (@scripts) {
137                         my $command = JSON->JSON2perl($s);
138                         $command->{_workstation} = $ws;
139                         $command->{_realtime} = $command->{timestamp} + $m->{delta};
140                         $logger->debug("offline: setting realtime to ".
141                                 $command->{_realtime} . " from timestamp " . $command->{timestamp});
142                         push( @parsed, $command );
143
144                 }
145         }
146
147         @parsed = sort { $a->{_realtime} <=> $b->{_realtime} } @parsed;
148         return \@parsed;
149 }
150
151
152 # --------------------------------------------------------------------
153 # Runs the commands and returns the list of errors
154 # --------------------------------------------------------------------
155 sub process_data {
156         my $data = shift;
157
158         for my $d (@$data) {
159
160                 my $t = $d->{type};
161                 next unless $t;
162
163                 append_result( {command => $d, event => handle_checkin($d)})    if $t eq 'checkin';
164                 append_result( {command => $d, event => handle_inhouse($d)})    if $t eq 'in_house_use';
165                 append_result( {command => $d, event => handle_checkout($d)})   if $t eq 'checkout';
166                 append_result( {command => $d, event => handle_renew($d)})              if $t eq 'renew';
167                 append_result( {command => $d, event => handle_register($d)})   if $t eq 'register';
168
169         }
170 }
171
172
173
174 # --------------------------------------------------------------------
175 # Runs an in_house_use action
176 # --------------------------------------------------------------------
177 sub handle_inhouse {
178
179         my $command             = shift;
180         my $realtime    = $command->{_realtime};
181         my $ws                  = $command->{_workstation};
182         my $barcode             = $command->{barcode};
183         my $count               = $command->{count} || 1;
184         my $use_time    = $command->{use_time} || "";
185
186         $logger->activity("offline: in_house_use : requestor=". &offline_requestor->id.", realtime=$realtime, ".  
187                 "workstation=$ws, barcode=$barcode, count=$count, use_time=$use_time");
188
189         my $ids = $U->simplereq(
190                 'open-ils.circ', 
191                 'open-ils.circ.in_house_use.create', &offline_authtoken, 
192                 { barcode => $barcode, count => $count, location => &offline_org, use_time => $use_time } );
193         
194         return OpenILS::Event->new('SUCCESS', payload => $ids) if( ref($ids) eq 'ARRAY' );
195         return $ids;
196 }
197
198
199
200 # --------------------------------------------------------------------
201 # Pulls the relevant circ args from the command, fetches data where 
202 # necessary
203 # --------------------------------------------------------------------
204 sub circ_args_from_command {
205         my $command = shift;
206
207         my $type                        = $command->{type};
208         my $realtime    = $command->{_realtime};
209         my $ws                  = $command->{_workstation};
210         my $barcode             = $command->{barcode} || "";
211         my $cotime              = $command->{checkout_time} || "";
212         my $pbc                 = $command->{patron_barcode};
213         my $due_date    = $command->{due_date} || "";
214         my $noncat              = ($command->{noncat}) ? "yes" : "no"; # for logging
215
216         $logger->activity("offline: $type : requestor=". &offline_requestor->id.
217                 ", realtime=$realtime, workstation=$ws, checkout_time=$cotime, ".
218                 "patron=$pbc, due_date=$due_date, noncat=$noncat");
219
220         my $args = { 
221                 permit_override => 1, 
222                 barcode                         => $barcode,            
223                 checkout_time           => $cotime, 
224                 patron_barcode          => $pbc,
225                 due_date                                => $due_date };
226
227         if( $command->{noncat} ) {
228                 $args->{noncat} = 1;
229                 $args->{noncat_type} = $command->{noncat_type};
230                 $args->{noncat_count} = $command->{noncat_count};
231         }
232
233         return $args;
234 }
235
236
237
238 # --------------------------------------------------------------------
239 # Performs a checkout action
240 # --------------------------------------------------------------------
241 sub handle_checkout {
242         my $command     = shift;
243         my $args = circ_args_from_command($command);
244         return $U->simplereq(
245                 'open-ils.circ', 'open-ils.circ.checkout', &offline_authtoken, $args );
246 }
247
248
249 # --------------------------------------------------------------------
250 # Performs the renewal action
251 # --------------------------------------------------------------------
252 sub handle_renew {
253         my $command = shift;
254         my $args = circ_args_from_command($command);
255         my $t = time;
256         return $U->simplereq(
257                 'open-ils.circ', 'open-ils.circ.renew', &offline_authtoken, $args );
258 }
259
260
261 # --------------------------------------------------------------------
262 # Runs a checkin action
263 # --------------------------------------------------------------------
264 sub handle_checkin {
265
266         my $command             = shift;
267         my $realtime    = $command->{_realtime};
268         my $ws                  = $command->{_workstation};
269         my $barcode             = $command->{barcode};
270         my $backdate    = $command->{backdate} || "";
271
272         $logger->activity("offline: checkin : requestor=". &offline_requestor()->id.
273                 ", realtime=$realtime, ".  "workstation=$ws, barcode=$barcode, backdate=$backdate");
274
275         return $U->simplereq(
276                 'open-ils.circ', 
277                 'open-ils.circ.checkin', &offline_authtoken,
278                 { barcode => $barcode, backdate => $backdate } );
279 }
280
281
282
283 # --------------------------------------------------------------------
284 # Registers a new patron
285 # --------------------------------------------------------------------
286 sub handle_register {
287         my $command = shift;
288
289         my $barcode = $command->{user}->{card}->{barcode};
290         delete $command->{user}->{card}; 
291
292         $logger->info("offline: creating new user with barcode $barcode");
293
294         # now, create the user
295         my $actor       = Fieldmapper::actor::user->new;
296         my $card                = Fieldmapper::actor::card->new;
297
298
299         # username defaults to the barcode
300         $actor->usrname( ($actor->usrname) ? $actor->usrname : $barcode );
301
302         # Set up all of the virtual IDs, isnew, etc.
303         $actor->isnew(1);
304         $actor->id(-1);
305         $actor->card(-1);
306         $actor->cards([$card]);
307
308         $card->isnew(1);
309         $card->id(-1);
310         $card->usr(-1);
311         $card->barcode($barcode);
312
313         my $billing_address;
314         my $mailing_address;
315
316         my @sresp;
317         for my $resp (@{$command->{user}->{survey_responses}}) {
318                 my $sr = Fieldmapper::action::survey_response->new;
319                 $sr->$_( $resp->{$_} ) for keys %$resp;
320                 $sr->isnew(1);
321                 $sr->usr(-1);
322                 push(@sresp, $sr);
323                 $logger->debug("offline: created new survey response for survey ".$sr->survey);
324         }
325         delete $command->{user}->{survey_responses};
326         $actor->survey_responses(\@sresp) if @sresp;
327
328         # extract the billing address
329         if( my $addr = $command->{user}->{billing_address} ) {
330                 $billing_address = Fieldmapper::actor::user_address->new;
331                 $billing_address->$_($addr->{$_}) for keys %$addr;
332                 $billing_address->isnew(1);
333                 $billing_address->id(-1);
334                 $billing_address->usr(-1);
335                 delete $command->{user}->{billing_address};
336                 $logger->debug("offline: read billing address ".$billing_address->street1);
337         }
338
339         # extract the mailing address
340         if( my $addr = $command->{user}->{mailing_address} ) {
341                 $mailing_address = Fieldmapper::actor::user_address->new;
342                 $mailing_address->$_($addr->{$_}) for keys %$addr;
343                 $mailing_address->isnew(1);
344                 $mailing_address->id(-2);
345                 $mailing_address->usr(-1);
346                 delete $command->{user}->{mailing_address};
347                 $logger->debug("offline: read mailing address ".$mailing_address->street1);
348         }
349
350         # make sure we have values for both
351         $billing_address ||= $mailing_address;
352         $mailing_address ||= $billing_address;
353
354         $actor->billing_address($billing_address->id);
355         $actor->mailing_address($mailing_address->id);
356         $actor->addresses([$mailing_address]);
357
358         push( @{$actor->addresses}, $billing_address ) 
359                 unless $billing_address->id eq $mailing_address->id;
360         
361         # pull all of the rest of the data from the command blob
362         $actor->$_( $command->{user}->{$_} ) for keys %{$command->{user}};
363
364         $logger->debug("offline: creating user object...");
365         $actor = $U->simplereq(
366                 'open-ils.actor', 
367                 'open-ils.actor.patron.update', &offline_authtoken, $actor);
368
369         return $actor if(ref($actor) eq 'HASH'); # an event occurred
370
371         return OpenILS::Event->new('SUCCESS', payload => $actor);
372 }
373
374
375
376
377 # --------------------------------------------------------------------
378 # Removes the log file and Moves the script files from the pending 
379 # directory to the archive dir
380 # --------------------------------------------------------------------
381 sub archive_files {
382         my $archivedir = &offline_archive_dir(1);
383         my $pendingdir = &offline_pending_dir;
384
385         my @files = <$pendingdir/*.log>;
386         push(@files, &offline_meta_file);
387         push(@files, &offline_result_file);
388
389         $logger->debug("offline: Archiving files [@files] to $archivedir...");
390
391         my $lock = &offline_lock_file;
392         qx/rm $lock/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
393
394         return unless (<$pendingdir/*>);
395
396         for my $f (@files) {
397                 qx/mv $f $archivedir/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
398         }
399
400         qx/rmdir $pendingdir/ and handle_event(OpenILS::Event->new('OFFLINE_FILE_ERROR'), 1);
401
402         (my $parentdir = $pendingdir) =~ s#^(/.*)/\w+/?$#$1#og; # - grab the parent dir
403         qx/rmdir $parentdir/ unless <$parentdir/*>; # - remove the parent if empty
404 }
405
406
407