LP#1728331 - Action Trigger Aggregator Date Parameter
[Evergreen.git] / Open-ILS / src / support-scripts / action_trigger_aggregator.pl.in
1 #!/usr/bin/perl
2 # ---------------------------------------------------------------
3 # Copyright (C) 2012 Equinox Software, Inc
4 # Author: Bill Erickson <berick@esilibrary.com>
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 # ---------------------------------------------------------------
16 use strict; 
17 use warnings;
18 use DateTime;
19 use Getopt::Long;
20 use OpenSRF::System;
21 use OpenSRF::AppSession;
22 use OpenILS::Utils::CStoreEditor;
23 use OpenILS::Utils::RemoteAccount;
24 use OpenILS::Utils::Fieldmapper;
25
26 my $osrf_config = '@sysconfdir@/opensrf_core.xml';
27 my $default_start_date = DateTime->now->strftime('%F');
28 my $date        = '';
29 my $start_date  = '';
30 my $end_date    = '';
31 my $event_defs  = '';
32 my $granularity = '';
33 my $output_file = '';
34 my $local_dir   = '/tmp'; # where to keep local copies of generated files
35 my $remote_acct = '';
36 my $cleanup     = 0; # cleanup generated files
37 my $verbose     = 0;
38 my $help        = 0;
39
40 GetOptions(
41     'osrf-config=s'     => \$osrf_config,
42     'date=s'            => \$date,
43     'start-date=s'      => \$start_date,
44     'end-date=s'        => \$end_date,
45     'event-defs=s'      => \$event_defs,
46     'granularity=s'     => \$granularity,
47     'output-file=s'     => \$output_file,
48     'remote-acct=s'     => \$remote_acct,
49     'local-dir=s'       => \$local_dir,
50     'cleanup'           => \$cleanup,
51     'verbose'           => \$verbose,
52     'help'              => \$help
53 );
54
55 sub help {
56     print <<HELP;
57
58 Action/Trigger Aggregator Script
59
60 Collect template output from one or more event-definitions and stitch the 
61 results together in a single file.  The file may be optionally stored locally
62 and/or delivered to a remote ftp/scp account.
63
64 This script is useful for generating a single mass notification (e.g. overdue)
65 file and sending the file to a 3rd party for processing and/or for local 
66 processing.  The anticipated use case would be to stitch together lines of CSV 
67 or chunks of XML.
68
69 Example
70
71 # Collect a week of notifications for 3 event definitions
72
73 $0 \
74     --event-defs 104,105,106 \
75     --start-date 2012-01-01 \
76     --end-date 2012-01-07 \
77     --output-file 2012-01-07.notify.csv \
78     --local-dir /var/run/evergreen/csv \
79     --remote-acct 6
80
81 Options
82
83     --cleanup
84         Remove the local file after script is done.
85
86     --event-defs
87         action_trigger.event_definition IDs to include
88
89     --granularity
90         Process all event definitions that match this granularity.  If used in
91         conjunction with --event-defs, the union of the two sets is used.
92     
93     --start-date 
94         Only collect output for events whose run_time is on or after this ISO date
95
96     --end-date 
97         Only collect output for events whose run_time occurred before this ISO date
98
99     --date
100         Only collect output for events whose run_time is on this ISO date.
101         Cannot be used with either --start-date or --end-date
102
103     --osrf-config
104         To set the OpenSRF config to something other than the default of
105         '@sysconfdir@/opensrf_core.xml'
106
107     --output-file [default STDOUT]
108         Output goes to this file.  
109
110     --local-dir [default /tmp]
111         Local directory where the output-file is placed.  If --cleanup is 
112         set, this is used as the tmp directory for file generation
113
114     --remote-acct
115         Evergreen config.remote_account ID
116         If set, the output-file will be sent via sFTP/SCP to this server.
117
118     --verbose
119         Show more information about what the script is doing.
120
121     --help
122         Show command usage information.
123
124 HELP
125     exit;
126 }
127
128 help() if $help;
129
130 my @event_defs = split(/,/, $event_defs);
131 die "--event-defs or --granularity required\n" 
132     unless @event_defs or $granularity;
133
134 my $local_file = $output_file ? "$local_dir/$output_file" : '&STDOUT';
135
136 open(OUTFILE, ">$local_file") or 
137     die "unable to open out-file '$local_file' for writing: $!\n";
138 binmode(OUTFILE, ":utf8");
139
140 print "Output will be written to $local_file\n" if $verbose;
141
142 OpenSRF::System->bootstrap_client(config_file => $osrf_config);
143 Fieldmapper->import(IDL => 
144     OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
145 OpenILS::Utils::CStoreEditor::init();
146 my $editor = OpenILS::Utils::CStoreEditor->new;
147
148 # if granularity is set, append all event defs with the 
149 # selected granularity to the set of event-defs to process.
150 if ($granularity) {
151     my $defs = $editor->search_action_trigger_event_definition(
152         {granularity => $granularity},
153         {idlist => 1}
154     );
155
156     for my $id (@$defs) {
157         push(@event_defs, $id) 
158             unless grep { $_ eq $id} @event_defs;
159     }
160 }
161
162 print "Processing event-defs @event_defs\n" if $verbose;
163
164 die "Can't use --date with --start-date or --end-date!" if ($date && ($start_date || $end_date) );
165
166 $start_date = $default_start_date unless $start_date; # This is basically what happened pre- --date
167
168 my %date_filter;
169 $date_filter{run_time} = {'>=' => $start_date} if $start_date;
170
171 if ($end_date) {
172     if ($date_filter{run_time}) {
173         # both filters requested, -and them together
174         $date_filter{'-and'} = [
175             {run_time => delete $date_filter{run_time}},
176             {run_time => {'<' => $end_date}}
177         ];
178     } else {
179         $date_filter{run_time} = {'<' => $end_date};
180     }
181 }
182
183 if ($date) {
184     delete $date_filter{run_time}; # will always exist because of defaulting $start_date
185     $date_filter{run_time} = { "=" => {"transform" => "date", "value" => $date}};
186 }
187
188 # collect the event tempate output data
189 # use a real session here so we can stream results directly to the output file
190 my $ses = OpenSRF::AppSession->create('open-ils.cstore');
191 my $req = $ses->request(
192     'open-ils.cstore.json_query', {
193         select => {ateo => [
194             # ateo's may be linked to multiple atev's; select distinct.
195             {column => 'id', transform => 'distinct'}, 'data']},
196         from => {ateo => { atev => {
197             filter => {state => 'complete', %date_filter},
198             join => {atevdef => {filter => {
199                 id => \@event_defs,
200                 active => 't'
201             }}}
202         }}}
203     }
204 );
205
206 # use a large timeout since this is likely to be a hefty query
207 while (my $resp = $req->recv(timeout => 3600)) {
208     die $req->failed . "\n" if $req->failed;
209     my $content = $resp->content or next;
210     print OUTFILE $content->{data};
211 }
212
213 if ($remote_acct) {
214     # send the file to the remote account
215
216     my $racct = $editor->retrieve_config_remote_account($remote_acct);
217     die "No such remote account $remote_acct" unless $racct;
218
219     my $type;
220     my $host = $racct->host;
221     ($host =~ s/^(S?FTP)://i and $type = uc($1)) or                   
222     ($host =~ s/^(SSH|SCP)://i and $type = 'SCP');
223     $host =~ s#//##;
224
225     my $acct = OpenILS::Utils::RemoteAccount->new(
226         type            => $type,
227         remote_host     => $host,
228         account_object  => $racct,
229         local_file      => $local_file,
230         remote_file     => $output_file
231     );
232
233     my $res = $acct->put;
234
235     die "Unable to push to remote server [$remote_acct] : " . 
236         $acct->error . "\n" unless $res;
237
238     print "Pushed file to $res\n" if $verbose;
239 }
240
241 if ($cleanup) {
242     unlink($local_file) or 
243         die "Unable to clean up file '$local_file' : $!\n";
244 }
245
246