From df8f2d30f2c71fb1ef0aa56c049dfd63c1c52601 Mon Sep 17 00:00:00 2001 From: Jason Etheridge Date: Wed, 4 Apr 2012 10:35:18 -0400 Subject: [PATCH] more modified perl modules from asp-tadl-sip1 --- .../src/perlmods/lib/OpenILS/Application/Actor.pm | 6 +- .../lib/OpenILS/Application/Storage/CDBI/actor.pm | 2 +- .../src/perlmods/lib/OpenILS/Utils/Cronscript.pm | 371 +++++++++++++++++++++ 3 files changed, 374 insertions(+), 5 deletions(-) create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/Utils/Cronscript.pm diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm index 9140233b8b..8b56e9757a 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm @@ -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); diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/actor.pm index cbcbf9a3ad..392febe999 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/actor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/actor.pm @@ -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 index 0000000000..a828fc5f91 --- /dev/null +++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/Cronscript.pm @@ -0,0 +1,371 @@ +package OpenILS::Utils::Cronscript; + +# --------------------------------------------------------------- +# Copyright (C) 2010 Equinox Software, Inc +# Author: Joe Atzberger +# +# 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 < Default: $self->{default_opts_clean}->{'osrf-config'} + Specify OpenSRF core config file. + + --lock-file Default: $self->{default_opts_clean}->{'lock-file'} + Specify lock file. + +HELP + . $chunk . <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 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 + +=cut + -- 2.11.0