acq order reader; getting started
authorBill Erickson <berick@esilibrary.com>
Mon, 23 Jul 2012 14:44:41 +0000 (10:44 -0400)
committerBill Erickson <berick@esilibrary.com>
Mon, 23 Jul 2012 14:44:41 +0000 (10:44 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/examples/opensrf.xml.example
Open-ILS/src/support-scripts/acq_order_reader.pl [new file with mode: 0755]

index ea6f18b..f1a4dab 100644 (file)
@@ -271,6 +271,30 @@ vim:et:ts=4:sw=4:
 
         </added_content>
 
+        <acq_order_reader>
+            <!-- root directory for all FTP'd ACQ order record files -->
+            <base_dir>/openils/var/data/acq_orders/</base_dir>
+
+            <!-- any files found in the shared subdir must be inspected
+                 (e.g. file name prefix) to determine the provider. -->
+            <shared_subdir>ALL</shared_subdir>
+
+            <!-- providers that don't provide a mechanism to inspect the file
+                 have to push their files to provider-specific locations -->
+            <provider>
+                <code>BAB</code>     <!-- acq.provider.code -->
+                <owner>CONS</owner>  <!-- actor.org_unit.shortname -->
+                <subdir>BAB</subdir> <!-- base_dir + subdir = full path -->
+            </provider>
+
+            <provider>
+                <code>ABC</code>
+                <owner>BR1</owner>
+                <subdir>ABC</subdir>
+            </provider>
+        </acq_order_reader>
+
+
         <!-- no apps are enabled globally by default -->
         <activeapps/> 
 
diff --git a/Open-ILS/src/support-scripts/acq_order_reader.pl b/Open-ILS/src/support-scripts/acq_order_reader.pl
new file mode 100755 (executable)
index 0000000..70f93d3
--- /dev/null
@@ -0,0 +1,259 @@
+#!/usr/bin/perl
+# Copyright (C) 2012 Equinox Software, Inc.
+# Author: Bill Erickson <erickson@esilibrary.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+use strict; use warnings;
+use MARC::Record;
+use MARC::Batch;
+use MARC::File::XML ( BinaryEncoding => 'UTF-8' );
+use MARC::File::USMARC;
+
+use Data::Dumper;
+use File::Temp;
+use Getopt::Long qw(:DEFAULT GetOptionsFromArray);
+use Pod::Usage;
+
+use OpenSRF::Utils::Logger qw/$logger/;
+use OpenSRF::AppSession;
+use OpenSRF::EX qw/:try/;
+use OpenSRF::Utils::SettingsClient;
+use OpenSRF::Utils::Cache;
+use OpenILS::Utils::Cronscript;
+use OpenILS::Utils::CStoreEditor;
+require 'oils_header.pl';
+use vars qw/$apputils/;
+
+my $acq_ses;
+my $authtoken;
+
+my $debug = 0;
+
+my %defaults = (
+    'osrf-config=s' => '/openils/conf/opensrf_core.xml',
+    'user=s'        => 'admin',
+    'password=s'    => '',
+    'nodaemon'      => 0,
+    'poll-interval=i' => 10
+);
+
+$OpenILS::Utils::Cronscript::debug=1 if $debug;
+$Getopt::Long::debug=1 if $debug > 1;
+my $o = OpenILS::Utils::Cronscript->new(\%defaults);
+
+my @script_args = ();
+
+if (grep {$_ eq '--'} @ARGV) {
+    print "Splitting options into groups\n" if $debug;
+    while (@ARGV) {
+        $_ = shift @ARGV;
+        $_ eq '--' and last;    # stop at the first --
+        push @script_args, $_;
+    }
+} else {
+    @script_args = @ARGV;
+    @ARGV = ();
+}
+
+print "Calling MyGetOptions ",
+    (@script_args ? "with options: " . join(' ', @script_args) : 'without options from command line'),
+    "\n" if $debug;
+
+my $real_opts = $o->MyGetOptions(\@script_args);
+$o->bootstrap;
+
+my $osrf_config   = $real_opts->{'osrf-config'};
+my $oils_username = $real_opts->{user};
+my $oils_password = $real_opts->{password};
+my $help          = $real_opts->{help};
+my $poll_interval = $real_opts->{'poll-interval'};
+   $debug         += $real_opts->{debug};
+
+foreach (keys %$real_opts) {
+    print("real_opt->{$_} = ", $real_opts->{$_}, "\n") if $real_opts->{debug} or $debug;
+}
+
+# FEEDBACK
+
+pod2usage(1) if $help;
+unless ($oils_password) {
+    print STDERR "\nERROR: password option required for session login\n\n";
+}
+
+print Dumper($o) if $debug;
+
+if ($debug) {
+    foreach my $ref (qw/osrf_config oils_username oils_password help debug/) {
+        no strict 'refs';
+        printf "%16s => %s\n", $ref, (eval("\$$ref") || '');
+    }
+}
+
+print Dumper($real_opts);
+
+# log in
+sub new_auth_token {
+    $authtoken = oils_login($oils_username, $oils_password, 'staff') 
+        or die "Unable to login to Evergreen as user $oils_username";
+    return $authtoken;
+}
+
+# log out
+sub clear_auth_token {
+    $apputils->simplereq(
+        'open-ils.auth',
+        'open-ils.auth.session.delete',
+        $authtoken
+    );
+}
+
+sub push_file_to_acq {
+    my $file = shift;
+    my $args = shift;
+
+    print "Pushing file '$file' to provider " . $args->{provider} . "\n";
+
+    # Cache the file name like Vandelay does.  ACQ will 
+    # read contents of the cache and then delete them.
+    # The key can be any unique value.
+    my $key = $$ . time . rand();
+    $cache->put_cache("vandelay_import_spool_$key", {path => $file});
+
+    my $req = $acq_ses->request(
+        'open-ils.acq.process_upload_records',
+        $authtoken,
+        $key, 
+        $args
+    );
+
+    while (my $resp = $req->recv(timeout => 600)) {
+        if(my $content = $resp->content) {
+            print Dumper($content) if $debug;
+        } else {
+            warn "Request returned no data: " . Dumper($resp) . "\n";
+        }
+    }
+}
+
+
+open-ils.acq open-ils.acq.process_upload_records "ce5ab1499cf019c0b52bf9eab87335fc", "f15791c2bcea4296c0d2c955c5204125", {"picklist":null,"provider":"1","ordering_agency":"4","create_po":false,"activate_po":"on","vandelay":{"import_no_match":"on","auto_overlay_exact":"on","auto_overlay_1match":false,"auto_overlay_best_match":false,"match_quality_ratio":"0.0","queue_name":"acq.bt.2","create_assets":false,"match_set":"","bib_source":"","merge_profile":"1","fall_through_merge_profile":"","existing_queue":""}}
+
+sub args_from_provider_conf {
+    my $conf = shift;
+
+    my $pcode = $conf->{code};
+    my $orgsn = $conf->{owner};
+
+    my $org = $editor->search_actor_org_unit(shortname => $orgsn)->[0];
+    if (!$org) {
+        warn "No such org unit in acq_order_reader config: '$orgsn'\n";
+        next;
+    }
+
+    my $provider = $editor->search_acq_provider({
+        code => $pcode,
+        owner => $org->id
+    });
+
+    if (!$provider) {
+        warn "No such provider in acq_order_reader config: '$pcode'\n";
+        next;
+    }
+
+}
+
+# -----------------------------------------------------
+# Main script
+# -----------------------------------------------------
+
+osrf_connect($osrf_config);
+
+my $conf = OpenSRF::Utils::SettingsClient->new;
+my $base_dir = $conf->config_value(acq_order_reader => 'base_dir');
+my $share_dir = $conf->config_value(acq_order_reader => 'shared_subdir');
+my $providers = $conf->config_value(acq_order_reader => 'provider');
+
+print Dumper($providers) if $debug;
+
+$acq_ses = OpenSRF::AppSession->create('open-ils.acq');
+my $cache = OpenSRF::Utils::Cache->new;
+my $editor = OpenILS::Utils::CStoreEditor->new;
+
+# main loop
+# for each provider directory, plus the shared directory, check
+# to see if there are any files pending.  For any files found, push
+# them up to the ACQ service, then delete the file
+
+while (1) {
+
+
+    new_auth_token();
+
+    # explicit providers
+    for my $provider_conf (@$providers) {
+        my $args = args_from_provider_conf($provider_conf) or next;
+        my @files = check_provider_files($args);
+        push_file_to_acq($_, $args) @files;
+    }
+    
+    # shared directory
+
+    clear_auth_token();
+
+    print "Sleeping for $poll_time seconds...\n" if $debug;
+    sleep $poll_time;
+}
+
+warn "Exiting main acq_order_reader loop!\n";
+
+
+__END__
+
+=head1 NAME
+
+acq_order_reader.pl - Collect MARC order record files and pass them onto the ACQ service
+
+=head1 SYNOPSIS
+
+./acq_order_reader.pl [script opts ...]
+
+This script uses the EG common options from B<Cronscript>.  See --help output for those.
+
+Run C<perldoc marc_stream_importer.pl> for full documentation.
+
+Note: this script has to be run in the same directory as B<oils_header.pl>.
+
+Typical server-style execution will include a trailing C<&> to run in the background.
+
+=head1 OPTIONS
+
+The only required option is --password
+
+ --password         =<eg_password>
+ --user             =<eg_username>  default: admin
+ --nodaemon                         default: OFF       When used with --spoolfile, turns off Net::Server mode and runs this utility in the foreground
+
+
+=head2 Old style: --noqueue and associated options
+
+=head1 EXAMPLES
+
+./acq_order_reader.pl --user admin --password demo123
+
+./acq_order_reader.pl --user admin --password demo123 -poll-interval 3 --debug --nodaemon
+
+=head1 AUTHORS
+
+    Bill Erickson <erickson@esilibrary.com>
+    Code liberally copied from marc_stream_importer.pl
+
+=cut