eb50d658255420901981dc2e611cd07c47d67e1c
[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::Guided;
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   => { reports => 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 = 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 = 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     binmode STDOUT, ':utf8';
371
372     # execute a saved report
373     # FIXME The default limit should not be hardcoded...
374     my $limit = 20;
375     my $offset;
376     my $report = $input->param('reports');
377     # offset algorithm
378     if ($input->param('page')) {
379         $offset = ($input->param('page') - 1) * 20;
380     }
381     else {
382         $offset = 0;
383     }
384     my ($sql,$type,$name,$notes) = get_saved_report($report);
385     my ($results, $total, $errors) = execute_query($sql, $type, $offset, $limit);
386     my $totpages = int($total/$limit) + (($total % $limit) > 0 ? 1 : 0);
387     my $url = "/cgi-bin/koha/reports/guided_reports.pl?reports=$report&phase=Run%20this%20report";
388     $template->param(
389         'results'       => $results,
390         'sql'           => $sql,
391         'execute'       => 1,
392         'name'          => $name,
393         'notes'         => $notes,
394         'pagination_bar' => pagination_bar($url, $totpages, $input->param('page'), "page"),
395         'errors'        => $errors,
396     );
397 }       
398
399 elsif ($phase eq 'Export'){
400     binmode STDOUT, ':utf8';
401
402         # export results to tab separated text
403         my $sql = $input->param('sql');
404         my $format = $input->param('format');
405         my ($results, $total, $errors) = execute_query($sql,1,0,0,$format);
406         if ($#$errors == -1) {
407             $no_html=1;
408             print $input->header(       -type => 'application/octet-stream',
409                                         -attachment=>'reportresults.csv'
410                                 );
411             print $results;
412         } else {
413             $template->param(
414                 'results'       => $results,
415                 'sql'           => $sql,
416                 'execute'       => 1,
417                 'name'          => 'Error exporting report!',
418                 'notes'         => '',
419                 'pagination_bar' => '',
420                 'errors'        => $errors,
421             );
422         }
423 }
424
425 elsif ($phase eq 'Create report from SQL') {
426         # allow the user to paste in sql
427         if ($input->param('sql')) {
428             $template->param(
429                 'sql'           => $input->param('sql'),
430                 'reportname'    => $input->param('reportname'),
431                 'notes'         => $input->param('notes'),
432             );
433         }
434         $template->param('create' => 1);
435         my $types = get_report_types();
436         if (my $type = $input->param('type')) {
437             for my $i ( 0 .. $#{@$types}) {
438                 @$types[$i]->{'selected'} = 1 if @$types[$i]->{'id'} eq $type;
439             }
440         }
441         $template->param( 'types' => $types ); 
442 }
443
444 elsif ($phase eq 'Create Compound Report'){
445         my $reports = get_saved_reports();  
446         $template->param( 'savedreports' => $reports,
447                 'compound' => 1,
448         );
449 }
450
451 elsif ($phase eq 'Save Compound'){
452     my $master = $input->param('master');
453         my $subreport = $input->param('subreport');
454 #       my $compound_report = create_compound($master,$subreport);
455 #       my $results = run_compound($compound_report);
456         my ($mastertables,$subtables) = create_compound($master,$subreport);
457         $template->param( 'save_compound' => 1,
458                 master=>$mastertables,
459                 subsql=>$subtables
460         );
461 }
462
463
464 $template->param(   'referer' => $referer,
465                     'DHTMLcalendar_dateformat' => C4::Dates->DHTMLcalendar(),
466                 );
467
468
469 if (!$no_html){
470         output_html_with_http_headers $input, $cookie, $template->output;
471 }