2 # ---------------------------------------------------------------
3 # Generates the overdue notices XML file
4 # ./eg_gen_overdue.pl <bootstap> 0
5 # generates today's notices
6 # ./eg_gen_overdue.pl <bootstap> 1 0
7 # generates notices for today - 1 and today
8 # ./eg_gen_overdue.pl <bootstap> 2 1 0
9 # ./eg_gen_overdue.pl <bootstap> 3 2 1 0 etc...
10 # ---------------------------------------------------------------
14 use strict; use warnings;
15 require '../../../Open-ILS/src/support-scripts/oils_header.pl';
16 use vars qw/$logger $apputils/;
18 use OpenILS::Const qw/:const/;
20 use DateTime::Format::ISO8601;
21 use OpenSRF::Utils qw/:datetime/;
22 use Unicode::Normalize;
24 my $bsconfig = shift || die "usage: $0 <bootstrap_config>\n";
26 @goback = (0) unless @goback;
27 osrf_connect($bsconfig);
28 my $e = OpenILS::Utils::CStoreEditor->new;
31 # ---------------------------------------------------------------
32 # Set up the email template
33 my $etmpl = $ENV{EG_OVERDUE_EMAIL_TEMPLATE};
34 print "Using email template: $etmpl\n";
38 my $email_template = "@etmpl";
39 # ---------------------------------------------------------------
43 my @date = CORE::localtime;
48 my $mon = $date[4] + 1;
49 my $year = $date[5] + 1900;
56 <?xml version='1.0' encoding='UTF-8'?>
57 <file type="notice" date="$day/$mon/$year" time="$hour:$min:$sec">
61 print_notices($_) for @goback;
69 # -----------------------------------------------------------------------
70 # -----------------------------------------------------------------------
74 my $goback = shift || 0;
76 for my $day ( qw/ 7 14 30 / ) {
77 my ($start, $end) = make_date_range($day + $goback);
78 $logger->debug("OD_notice: process date range $start -> $end");
80 my $circs = $e->search_action_circulation(
84 due_date => { between => [ $start, $end ] }
87 order_by => { circ => 'usr, circ_lib' }
93 process_circs( $circs, "${day}day" );
102 return unless @$circs;
104 $logger->debug("OD_notice: processing range $range and circs @$circs");
110 for my $circ (@$circs) {
111 $circ = $e->retrieve_action_circulation($circ);
114 $circ->circ_lib != $org or $circ->usr ne $patron ) {
115 $org = $circ->circ_lib;
116 $patron = $circ->usr;
117 print_notice( $range, \@current ) if @current;
121 push( @current, $circ );
124 print_notice( $range, \@current );
127 sub make_date_range {
128 my $daysback = shift;
130 my $date = DateTime->from_epoch(
131 epoch => ( CORE::time - ($daysback * 24 * 60 * 60) ) );
134 $date->set_minute(0);
135 $date->set_second(0);
139 $date->set_minute(59);
140 $date->set_second(59);
142 return ($start, "$date");
147 my( $range, $circs ) = @_;
148 return unless @$circs;
149 my $org = $circs->[0]->circ_lib;
150 my $usr = $circs->[0]->usr;
151 $logger->debug("OD_notice: printing $range user:$usr org:$org");
153 my @patron_data = fetch_patron_data($usr);
154 my @org_data = fetch_org_data($org);
158 if( $email = $patron_data[0]->email
159 and $email =~ /.+\@.+/
160 and ($range eq '7day' or $range eq '14day') ) {
162 send_email($range, \@patron_data, \@org_data, $circs);
166 print "\t\t<notice type='overdue' count='$range'>\n";
167 print_patron_xml_chunk(@patron_data);
168 print_org_xml_chunk(@org_data);
169 print_circ_chunk($_) for @$circs;
170 print "\t\t</notice>\n";
175 sub fetch_patron_data {
178 my $patron = $USER_CACHE{$user_id};
181 $logger->debug("OD_notice: fetching patron $user_id");
183 $patron = $e->retrieve_actor_user(
189 'au' => [qw/ card billing_address mailing_address /]
193 ) or return handle_event($e->event);
195 $USER_CACHE{$user_id} = $patron;
198 my $bc = $patron->card->barcode;
199 my $fn = $patron->first_given_name;
200 my $mn = $patron->second_given_name;
201 my $ln = $patron->family_name;
203 my ( $s1, $s2, $city, $state, $zip );
204 my $baddr = $patron->billing_address || $patron->mailing_address;
206 $s1 = $baddr->street1;
207 $s2 = $baddr->street2;
208 $city = $baddr->city;
209 $state = $baddr->state;
210 $zip = $baddr->post_code;
213 $bc = entityize($bc);
214 $fn = entityize($fn);
215 $mn = entityize($mn);
216 $ln = entityize($ln);
217 $s1 = entityize($s1);
218 $s2 = entityize($s2);
219 $city = entityize($city);
220 $state = entityize($state);
221 $zip = entityize($zip);
223 return ( $patron, $bc, $fn, $mn, $ln, $s1, $s2, $city, $state, $zip );
227 sub print_patron_xml_chunk {
228 my( $patron, $bc, $fn, $mn, $ln, $s1, $s2, $city, $state, $zip ) = @_;
231 <id type="barcode">$bc</id>
232 <fullname>$fn $mn $ln</fullname>
233 <street1>$s1 $s2</street1>
234 <city_state_zip>$city, $state $zip</city_state_zip>
243 my $org = $ORG_CACHE{$org_id};
246 $logger->debug("OD_notice: fetching org $org_id");
248 $org = $e->retrieve_actor_org_unit(
254 { aou => [ qw/billing_address mailing_address/ ] }
257 ) or return handle_event($e->event);
259 $ORG_CACHE{$org_id} = $org;
262 my $name = $org->name;
263 my $email = $org->email;
265 my( $phone, $s1, $s2, $city, $state, $zip );
266 my $baddr = $org->billing_address || $org->mailing_address;
268 $s1 = $baddr->street1;
269 $s2 = $baddr->street2;
270 $city = $baddr->city;
271 $state = $baddr->state;
272 $zip = $baddr->post_code;
275 $name = entityize($name);
276 $phone = entityize($phone);
277 $s1 = entityize($s1);
278 $s2 = entityize($s2);
279 $city = entityize($city);
280 $state = entityize($state);
281 $zip = entityize($zip);
282 $email = entityize($email);
284 return ( $org, $name, $phone, $s1, $s2, $city, $state, $zip, $email );
288 sub print_org_xml_chunk {
289 my( $org, $name, $phone, $s1, $s2, $city, $state, $zip, $email ) = @_;
292 <libname>$name</libname>
293 <libphone>$phone</libphone>
294 <libstreet1>$s1 $s2</libstreet1>
295 <libcity_state_zip>$city, $state $zip</libcity_state_zip>
301 sub fetch_circ_data {
308 my $d = $circ->due_date;
309 $d =~ s/[T ].*//og; # just for logging
310 $logger->debug("OD_notice: processing circ ".$circ->id." $d");
312 my $due = DateTime::Format::ISO8601->new->parse_datetime(
313 clense_ISO8601($circ->due_date));
316 my $mon = $due->month;
317 my $year = $due->year;
319 my $copy = $e->retrieve_asset_copy($circ->target_copy)
320 or return handle_event($e->event);
322 my $bc = $copy->barcode;
324 if( $copy->call_number == OILS_PRECAT_CALL_NUMBER ) {
325 $title = $copy->dummy_title || "";
326 $author = $copy->dummy_author || "";
330 my $volume = $e->retrieve_asset_call_number(
336 acn => [ qw/record/ ]
340 ) or return handle_event($e->event);
342 $cn = $volume->label;
343 my $mods = $apputils->record_to_mvr($volume->record);
345 $title = $mods->title || "";
346 $author = $mods->author || "";
350 $title = entityize($title);
351 $author = entityize($author);
352 $cn = entityize($cn);
353 $bc = entityize($bc);
355 return( $title, $author, $cn, $bc, $day, $mon, $year );
359 sub print_circ_chunk {
361 my ( $title, $author, $cn, $bc, $day, $mon, $year ) = fetch_circ_data($circ);
364 <title>$title</title>
365 <author>$author</author>
366 <duedate>$day/$mon/$year</duedate>
368 <barcode>$bc</barcode>
376 my( $range, $patron_data, $org_data, $circs ) = @_;
377 my( $org, $org_name, $org_phone, $org_s1, $org_s2, $org_city, $org_state, $org_zip, $org_email ) = @$org_data;
378 my( $patron, $bc, $fn, $mn, $ln, $user_s1, $user_s2, $user_city, $user_state, $user_zip ) = @$patron_data;
380 my $pemail = $patron_data->[0]->email;
382 my $tmpl = $email_template;
383 my @time = localtime;
384 my $year = $time[5] + 1900;
385 my $mon = $time[4] + 1;
388 my $r = ($range eq '7day') ? 7 : 14;
390 $tmpl =~ s/\${EMAIL_RECIPIENT}/$pemail/o;
391 $tmpl =~ s/\${EMAIL_SENDER}/$org_email/o;
392 $tmpl =~ s/\${EMAIL_REPLY_TO}/$org_email/o;
393 $tmpl =~ s/\${EMAIL_HEADERS}/\n\r\n\r/o;
394 $tmpl =~ s/\${RANGE}/$r/o;
395 $tmpl =~ s/\${DATE}/$day\/$mon\/$year/o;
396 $tmpl =~ s/\${FIRST_NAME}/$fn/o;
397 $tmpl =~ s/\${MIDDLE_NAME}/$mn/o;
398 $tmpl =~ s/\${LAST_NAME}/$ln/o;
400 my ($itmpl) = $tmpl =~ /\${OVERDUE_ITEMS\[(.*)\]}/ms;
403 for my $circ (@$circs) {
404 my $circtmpl = $itmpl;
405 my ( $title, $author, $cn, $bc, $due_day, $due_mon, $due_year ) = fetch_circ_data($circ);
406 $circtmpl =~ s/\${TITLE}/$title/o;
407 $circtmpl =~ s/\${AUTHOR}/$author/o;
408 $circtmpl =~ s/\${CALL_NUMBER}/$cn/o;
409 $circtmpl =~ s/\${DUE_DAY}/$due_day/o;
410 $circtmpl =~ s/\${DUE_MONTH}/$due_mon/o;
411 $circtmpl =~ s/\${DUE_YEAR}/$due_year/o;
412 $circtmpl =~ s/\${ITEM_BARCODE}/$bc/o;
413 $items .= "$circtmpl\n";
416 $tmpl =~ s/\${OVERDUE_ITEMS\[.*\]}/$items/ms;
418 my $org_addr = "$org_s1 $org_s2 $org_city, $org_state $org_zip";
419 $tmpl =~ s/\${ORG_NAME}/$org_name/o;
420 $tmpl =~ s/\${ORG_ADDRESS}/$org_addr/o;
421 $tmpl =~ s/\${ORG_PHONE}/$org_phone/o;
423 warn "EMAIL: $tmpl\n";
425 $logger->info("OD_notice: sending email to".$patron_data->[0]->email);
430 warn "OD_notice: ".Dumper($evt) . "\n";
431 $logger->error("OD_notice: ".Dumper($evt));
436 my $stuff = shift || return "";
437 $stuff =~ s/\</</og;
438 $stuff =~ s/\>/>/og;
439 $stuff =~ s/\&/&/og;
440 $stuff = NFC($stuff);
441 $stuff =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;