more modified perl modules from asp-tadl-sip1
authorJason Etheridge <jason@esilibrary.com>
Wed, 4 Apr 2012 14:35:18 +0000 (10:35 -0400)
committerJason Etheridge <jason@esilibrary.com>
Wed, 4 Apr 2012 14:35:18 +0000 (10:35 -0400)
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/actor.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/Cronscript.pm [new file with mode: 0644]

index 9140233..8b56e97 100644 (file)
@@ -775,7 +775,6 @@ sub _add_update_cards {
        my $session = shift;
        my $patron = shift;
        my $new_patron = shift;
-       my $user = shift;
 
        my $evt;
 
@@ -787,7 +786,7 @@ sub _add_update_cards {
                if(ref($card) and $card->isnew()) {
 
                        $virtual_id = $card->id();
-                       ( $card, $evt ) = _add_card($session,$card,$user);
+                       ( $card, $evt ) = _add_card($session,$card);
                        return (undef, $evt) if $evt;
 
                        #if(ref($patron->card)) { $patron->card($patron->card->id); }
@@ -808,9 +807,8 @@ sub _add_update_cards {
 
 # adds an card to the db and returns the card with new id
 sub _add_card {
-       my( $session, $card, $user ) = @_;
+       my( $session, $card ) = @_;
        $card->clear_id();
-       $card->creator($user->id);
 
        $logger->info("Adding new patron card ".$card->barcode);
 
index cbcbf9a..392febe 100644 (file)
@@ -142,7 +142,7 @@ use base qw/actor/;
 
 __PACKAGE__->table( 'actor_card' );
 __PACKAGE__->columns( Primary => qw/id/ );
-__PACKAGE__->columns( Essential => qw/usr barcode active creator create_date/ );
+__PACKAGE__->columns( Essential => qw/usr barcode active/ );
 
 #-------------------------------------------------------------------------------
 package actor::user_access_entry;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/Cronscript.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Cronscript.pm
new file mode 100644 (file)
index 0000000..a828fc5
--- /dev/null
@@ -0,0 +1,371 @@
+package OpenILS::Utils::Cronscript;
+
+# ---------------------------------------------------------------
+# Copyright (C) 2010 Equinox Software, Inc
+# Author: Joe Atzberger <jatzberger@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.
+# ---------------------------------------------------------------
+
+# The purpose of this module is to consolidate the common aspects
+# of various cron tasks that all need the same things:
+#    ~ non-duplicative processing, i.e. lockfiles and lockfile checking
+#    ~ opensrf_core.xml file location 
+#    ~ common options like help and debug
+
+use strict;
+use warnings;
+
+use Getopt::Long qw(:DEFAULT GetOptionsFromArray);
+use OpenSRF::System;
+use OpenSRF::AppSession;
+use OpenSRF::Utils::JSON;
+use OpenSRF::EX qw(:try);
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::Lockfile;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+
+use File::Basename qw/fileparse/;
+
+use Data::Dumper;
+use Carp;
+
+our @extra_opts = (     # additional keys are stored here
+    # 'addlopt'
+);
+
+our $debug = 0;
+
+sub _default_self {
+    return {
+    #   opts       => {},
+    #   opts_clean => {},
+    #   default_opts_clean => {},
+        default_opts       => {
+            'lock-file=s'   => OpenILS::Utils::Lockfile::default_filename,
+            'osrf-config=s' => '/openils/conf/opensrf_core.xml',
+            'debug'         => 0,
+            'verbose+'      => 0,
+            'help'          => 0,
+          # 'internal_var'  => 'XYZ',
+        },
+    #   lockfile => undef,
+    #   session => undef,
+    #   bootstrapped => 0,
+    #   got_options => 0,
+        auto_get_options_4_bootstrap => 1,
+    };
+}
+
+sub is_clean {
+    my $key = shift   or  return 1;
+    $key =~ /[=:].*$/ and return 0;
+    $key =~ /[+!]$/   and return 0;
+    return 1;
+}
+
+sub clean {
+    my $key = shift or return;
+    $key =~ s/[=:].*$//;
+    $key =~ s/[+!]$//;
+    return $key;
+}
+
+sub fuzzykey {                      # when you know the hash you want from, but not the exact key
+    my $self = shift or return;
+    my $key  = shift or return;
+    my $target = @_ ? shift : 'opts_clean';
+    foreach (map {clean($_)} keys %{$self->{default_opts}}) {  # TODO: cache
+        $key eq $_ and return $self->{$target}->{$_};
+    }
+}
+
+# MyGetOptions
+# A wrapper around GetOptions
+# {opts} does two things for GetOptions (see Getopt::Long)
+#  (1) maps command-line options to the *other* variables where values are stored (in opts_clean)
+#  (2) provides hashspace for the rest of the arbitrary options from the command-line
+#
+# TODO: allow more options to be passed here, maybe mimic Getopt::Long::GetOptions style
+#
+# If an arrayref argument is passed, then @ARGV will NOT be touched.
+# Instead, the array will be passed to GetOptionsFromArray.
+#
+
+sub MyGetOptions {
+    my $self = shift;
+    my $arrayref = @_ ? shift : undef;
+    if ($arrayref and ref($arrayref) ne 'ARRAY') {
+        carp "MyGetOptions argument is not an array ref.  Expect GetOptionsFromArray to explode";
+    }
+    $self->{got_options} and carp "MyGetOptions called after options were already retrieved previously";
+    my @keys = sort {is_clean($b) <=> is_clean($a)} keys %{$self->{default_opts}};
+    $debug and print "KEYS: ", join(", ", @keys), "\n";
+    foreach (@keys) {
+        my $clean = clean($_);
+        my $place = $self->{default_opts_clean}->{$clean};
+        $self->{opts_clean}->{$clean} = $place;  # prepopulate default
+        # $self->{opts}->{$_} = $self->{opts_clean}->{$clean};                 # pointer for GetOptions
+        $self->{opts}->{$_} = sub {
+            my $opt = shift;
+            my $val = shift;
+            ref ( $self->{opts_clean}->{$opt} ) and ref($self->{opts_clean}->{$opt}) eq 'SCALAR'
+            and ${$self->{opts_clean}->{$opt}} = $val;  # set the referent's value
+            $self->{opts_clean}->{$opt} = $val;     # burn the map, stick the value there
+        };                 # pointer for GetOptions
+    }
+    $arrayref  ? GetOptionsFromArray($arrayref, $self->{opts}, @keys)
+               : GetOptions(                    $self->{opts}, @keys) ;
+   
+    foreach (@keys) {
+        delete $self->{opts}->{$_};     # now remove the mappings from (1) so we just have (2)
+    }
+    $self->clean_mirror('opts');        # populate clean_opts w/ cleaned versions of (2), plus everything else
+
+    print $self->help() and exit if $self->{opts_clean}->{help};
+    $self->new_lockfile();
+    $self->{got_options}++;
+    return wantarray ? %{$self->{opts_clean}} : $self->{opts_clean};
+}
+
+sub new_lockfile {
+    my $self = shift;
+    $debug and $OpenILS::Utils::Lockfile::debug = $debug;
+    unless ($self->{opts_clean}->{nolockfile} || $self->{default_opts_clean}->{nolockfile}) {
+        $self->{lockfile_obj} = OpenILS::Utils::Lockfile->new($self->first_defined('lock-file'));
+        $self->{lockfile}     = $self->{lockfile_obj}->filename;
+    }
+}
+
+sub first_defined {
+    my $self = shift;
+    my $key  = shift or return;
+    foreach (qw(opts_clean opts default_opts_clean default_opts)) {
+        defined $self->{$_}->{$key} and return $self->{$_}->{$key};
+    }
+    return;
+}
+
+sub clean_mirror {
+    my $self  = shift;
+    my $dirty = @_ ? shift : 'default_opts';
+    foreach (keys %{$self->{$dirty}}) {
+        defined $self->{$dirty}->{$_} or next;
+        $self->{$dirty . '_clean'}->{clean($_)} = $self->{$dirty}->{$_};
+    }
+}
+
+sub new {
+    my $class = shift;
+    my $self  = _default_self;
+    bless ($self, $class);
+    $self->init(@_);
+    $debug and print "new ",  __PACKAGE__, " obj: ", Dumper($self);
+    return $self;
+}
+
+sub add_and_purge {
+    my $self = shift;
+    my $key  = shift;
+    my $val  = shift;
+    my $clean = clean($key);
+    my @others = grep {/$clean/ and $_ ne $key} keys %{$self->{default_opts}};
+    unless (@others) {
+        $debug and print "unique key $key => $val\n";
+        $self->{default_opts}->{$key} = $val;   # no purge, just add
+        return;
+    }
+    foreach (@others) {
+        $debug and print "variant of $key => $_\n";
+        if ($key ne $clean) {    # if it is a dirtier key, delete the clean one
+            delete $self->{default_opts}->{$_};
+            $self->{default_opts}->{$key} = $val;
+        } else {                 # else update the dirty one
+            $self->{default_opts}->{$_} = $val;
+        }
+    }
+}
+
+sub init {      # not INIT
+    my $self = shift;
+    my $opts  = @_ ? shift : {};    # user can specify more default options to constructor
+# TODO: check $opts is hashref; then check verbose/debug first.  maybe check negations e.g. "no-verbose" ?
+    @extra_opts = keys %$opts;
+    foreach (@extra_opts) {        # add any other keys w/ default values
+        $debug and print "init() adding option $_, default value: $opts->{$_}\n";
+        $self->add_and_purge($_, $opts->{$_});
+    }
+    $self->clean_mirror;
+    return $self;
+}
+
+sub usage {
+    # my $self = shift;
+    return "\nUSAGE: $0 [OPTIONS]";
+}
+
+sub options_help {
+    my $self = shift;
+    my $chunk = @_ ? shift : '';
+    return <<HELP
+
+COMMON OPTIONS:
+    --osrf-config </path/to/config_file>  Default: $self->{default_opts_clean}->{'osrf-config'}
+                 Specify OpenSRF core config file.
+
+    --lock-file </path/to/file_name>      Default: $self->{default_opts_clean}->{'lock-file'}
+                 Specify lock file.     
+
+HELP
+    . $chunk . <<HELP;
+    --debug      Print server responses to STDOUT for debugging
+    --verbose    Set verbosity
+    --help       Show this help message
+HELP
+}
+
+sub help {
+    my $self = shift;
+    return $self->usage() . "\n" . $self->options_help(@_) . $self->example();
+}
+
+sub example {
+    return "\n\nEXAMPLES:\n\n    $0 --osrf-config /my/other/opensrf_core.xml\n";
+}
+
+# the proper order is: MyGetOptions, bootstrap, session.
+# But the latter subs will check to see if they need to call the preceeding one(s).  
+
+sub session {
+    my $self = shift or return;
+    $self->{bootstrapped} or $self->bootstrap();
+    @_ or croak "session() called without required argument (app_name, e.g. 'open-ils.acq')";
+    return ($self->{session} ||= OpenSRF::AppSession->create(@_));
+}
+
+sub bootstrap {
+    my $self = shift or return;
+    if ($self->{auto_get_options_4_bootstrap} and not $self->{got_options}) {
+        $debug and print "Automatically calling MyGetOptions before bootstrap\n";
+        $self->MyGetOptions();
+    }
+    try {
+        $debug and print "bootstrap lock-file  : ", $self->first_defined('lock-file'), "\n";
+        $debug and print "bootstrap osrf-config: ", $self->first_defined('osrf-config'), "\n";
+        OpenSRF::System->bootstrap_client(config_file => $self->first_defined('osrf-config'));
+        Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
+        $self->{bootstrapped} = 1;
+    } otherwise {
+        $self->{bootstrapped} = 0;
+        warn shift;
+    };
+}
+
+sub editor_init {
+    my $self = shift or return;
+    OpenILS::Utils::CStoreEditor::init();   # no return value to check
+    $self->{editor_inited} = 1;
+}
+
+sub editor {
+    my $self = shift or return;
+    $self->{bootstrapped}  or $self->bootstrap();
+    $self->{editor_inited} or $self->editor_init();
+    return new_editor(@_);
+}
+
+1;
+__END__
+
+=pod
+
+=head1 NAME
+
+OpenILS::Utils::Cronscript - Consolidated options handling for any script (not just cron, really)
+
+=head1 SYNOPSIS
+
+    use OpenILS::Utils::Cronscript;
+
+    my %defaults = (
+        'min=i'      => 0,          # keys are Getopt::Long style options
+        'max=i'      => 999,        # values are default values
+        'user=s'     => 'admin',
+        'password=s' => '',
+        'nolockfile' => 1,
+    };
+
+    my $core = OpenILS::Utils::Cronscript->new(\%defaults);
+    my $opts = $core->MyGetOptions();   # options now in, e.g.: $opts->{max}
+    $core->bootstrap;
+
+Or if you don't need any additional options and just want to get a session going:
+    
+    use OpenILS::Utils::Cronscript;
+    my $session = OpenILS::Utils::Cronscript->new()->session('open-ils.acq');
+
+=head1 DESCRIPTION
+
+There are a few main problems when writing a new script for Evergreen. 
+
+=head2 Initialization
+
+The runtime 
+environment for the application requires a lot of initialization, but during normal operation it
+has already occured (when Evergreen was started).  So most of the EG code never has to deal with 
+this problem, but standalone scripts do.  The timing and sequence of requisite events is important and not obvious.
+
+=head2 Common Options, Consistent Options
+
+We need several common options for each script that accesses the database or
+uses EG data objects and methods.  Logically, these options often deal with initialization.  They
+should take the B<exact> same form(s) for each script and should not be 
+dependent on the local author to copy and paste them from some reference source.  We really don't want to encourage (let alone force)
+admins to use C<--config>, C<--osrf-confg>, C<-c>, and C<@ARGV[2]> for the same purpose in different scripts, with different
+default handling, help descriptions and error messages (or lack thereof).
+
+This suggests broader problem of UI consistency and uniformity, also partially addressed by this module.
+
+=head2 Lockfiles
+
+A lockfile is necessary for a script that wants to prevent possible simultaneous execution.  For example, consider a script 
+that is scheduled to run frequently, but that experiences occasional high load: you wouldn't want crontab to start running
+it again if the first instance had not yet finished.  
+
+But the code for creating, writing to, checking for, reading and cleaning up a lockfile for the script bloats what might otherwise be a terse 
+method call.  Conscript handles lockfile generation and removal automatically.
+
+=head1 OPTIONS
+
+The common options (and default values) are:
+
+    'lock-file=s'   => OpenILS::Utils::Lockfile::default_filename,
+    'osrf-config=s' => '/openils/conf/opensrf_core.xml',
+    'debug'         => 0,
+    'verbose+'      => 0,
+    'help'          => 0,
+
+=head1 TODO 
+
+More docs here.
+
+=head1 SEE ALSO
+
+    Getopt::Long
+    OpenILS::Utils::Lockfile
+    oils_header.pl
+
+=head1 AUTHOR
+
+Joe Atzberger <jatzberger@esilibrary.com>
+
+=cut
+