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