Update release notes for 22.05.21 release
[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 GetOptions(
197     'help|?'            => \$help,
198     'man'               => \$man,
199     'verbose'           => \$verbose,
200     'format=s'          => \$format,
201     'separator=s'       => \$csv_separator,
202     'to=s'              => \$to,
203     'from=s'            => \$from,
204     'subject=s'         => \$subject,
205     'param=s'           => \@params,
206     'email'             => \$send_email,
207     'a|attachment'      => \$attachment,
208     'username:s'        => \$username,
209     'password:s'        => \$password,
210     'method:s'          => \$method,
211     'store-results'     => \$store_results,
212     'csv-header'        => \$csv_header,
213
214 ) or pod2usage(2);
215 pod2usage( -verbose => 2 ) if ($man);
216 pod2usage( -verbose => 2 ) if ($help and $verbose);
217 pod2usage(1) if $help;
218
219 cronlogaction();
220
221 unless ($format) {
222     $verbose and print STDERR "No format specified, assuming 'text'\n";
223     $format = 'text';
224 }
225
226 if ($csv_separator) {
227     if ( $format eq 'csv' ) {
228         $separator = "$csv_separator";
229     } else {
230         print STDERR "Cannot specify separator if not using CSV format\n";
231     }
232 }
233
234 if ($format eq 'tsv' || $format eq 'text') {
235     $format = 'csv';
236     $separator = "\t";
237 }
238
239 if ($to or $from or $send_email) {
240     $send_email = 1;
241     $from or $from = C4::Context->preference('KohaAdminEmailAddress');
242     $to   or $to   = C4::Context->preference('KohaAdminEmailAddress');
243 }
244
245 unless (scalar(@ARGV)) {
246     print STDERR "ERROR: No reportID(s) specified\n";
247     pod2usage(1);
248 }
249 ($verbose) and print scalar(@ARGV), " argument(s) after options: " . join(" ", @ARGV) . "\n";
250
251 my $today = dt_from_string();
252 my $date = $today->ymd();
253
254 foreach my $report_id (@ARGV) {
255     my $report = Koha::Reports->find( $report_id );
256     unless ($report) {
257         warn "ERROR: No saved report $report_id found";
258         next;
259     }
260     my $sql         = $report->savedsql;
261     my $report_name = $report->report_name;
262     my $type        = $report->type;
263
264     $verbose and print "SQL: $sql\n\n";
265     if ( $subject eq "" )
266     {
267         if ( defined($report_name) and $report_name ne "")
268         {
269             $subject = $report_name ;
270         }
271         else
272         {
273             $subject = 'Koha Saved Report';
274         }
275     }
276
277     # convert SQL parameters to placeholders
278     my $params_needed = ( $sql =~ s/(<<[^>]+>>)/\?/g );
279     die("You supplied ". scalar @params . " parameter(s) and $params_needed are required by the report") if scalar @params != $params_needed;
280
281     my ($sth) = execute_query(
282         {
283             sql        => $sql,
284             sql_params => \@params,
285             report_id  => $report_id,
286         }
287     );
288     my $count = scalar($sth->rows);
289     unless ($count) {
290         print "NO OUTPUT: 0 results from execute_query\n";
291         next;
292     }
293     $verbose and print "$count results from execute_query\n";
294
295     my $message;
296     my @rows_to_store;
297     if ($format eq 'html') {
298         my $cgi = CGI->new();
299         my @rows;
300         while (my $line = $sth->fetchrow_arrayref) {
301             foreach (@$line) { defined($_) or $_ = ''; }    # catch undef values, replace w/ ''
302             push @rows, $cgi->TR( join('', $cgi->td($line)) ) . "\n";
303             push @rows_to_store, [@$line] if $store_results;
304         }
305         $message = $cgi->table(join "", @rows);
306     } elsif ($format eq 'csv') {
307         my $csv = Text::CSV::Encoded->new({
308             encoding_out => 'utf8',
309             binary      => 1,
310             quote_char  => $quote,
311             sep_char    => $separator,
312             });
313
314         if ( $csv_header ) {
315             my @fields = map { decode( 'utf8', $_ ) } @{ $sth->{NAME} };
316             $csv->combine( @fields );
317             $message .= $csv->string() . "\n";
318             push @rows_to_store, [@fields] if $store_results;
319         }
320
321         while (my $line = $sth->fetchrow_arrayref) {
322             $csv->combine(@$line);
323             $message .= $csv->string() . "\n";
324             push @rows_to_store, [@$line] if $store_results;
325         }
326         $message = Encode::decode_utf8($message);
327     }
328     if ( $store_results ) {
329         my $json = to_json( \@rows_to_store );
330         C4::Reports::Guided::store_results( $report_id, $json );
331     }
332     if ($send_email) {
333
334         my $email = Koha::Email->new(
335             {
336                 to      => $to,
337                 from    => $from,
338                 subject => $subject,
339             }
340         );
341
342         if ( $format eq 'html' ) {
343             $message = "<html><head><style>tr:nth-child(2n+1) { background-color: #ccc;}</style></head><body>$message</body></html>";
344             $email->html_body($message);
345         }
346         else {
347             $email->text_body($message);
348         }
349
350         $email->attach(
351             Encode::encode_utf8($message),
352             content_type => "text/$format",
353             name         => "report$report_id-$date.$format",
354             disposition  => 'attachment',
355         ) if $attachment;
356
357         my $smtp_server = Koha::SMTP::Servers->get_default;
358         $smtp_server->set(
359             {
360                 user_name => $username,
361                 password  => $password,
362             }
363         )
364             if $username;
365
366         $email->transport( $smtp_server->transport );
367         try {
368             $email->send_or_die;
369         }
370         catch {
371             carp "Mail not sent: $_";
372         };
373     }
374     else {
375         print $message;
376     }
377 }