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