Fix the CSV export, provide it in UTF-8
[koha.git] / reports / guided_reports.pl
1 #!/usr/bin/perl
2
3 # Copyright 2007 Liblime ltd
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 use strict;
21 use CGI;
22 use C4::Reports;
23 use C4::Auth;
24 use C4::Output;
25 use C4::Dates;
26 use C4::Debug;
27
28 =head1 NAME
29
30 Script to control the guided report creation
31
32 =head1 DESCRIPTION
33
34
35 =over2
36
37 =cut
38
39 my $input = new CGI;
40 my $referer = $input->referer();
41
42 my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
43     {
44         template_name   => "reports/guided_reports_start.tmpl",
45         query           => $input,
46         type            => "intranet",
47         authnotrequired => 0,
48         flagsrequired   => { editcatalogue => 1 },
49         debug           => 1,
50     }
51 );
52
53 my $phase = $input->param('phase');
54 my $no_html = 0; # this will be set if we dont want to print out an html::template
55
56 if ( !$phase ) {
57     $template->param( 'start' => 1 );
58
59     # show welcome page
60 }
61
62 elsif ( $phase eq 'Build new' ) {
63
64     # build a new report
65     $template->param( 'build1' => 1 );
66
67     # get report areas
68     my $areas = C4::Reports::get_report_areas();
69     $template->param( 'areas' => $areas );
70
71 }
72
73 elsif ( $phase eq 'Used saved' ) {
74
75     # use a saved report
76     # get list of reports and display them
77     $template->param( 'saved1' => 1 );
78     my $reports = get_saved_reports();
79     $template->param( 'savedreports' => $reports );
80 }
81
82 elsif ( $phase eq 'Delete Saved') {
83         
84         # delete a report from the saved reports list
85         $no_html = 1;
86         my $id = $input->param('reports');
87         delete_report($id);
88     print $input->redirect("/cgi-bin/koha/reports/guided_reports.pl?phase=Used%20saved");
89         
90 }               
91
92 elsif ( $phase eq 'Show SQL'){
93         
94         my $id = $input->param('reports');
95         my $sql = get_sql($id);
96         $template->param(
97                 'sql' => $sql,
98                 'showsql' => 1,
99                 );
100 }
101
102 elsif ($phase eq 'retrieve results') {
103         my $id = $input->param('id');
104         my ($results,$name,$notes) = format_results($id);
105         # do something
106         $template->param(
107                 'retresults' => 1,
108                 'results' => $results,
109                 'name' => $name,
110                 'notes' => $notes,
111                 );
112         
113 }
114
115 elsif ( $phase eq 'Report on this Area' ) {
116
117     # they have choosen a new report and the area to report on
118     # get area
119     my $area = $input->param('areas');
120     $template->param(
121         'build2' => 1,
122         'area'   => $area
123     );
124
125     # get report types
126     my $types = C4::Reports::get_report_types();
127     $template->param( 'types' => $types );
128 }
129
130 elsif ( $phase eq 'Choose this type' ) {
131
132     # they have chosen type and area
133     # get area and type and pass them to the template
134     my $area = $input->param('area');
135     my $type = $input->param('types');
136     $template->param(
137         'build3' => 1,
138         'area'   => $area,
139         'type'   => $type,
140     );
141
142     # get columns
143     my $columns = get_columns($area,$input);
144     $template->param( 'columns' => $columns );
145 }
146
147 elsif ( $phase eq 'Choose these columns' ) {
148
149     # we now know type, area, and columns
150     # next step is the constraints
151     my $area    = $input->param('area');
152     my $type    = $input->param('type');
153     my @columns = $input->param('columns');
154     my $column  = join( ',', @columns );
155         my $definitions = get_from_dictionary($area);
156     $template->param(
157         'build4' => 1,
158         'area'   => $area,
159         'type'   => $type,
160         'column' => $column,
161     );
162     my $criteria = get_criteria($area,$input);
163     $template->param( 'criteria' => $criteria,
164         'definitions' => $definitions);
165 }
166
167 elsif ( $phase eq 'Choose these criteria' ) {
168     my $area     = $input->param('area');
169     my $type     = $input->param('type');
170     my $column   = $input->param('column');
171         my @definitions = $input->param('definition');
172         my $definition = join (',',@definitions);
173     my @criteria = $input->param('criteria_column');
174         my $query_criteria;
175     foreach my $crit (@criteria) {
176         my $value = $input->param( $crit . "_value" );
177         if ($value) {
178             if ($value =~ C4::Dates->regexp(C4::Context->preference('dateformat'))) { 
179                 my $date = C4::Dates->new($value);
180                 $value = $date->output("iso");
181             }
182             $query_criteria .= " AND $crit='$value'";
183         }
184     }
185
186     $template->param(
187         'build5'         => 1,
188         'area'           => $area,
189         'type'           => $type,
190         'column'         => $column,
191         'definition'     => $definition,
192         'criteriastring' => $query_criteria,
193     );
194
195     # get columns
196     my @columns = split( ',', $column );
197     my @total_by;
198
199     # build structue for use by tmpl_loop to choose columns to order by
200     # need to do something about the order of the order :)
201         # we also want to use the %columns hash to get the plain english names
202     foreach my $col (@columns) {
203         my %total;
204         $total{'name'} = $col;
205         my @selects;
206         my %select1;
207         $select1{'value'} = 'sum';
208         push @selects, \%select1;
209         my %select2;
210         $select2{'value'} = 'min';
211         push @selects, \%select2;
212         my %select3;
213         $select3{'value'} = 'max';
214         push @selects, \%select3;
215         my %select4;
216         $select4{'value'} = 'avg';
217         push @selects, \%select4;
218         my %select5;
219         $select5{'value'} = 'count';
220         push @selects, \%select5;
221
222         $total{'select'} = \@selects;
223         push @total_by, \%total;
224     }
225
226     $template->param( 'total_by' => \@total_by );
227 }
228
229 elsif ( $phase eq 'Choose These Operations' ) {
230     my $area     = $input->param('area');
231     my $type     = $input->param('type');
232     my $column   = $input->param('column');
233     my $criteria = $input->param('criteria');
234         my $definition = $input->param('definition');
235     my @total_by = $input->param('total_by');
236     my $totals;
237     foreach my $total (@total_by) {
238         my $value = $input->param( $total . "_tvalue" );
239         $totals .= "$value($total),";
240     }
241
242     $template->param(
243         'build6'         => 1,
244         'area'           => $area,
245         'type'           => $type,
246         'column'         => $column,
247         'criteriastring' => $criteria,
248         'totals'         => $totals,
249         'definition'    => $definition,
250     );
251
252     # get columns
253     my @columns = split( ',', $column );
254     my @order_by;
255
256     # build structue for use by tmpl_loop to choose columns to order by
257     # need to do something about the order of the order :)
258     foreach my $col (@columns) {
259         my %order;
260         $order{'name'} = $col;
261         my @selects;
262         my %select1;
263         $select1{'value'} = 'asc';
264         push @selects, \%select1;
265         my %select2;
266         $select2{'value'} = 'desc';
267         push @selects, \%select2;
268         $order{'select'} = \@selects;
269         push @order_by, \%order;
270     }
271
272     $template->param( 'order_by' => \@order_by );
273 }
274
275 elsif ( $phase eq 'Build Report' ) {
276
277     # now we have all the info we need and can build the sql
278     my $area     = $input->param('area');
279     my $type     = $input->param('type');
280     my $column   = $input->param('column');
281     my $crit     = $input->param('criteria');
282     my $totals   = $input->param('totals');
283         my $definition = $input->param('definition');
284 #    my @criteria = split( ',', $crit );
285     my $query_criteria=$crit;
286     # split the columns up by ,
287     my @columns = split( ',', $column );
288     my @order_by = $input->param('order_by');
289
290     my $query_orderby;
291     foreach my $order (@order_by) {
292         my $value = $input->param( $order . "_ovalue" );
293         if ($query_orderby) {
294             $query_orderby .= ",$order $value";
295         }
296         else {
297             $query_orderby = " ORDER BY $order $value";
298         }
299     }
300
301     # get the sql
302     my $sql =
303       build_query( \@columns, $query_criteria, $query_orderby, $area, $totals, $definition );
304     $template->param(
305         'showreport' => 1,
306         'sql'        => $sql,
307         'type'       => $type
308     );
309 }
310
311 elsif ( $phase eq 'Save' ) {
312         # Save the report that has just been built
313     my $sql  = $input->param('sql');
314     my $type = $input->param('type');
315     $template->param(
316         'save' => 1,
317         'sql'  => $sql,
318         'type' => $type
319     );
320 }
321
322 elsif ( $phase eq 'Save Report' ) {
323     # save the sql pasted in by a user 
324     my $sql  = $input->param('sql');
325     my $name = $input->param('reportname');
326     my $type = $input->param('types');
327     my $notes = $input->param('notes');
328     my @errors = ();
329     my $error = {};
330     if ($sql =~ /;?\W?(UPDATE|DELETE|DROP|INSERT|SHOW|CREATE)\W/i) {
331         $error->{'sqlerr'} = $1;
332         push @errors, $error;
333     }
334     elsif ($sql !~ /^(SELECT)/i) {
335         $error->{'queryerr'} = 1;
336         push @errors, $error;
337     }
338     if (@errors) {
339         $template->param(
340             'save_successful'       => 1,
341             'errors'    => \@errors,
342             'sql'       => $sql,
343             'reportname'=> $name,
344             'type'      => $type,
345             'notes'     => $notes,
346         );
347     }
348     else {
349         save_report( $sql, $name, $type, $notes );
350         $template->param(
351             'save_successful'       => 1,
352         );
353     }
354 }
355
356 # This condition is not used currently
357 #elsif ( $phase eq 'Execute' ) {
358 #    # run the sql, and output results in a template    
359 #    my $sql     = $input->param('sql');
360 #    my $type    = $input->param('type');
361 #    my ($results, $total, $errors) = execute_query($sql, $type);
362 #    $template->param(
363 #        'results' => $results,
364 #        'sql' => $sql,
365 #        'execute' => 1,
366 #    );
367 #}
368
369 elsif ($phase eq 'Run this report'){
370     # execute a saved report
371     # FIXME The default limit should not be hardcoded...
372     my $limit = 20;
373     my $offset;
374     my $report = $input->param('reports');
375     # offset algorithm
376     if ($input->param('page')) {
377         $offset = ($input->param('page') - 1) * 20;
378     }
379     else {
380         $offset = 0;
381     }
382     my ($sql,$type,$name,$notes) = get_saved_report($report);
383     my ($results, $total, $errors) = execute_query($sql, $type, $offset, $limit);
384     my $totpages = int($total/$limit) + (($total % $limit) > 0 ? 1 : 0);
385     my $url = "/cgi-bin/koha/reports/guided_reports.pl?reports=$report&phase=Run%20this%20report";
386     $template->param(
387         'results'       => $results,
388         'sql'           => $sql,
389         'execute'       => 1,
390         'name'          => $name,
391         'notes'         => $notes,
392         'pagination_bar' => pagination_bar($url, $totpages, $input->param('page'), "page"),
393         'errors'        => $errors,
394     );
395 }       
396
397 elsif ($phase eq 'Export'){
398     binmode STDOUT, ':utf8';
399
400         # export results to tab separated text
401         my $sql = $input->param('sql');
402         my $format = $input->param('format');
403         my ($results, $total, $errors) = execute_query($sql,1,0,0,$format);
404         if ($#$errors == -1) {
405             $no_html=1;
406             print $input->header(       -type => 'application/octet-stream',
407                                         -attachment=>'reportresults.csv'
408                                 );
409             print $results;
410         } else {
411             $template->param(
412                 'results'       => $results,
413                 'sql'           => $sql,
414                 'execute'       => 1,
415                 'name'          => 'Error exporting report!',
416                 'notes'         => '',
417                 'pagination_bar' => '',
418                 'errors'        => $errors,
419             );
420         }
421 }
422
423 elsif ($phase eq 'Create report from SQL') {
424         # allow the user to paste in sql
425         if ($input->param('sql')) {
426             $template->param(
427                 'sql'           => $input->param('sql'),
428                 'reportname'    => $input->param('reportname'),
429                 'notes'         => $input->param('notes'),
430             );
431         }
432         $template->param('create' => 1);
433         my $types = C4::Reports::get_report_types();
434         if (my $type = $input->param('type')) {
435             for my $i ( 0 .. $#{@$types}) {
436                 @$types[$i]->{'selected'} = 1 if @$types[$i]->{'id'} eq $type;
437             }
438         }
439         $template->param( 'types' => $types ); 
440 }
441
442 elsif ($phase eq 'Create Compound Report'){
443         my $reports = get_saved_reports();  
444         $template->param( 'savedreports' => $reports,
445                 'compound' => 1,
446         );
447 }
448
449 elsif ($phase eq 'Save Compound'){
450     my $master = $input->param('master');
451         my $subreport = $input->param('subreport');
452 #       my $compound_report = create_compound($master,$subreport);
453 #       my $results = run_compound($compound_report);
454         my ($mastertables,$subtables) = create_compound($master,$subreport);
455         $template->param( 'save_compound' => 1,
456                 master=>$mastertables,
457                 subsql=>$subtables
458         );
459 }
460
461
462 $template->param(   'referer' => $referer,
463                     'DHTMLcalendar_dateformat' => C4::Dates->DHTMLcalendar(),
464                 );
465
466
467 if (!$no_html){
468         output_html_with_http_headers $input, $cookie, $template->output;
469 }