Bug 34583: Overdue notice - wrong coding in outlook in czech e-mail in “print” mode
[koha.git] / misc / cronjobs / runreport.pl
1 #!/usr/bin/perl
2 #
3 # Copyright 2008 Liblime
4 # Copyright 2014 Foundations Bible College, Inc.
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20
21 use Modern::Perl;
22
23 use Koha::Script -cron;
24 use C4::Reports::Guided qw( store_results execute_query );
25 use Koha::Reports;
26 use C4::Context;
27 use C4::Log qw( cronlogaction );
28 use Koha::Email;
29 use Koha::DateUtils qw( dt_from_string );
30 use Koha::SMTP::Servers;
31
32 use Getopt::Long qw( GetOptions );
33 use Pod::Usage qw( pod2usage );
34 use Text::CSV::Encoded;
35 use CGI qw ( -utf8 );
36 use Carp qw( carp );
37 use Encode qw( decode );
38 use JSON qw( to_json );
39 use Try::Tiny qw( catch try );
40
41 =head1 NAME
42
43 runreport.pl - Run pre-existing saved reports
44
45 =head1 SYNOPSIS
46
47 runreport.pl [ -h | -m ] [ -v ] reportID [ reportID ... ]
48
49  Options:
50    -h --help       brief help message
51    -m --man        full documentation, same as --help --verbose
52    -v --verbose    verbose output
53
54    --format=s      selects format. Choice of text, html, csv or tsv
55
56    -e --email      whether to use e-mail (implied by --to or --from)
57    -a --attachment additionally attach the report as a file. cannot be used with html format
58    --username      username to pass to the SMTP server for authentication
59    --password      password to pass to the SMTP server for authentication
60    --method        method is the type of authentication. Ie. LOGIN, DIGEST-MD5, etc.
61    --to=s          e-mail address to send report to
62    --from=s        e-mail address to send report from
63    --subject=s     subject for the e-mail
64    --param=s      parameters for the report
65    --store-results store the result of the report
66    --csv-header    add column names as first line of csv output
67
68
69  Arguments:
70    reportID        report ID Number from saved_sql.id, multiple ID's may be specified
71
72 =head1 OPTIONS
73
74 =over
75
76 =item B<--help>
77
78 Print a brief help message and exits.
79
80 =item B<--man>
81
82 Prints the manual page and exits.
83
84 =item B<-v>
85
86 Verbose. Without this flag set, only fatal errors are reported.
87
88 =item B<--format>
89
90 Current options are text, html, csv, and tsv. At the moment, text and tsv both produce tab-separated output.
91
92 =item B<--separator>
93
94 Separator character, only for csv format. Default to comma.
95
96 =item B<--email>
97
98 Whether to use e-mail (implied by --to or --from).
99
100 =item B<--username>
101
102 Username to pass to the SMTP server for authentication
103
104 =item B<--password>
105
106 Password to pass to the SMTP server for authentication
107
108 =item B<--method>
109
110 Method is the type of authentication. Ie. LOGIN, DIGEST-MD5, etc.
111
112 =item B<--to>
113
114 E-mail address to send report to. Defaults to KohaAdminEmailAddress.
115
116 =item B<--from>
117
118 E-mail address to send report from. Defaults to KohaAdminEmailAddress.
119
120 =item B<--subject>
121
122 Subject for the e-mail message. Defaults to "Koha Saved Report"
123
124 =item B<--param>
125
126 Repeatable, should provide one param per param requested for the report.
127 Report params are not combined as on the staff side, so you may need to repeat
128 params.
129
130 =item B<--store-results>
131
132 Store the result of the report into the saved_reports DB table.
133
134 To access the results, go on Reports > Guided reports > Saved report.
135
136 =back
137
138 =head1 DESCRIPTION
139
140 This script is designed to run existing Saved Reports.
141
142 =head1 USAGE EXAMPLES
143
144 B<runreport.pl 16>
145
146 In the most basic form, runs the report specified by ID number from 
147 saved_sql.id, in this case #16, outputting the results to STDOUT.  
148
149 B<runreport.pl 16 17>
150
151 Same as above, but also runs report #17. 
152
153 =head1 TO DO
154
155 =over
156
157
158 =item *
159
160 Allow Saved Results option.
161
162
163 =back
164
165 =head1 SEE ALSO
166
167 Reports - Guided Reports
168
169 =cut
170
171 binmode STDOUT, ":encoding(UTF-8)";
172
173 # These variables can be set by command line options,
174 # initially set to default values.
175
176 my $help    = 0;
177 my $man     = 0;
178 my $verbose = 0;
179 my $send_email = 0;
180 my $attachment = 0;
181 my $format  = "text";
182 my $to      = "";
183 my $from    = "";
184 my $subject = "";
185 my @params = ();
186 my $separator = ',';
187 my $quote = '"';
188 my $store_results = 0;
189 my $csv_header = 0;
190 my $csv_separator = "";
191
192 my $username = undef;
193 my $password = undef;
194 my $method = 'LOGIN';
195
196 my $command_line_options = join(" ",@ARGV);
197
198 GetOptions(
199     'help|?'            => \$help,
200     'man'               => \$man,
201     'verbose'           => \$verbose,
202     'format=s'          => \$format,
203     'separator=s'       => \$csv_separator,
204     'to=s'              => \$to,
205     'from=s'            => \$from,
206     'subject=s'         => \$subject,
207     'param=s'           => \@params,
208     'email'             => \$send_email,
209     'a|attachment'      => \$attachment,
210     'username:s'        => \$username,
211     'password:s'        => \$password,
212     'method:s'          => \$method,
213     'store-results'     => \$store_results,
214     'csv-header'        => \$csv_header,
215
216 ) or pod2usage(2);
217 pod2usage( -verbose => 2 ) if ($man);
218 pod2usage( -verbose => 2 ) if ($help and $verbose);
219 pod2usage(1) if $help;
220
221 cronlogaction({ info => $command_line_options });
222
223 unless ($format) {
224     $verbose and print STDERR "No format specified, assuming 'text'\n";
225     $format = 'text';
226 }
227
228 if ($csv_separator) {
229     if ( $format eq 'csv' ) {
230         $separator = "$csv_separator";
231     } else {
232         print STDERR "Cannot specify separator if not using CSV format\n";
233     }
234 }
235
236 if ($format eq 'tsv' || $format eq 'text') {
237     $format = 'csv';
238     $separator = "\t";
239 }
240
241 if ($to or $from or $send_email) {
242     $send_email = 1;
243     $from or $from = C4::Context->preference('KohaAdminEmailAddress');
244     $to   or $to   = C4::Context->preference('KohaAdminEmailAddress');
245 }
246
247 unless (scalar(@ARGV)) {
248     print STDERR "ERROR: No reportID(s) specified\n";
249     pod2usage(1);
250 }
251 ($verbose) and print scalar(@ARGV), " argument(s) after options: " . join(" ", @ARGV) . "\n";
252
253 my $today = dt_from_string();
254 my $date = $today->ymd();
255
256 foreach my $report_id (@ARGV) {
257     my $report = Koha::Reports->find( $report_id );
258     unless ($report) {
259         warn "ERROR: No saved report $report_id found";
260         next;
261     }
262     my $sql         = $report->savedsql;
263     my $report_name = $report->report_name;
264     my $type        = $report->type;
265
266     $verbose and print "SQL: $sql\n\n";
267     if ( $subject eq "" )
268     {
269         if ( defined($report_name) and $report_name ne "")
270         {
271             $subject = $report_name ;
272         }
273         else
274         {
275             $subject = 'Koha Saved Report';
276         }
277     }
278
279     # convert SQL parameters to placeholders
280     my $params_needed = ( $sql =~ s/(<<[^>]+>>)/\?/g );
281     die("You supplied ". scalar @params . " parameter(s) and $params_needed are required by the report") if scalar @params != $params_needed;
282
283     my ($sth) = execute_query(
284         {
285             sql        => $sql,
286             sql_params => \@params,
287             report_id  => $report_id,
288         }
289     );
290     my $count = scalar($sth->rows);
291     unless ($count) {
292         print "NO OUTPUT: 0 results from execute_query\n";
293         next;
294     }
295     $verbose and print "$count results from execute_query\n";
296
297     my $message;
298     my @rows_to_store;
299     if ($format eq 'html') {
300         my $cgi = CGI->new();
301         my @rows;
302         while (my $line = $sth->fetchrow_arrayref) {
303             foreach (@$line) { defined($_) or $_ = ''; }    # catch undef values, replace w/ ''
304             push @rows, $cgi->TR( join('', $cgi->td($line)) ) . "\n";
305             push @rows_to_store, [@$line] if $store_results;
306         }
307         $message = $cgi->table(join "", @rows);
308     } elsif ($format eq 'csv') {
309         my $csv = Text::CSV::Encoded->new({
310             encoding_out => 'utf8',
311             binary      => 1,
312             quote_char  => $quote,
313             sep_char    => $separator,
314             });
315
316         if ( $csv_header ) {
317             my @fields = map { decode( 'utf8', $_ ) } @{ $sth->{NAME} };
318             $csv->combine( @fields );
319             $message .= $csv->string() . "\n";
320             push @rows_to_store, [@fields] if $store_results;
321         }
322
323         while (my $line = $sth->fetchrow_arrayref) {
324             $csv->combine(@$line);
325             $message .= $csv->string() . "\n";
326             push @rows_to_store, [@$line] if $store_results;
327         }
328         $message = Encode::decode_utf8($message);
329     }
330     if ( $store_results ) {
331         my $json = to_json( \@rows_to_store );
332         C4::Reports::Guided::store_results( $report_id, $json );
333     }
334     if ($send_email) {
335
336         my $email = Koha::Email->new(
337             {
338                 to      => $to,
339                 from    => $from,
340                 subject => $subject,
341             }
342         );
343
344         if ( $format eq 'html' ) {
345             $message = "<html><head><style>tr:nth-child(2n+1) { background-color: #ccc;}</style></head><body>$message</body></html>";
346             $email->html_body($message);
347         }
348         else {
349             $email->text_body($message);
350         }
351
352         $email->attach(
353             Encode::encode_utf8($message),
354             content_type => "text/$format",
355             name         => "report$report_id-$date.$format",
356             disposition  => 'attachment',
357         ) if $attachment;
358
359         my $smtp_server = Koha::SMTP::Servers->get_default;
360         $smtp_server->set(
361             {
362                 user_name => $username,
363                 password  => $password,
364             }
365         )
366             if $username;
367
368         $email->transport( $smtp_server->transport );
369         try {
370             $email->send_or_die;
371         }
372         catch {
373             carp "Mail not sent: $_";
374         };
375     }
376     else {
377         print $message;
378     }
379 }
380
381 cronlogaction({ action => 'End', info => "COMPLETED" });