2 # Copyright (C) 2012 Equinox Software, Inc.
3 # Author: Bill Erickson <erickson@esilibrary.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 use strict; use warnings;
18 use MARC::File::XML ( BinaryEncoding => 'UTF-8' );
19 use MARC::File::USMARC;
23 use Getopt::Long qw(:DEFAULT GetOptionsFromArray);
27 use OpenSRF::Utils::Logger qw/$logger/;
28 use OpenSRF::AppSession;
29 use OpenSRF::EX qw/:try/;
30 use OpenSRF::Utils::SettingsClient;
31 use OpenSRF::Utils::Cache;
32 use OpenILS::Utils::Cronscript;
33 use OpenILS::Utils::CStoreEditor;
34 use OpenILS::Utils::Fieldmapper;
35 require 'oils_header.pl';
36 use vars qw/$apputils/;
49 'osrf-config=s' => '/openils/conf/opensrf_core.xml',
53 'poll-interval=i' => 10
56 # -----------------------------------------------------
57 # Command-line args reading / munging
58 # -----------------------------------------------------
59 $OpenILS::Utils::Cronscript::debug=1 if $debug;
60 $Getopt::Long::debug=1 if $debug > 1;
61 my $o = OpenILS::Utils::Cronscript->new(\%defaults);
65 if (grep {$_ eq '--'} @ARGV) {
66 print "Splitting options into groups\n" if $debug;
69 $_ eq '--' and last; # stop at the first --
70 push @script_args, $_;
77 print "Calling MyGetOptions ",
78 (@script_args ? "with options: " . join(' ', @script_args) : 'without options from command line'),
81 my $real_opts = $o->MyGetOptions(\@script_args);
84 my $osrf_config = $real_opts->{'osrf-config'};
85 my $oils_username = $real_opts->{user};
86 my $oils_password = $real_opts->{password};
87 my $help = $real_opts->{help};
88 my $poll_interval = $real_opts->{'poll-interval'};
89 $debug += $real_opts->{debug};
91 foreach (keys %$real_opts) {
92 print("real_opt->{$_} = ", $real_opts->{$_}, "\n") if $real_opts->{debug} or $debug;
97 pod2usage(1) if $help;
98 unless ($oils_password) {
99 print STDERR "\nERROR: password option required for session login\n\n";
102 $debug and print Dumper($o);
105 foreach my $ref (qw/osrf_config oils_username oils_password help debug/) {
107 printf "%16s => %s\n", $ref, (eval("\$$ref") || '');
111 $debug and print Dumper($real_opts);
113 # -----------------------------------------------------
115 # -----------------------------------------------------
119 $authtoken = oils_login($oils_username, $oils_password, 'staff')
120 or die "Unable to login to Evergreen as user $oils_username";
125 sub clear_auth_token {
126 $apputils->simplereq(
128 'open-ils.auth.session.delete',
133 sub push_file_to_acq {
137 $logger->info("acq-or: pushing file '$file' to provider " . $args->{provider});
139 # Cache the file name like Vandelay does. ACQ will
140 # read contents of the cache and then delete them.
141 # The key can be any unique value.
142 my $key = $$ . time . rand();
143 $cache->put_cache("vandelay_import_spool_$key", {path => $file});
145 # some arguments are not optional
146 $args->{create_po} = 1;
148 # don't send our internal args to the service
149 my $local = delete $args->{_local};
151 my $req = $acq_ses->request(
152 'open-ils.acq.process_upload_records',
158 while (my $resp = $req->recv(timeout => 600)) {
159 if(my $content = $resp->content) {
160 $debug and print Dumper($content);
162 warn "Request returned no data: " . Dumper($resp) . "\n";
166 # TODO: delete tmp queue?
172 return $org_cache{$sn} if $org_cache{$sn};
173 my $org = $editor->search_actor_org_unit({shortname => $sn})->[0];
175 warn "No such org unit in acq_order_reader config: '$sn'\n";
178 return $org_cache{$sn} = $org;
181 # translate config info into a request arguments structure
182 sub args_from_provider_conf {
186 my $pcode = $conf->{code};
187 my $orgsn = $conf->{owner};
189 $debug and print "Extracting request args for provider $pcode at $orgsn\n";
191 my $org = org_from_sn($conf->{owner}) or return undef;
193 my $provider = $editor->search_acq_provider({
199 warn "No such provider in acq_order_reader config: '$pcode'\n";
203 my $oa = org_from_sn($conf->{ordering_agency}) or return undef;
205 $args{provider} = $provider->id;
206 $args{ordering_agency} = $oa->id;
207 $args{activate_po} = ($conf->{activate_po} || '') =~ /true/i;
209 # vandelay import options
210 my $vconf = $conf->{vandelay} || {};
211 $args{vandelay} = {};
221 $args{vandelay}->{$opt} = $vconf->{$opt}
231 auto_overlay_best_match/ ) {
233 $args{vandelay}->{$opt} = 1 if ($vconf->{$opt} || '') =~ /true/i;
236 if ($vconf->{queue}) {
237 $args{vandelay}->{queue_name} = $vconf->{queue};
238 $args{vandelay}->{existing_queue} = $vconf->{queue};
242 # create a temporary queue
243 $args{vandelay}->{queue_name} = sprintf("acq-order-reader-%s-%s-%s",
244 $org->shortname, $provider->code, $apputils->epoch2ISO8601(time));
248 provider_code => $pcode, # good for debugging
249 dirname => File::Spec->catfile($base_dir, $conf->{subdir})
255 # returns the list of new order record files that
256 # need to be processed for this vendor
257 sub check_provider_files {
259 my $dirname = $args->{_local}->{dirname};
263 $debug and print "Searching for new files at $dirname\n";
265 if ( !opendir($dh, $dirname) ) {
266 warn "Couldn't open dir '$dirname': $!";
270 @files = readdir $dh;
271 # ignore '.', '..', and hidden files
272 @files = grep {$_ !~ /^\./} @files;
274 $logger->info("acq-or: found " . scalar(@files) . " ACQ order files at $dirname");
276 # return the file names w/ full path
277 return map {File::Spec->catfile($dirname, $_)} @files;
280 # -----------------------------------------------------
282 # -----------------------------------------------------
284 osrf_connect($osrf_config);
286 $conf = OpenSRF::Utils::SettingsClient->new;
287 $cache = OpenSRF::Utils::Cache->new;
288 $editor = OpenILS::Utils::CStoreEditor->new;
289 $acq_ses = OpenSRF::AppSession->create('open-ils.acq');
291 my $user = $editor->search_actor_user({usrname => $oils_username})->[0];
293 warn "Invalid user: $oils_username\n";
298 $base_dir = $conf->config_value(acq_order_reader => 'base_dir');
299 $share_dir = $conf->config_value(acq_order_reader => 'shared_subdir');
300 $providers = $conf->config_value(acq_order_reader => 'provider');
301 $providers = [$providers] unless ref $providers eq 'ARRAY';
303 $debug and print Dumper($providers);
305 # -----------------------------------------------------
307 # for each provider directory, plus the shared directory, check
308 # to see if there are any files pending. For any files found, push
309 # them up to the ACQ service, then delete the file
316 for my $provider_conf (@$providers) {
317 my $args = args_from_provider_conf($provider_conf) or next;
318 my @files = check_provider_files($args);
319 push_file_to_acq($_, $args) for @files;
320 $processed += scalar(@files);
328 $logger->info("acq-or: loop processed $processed files");
331 print "Cleaning up...\n";
332 exit; # allows lockfile cleanup
335 # processing takes time. If we processed any records
336 # during the current iteration, immediately check again
337 # for more work. Otherwise, wait $poll_interval seconds
338 sleep $poll_interval if $processed == 0;
345 acq_order_reader.pl - Collect MARC order record files and pass them onto the ACQ service
349 ./acq_order_reader.pl [script opts ...]
351 This script uses the EG common options from B<Cronscript>. See --help output for those.
353 Run C<perldoc marc_stream_importer.pl> for full documentation.
355 Note: this script has to be run in the same directory as B<oils_header.pl>.
357 Typical server-style execution will include a trailing C<&> to run in the background.
361 The only required option is --password
363 --password =<eg_password>
364 --user =<eg_username> default: admin
365 --nodaemon default: OFF When used with --spoolfile, turns off Net::Server mode and runs this utility in the foreground
368 =head2 Old style: --noqueue and associated options
372 ./acq_order_reader.pl --user admin --password demo123
374 ./acq_order_reader.pl --user admin --password demo123 -poll-interval 3 --debug --nodaemon
378 Bill Erickson <erickson@esilibrary.com>
379 Code liberally copied from marc_stream_importer.pl