Bug 35043: Do not have \n or \t appear in PO files
[koha.git] / reports / reserves_stats.pl
1 #!/usr/bin/perl
2
3 # Copyright 2010 BibLibre
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use CGI qw ( -utf8 );
23
24 use C4::Auth qw( get_template_and_user );
25 use C4::Context;
26 use C4::Koha qw( GetAuthorisedValues );
27 use C4::Output qw( output_html_with_http_headers );
28 use C4::Reports qw( GetDelimiterChoices );
29 use C4::Members;
30 use Koha::AuthorisedValues;
31 use Koha::DateUtils qw( dt_from_string output_pref );
32 use Koha::ItemTypes;
33 use Koha::Libraries;
34 use Koha::Patron::Categories;
35 use List::MoreUtils qw( any );
36
37 =head1 NAME
38
39 reports/reserve_stats.pl
40
41 =head1 DESCRIPTION
42
43 Plugin that shows reserve stats
44
45 =cut
46
47 my $input = CGI->new;
48 my $fullreportname = "reports/reserves_stats.tt";
49 my $do_it    = $input->param('do_it');
50 my $line     = $input->param("Line");
51 my $column   = $input->param("Column");
52 my $calc     = $input->param("Cellvalue");
53 my $output   = $input->param("output");
54 my $basename = $input->param("basename");
55 my $hash_params = $input->Vars;
56 my $filter_hashref;
57 foreach my $filter (grep {$_ =~/^filter/} keys %$hash_params){
58         my $filterstring=$filter;
59         $filterstring=~s/^filter_//g;
60         $$filter_hashref{$filterstring}=$$hash_params{$filter} if (defined $$hash_params{$filter} && $$hash_params{$filter} ne "");
61 }
62 my ($template, $borrowernumber, $cookie) = get_template_and_user({
63         template_name => $fullreportname,
64         query => $input,
65         type => "intranet",
66         flagsrequired => {reports => '*'},
67 });
68 our $sep = C4::Context->csv_delimiter(scalar $input->param("sep"));
69 $template->param(do_it => $do_it,
70 );
71
72 my @patron_categories = Koha::Patron::Categories->search_with_library_limits({}, {order_by => ['description']})->as_list;
73
74 my $locations = { map { ( $_->{authorised_value} => $_->{lib} ) } Koha::AuthorisedValues->get_descriptions_by_koha_field( { frameworkcode => '', kohafield => 'items.location' }, { order_by => ['description'] } ) };
75 my $ccodes = { map { ( $_->{authorised_value} => $_->{lib} ) } Koha::AuthorisedValues->get_descriptions_by_koha_field( { frameworkcode => '', kohafield => 'items.ccode' }, { order_by => ['description'] } ) };
76
77 my $Bsort1 = GetAuthorisedValues("Bsort1");
78 my $Bsort2 = GetAuthorisedValues("Bsort2");
79 my ($hassort1,$hassort2);
80 $hassort1=1 if $Bsort1;
81 $hassort2=1 if $Bsort2;
82
83
84 if ($do_it) {
85 # Displaying results
86         my $results = calculate($line, $column,  $calc, $filter_hashref);
87         if ($output eq "screen"){
88 # Printing results to screen
89                 $template->param(mainloop => $results);
90                 output_html_with_http_headers $input, $cookie, $template->output;
91         } else {
92 # Printing to a csv file
93         print $input->header(-type => 'application/vnd.sun.xml.calc',
94                             -encoding    => 'utf-8',
95                             -attachment=>"$basename.csv",
96                             -filename=>"$basename.csv" );
97                 my $cols  = @$results[0]->{loopcol};
98                 my $lines = @$results[0]->{looprow};
99 # header top-right
100                 print @$results[0]->{line} ."/". @$results[0]->{column} .$sep;
101 # Other header
102                 foreach my $col ( @$cols ) {
103                         print $col->{coltitle}.$sep;
104                 }
105                 print "Total\n";
106 # Table
107                 foreach my $line ( @$lines ) {
108                         my $x = $line->{loopcell};
109                         print $line->{rowtitle}.$sep;
110                         print map {$_->{value}.$sep} @$x;
111                         print $line->{totalrow}, "\n";
112                 }
113 # footer
114         print "TOTAL";
115         $cols = @$results[0]->{loopfooter};
116                 print map {$sep.$_->{totalcol}} @$cols;
117         print $sep.@$results[0]->{total};
118         }
119         exit; # exit either way after $do_it
120 }
121
122 my $dbh = C4::Context->dbh;
123
124 my $itemtypes = Koha::ItemTypes->search_with_localization;
125
126     # location list
127 my @locations;
128 foreach (sort keys %$locations) {
129         push @locations, { code => $_, description => "$_ - " . $locations->{$_} };
130 }
131     
132 my @ccodes;
133 foreach (sort {$ccodes->{$a} cmp $ccodes->{$b}} keys %$ccodes) {
134         push @ccodes, { code => $_, description => $ccodes->{$_} };
135 }
136
137 # various
138 my $CGIextChoice = ( 'CSV' ); # FIXME translation
139 my $CGIsepChoice=GetDelimiterChoices;
140  
141 $template->param(
142     categoryloop => \@patron_categories,
143     itemtypes => $itemtypes,
144         locationloop => \@locations,
145            ccodeloop => \@ccodes,
146         hassort1=> $hassort1,
147         hassort2=> $hassort2,
148         Bsort1 => $Bsort1,
149         Bsort2 => $Bsort2,
150         CGIextChoice => $CGIextChoice,
151         CGIsepChoice => $CGIsepChoice,
152 );
153 output_html_with_http_headers $input, $cookie, $template->output;
154
155 sub calculate {
156         my ($linefield, $colfield, $process, $filters_hashref) = @_;
157         my @loopfooter;
158         my @loopcol;
159         my @loopline;
160         my @looprow;
161         my %globalline;
162         my $grantotal =0;
163 # extract parameters
164         my $dbh = C4::Context->dbh;
165
166 # Filters
167 # Checking filters
168 #
169     my @loopfilter;
170     foreach my $filter ( keys %$filters_hashref ) {
171         $filters_hashref->{$filter} =~ s/\*/%/;
172         if ( $filter =~ /date/ ) {
173             $filters_hashref->{$filter} =
174                 eval { output_pref( { dt => dt_from_string( $filters_hashref->{$filter} ), dateonly => 1, dateformat => 'iso' }); };
175         }
176     }
177
178     #display
179     @loopfilter = map {
180         {
181             crit   => $_,
182             filter => (
183                 $_ =~ /date/
184                 ? eval { output_pref( { dt => dt_from_string( $filters_hashref->{$_} ), dateonly => 1 }); }
185                 : $filters_hashref->{$_}
186             )
187         }
188     } sort keys %$filters_hashref;
189
190
191
192
193         my $linesql=changeifreservestatus($linefield);
194         my $colsql=changeifreservestatus($colfield);
195         #Initialization of cell values.....
196
197         # preparing calculation
198     my $strcalc = "(SELECT $linesql line, $colsql col, ";
199         $strcalc .= ($process == 1) ? " COUNT(*)  calculation"                                 :
200                                         ($process == 2) ? "(COUNT(DISTINCT reserves.borrowernumber)) calculation"  :
201                                 ($process == 3) ? "(COUNT(DISTINCT reserves.itemnumber)) calculation"      : 
202                                 ($process == 4) ? "(COUNT(DISTINCT reserves.biblionumber)) calculation"    : '*';
203         $strcalc .= "
204         FROM (select * from reserves union select * from old_reserves) reserves
205         LEFT JOIN borrowers USING (borrowernumber)
206         ";
207         $strcalc .= "LEFT JOIN biblio ON reserves.biblionumber=biblio.biblionumber "
208         if ($linefield =~ /^biblio\./ or $colfield =~ /^biblio\./ or any {$_=~/biblio/}keys %$filters_hashref);
209         $strcalc .= "LEFT JOIN items ON reserves.itemnumber=items.itemnumber "
210         if ($linefield =~ /^items\./ or $colfield =~ /^items\./ or any {$_=~/items/}keys %$filters_hashref);
211
212         my @sqlparams;
213         my @sqlorparams;
214         my @sqlor;
215         my @sqlwhere;
216         foreach my $filter (keys %$filters_hashref){
217                 my $string;
218                 my $stringfield=$filter;
219                 $stringfield=~s/\_[a-z_]+$//;
220                 if ($filter=~/ /){
221                         $string=$stringfield;
222                 }
223                 elsif ($filter=~/_or/){
224                          push @sqlor, qq{( }.changeifreservestatus($filter)." = ? ) ";
225                          push @sqlorparams, $$filters_hashref{$filter};
226                 }
227                 elsif ($filter=~/_endex$/){
228                         $string = " $stringfield < ? ";
229                 }
230                 elsif ($filter=~/_end$/){
231                         $string = " $stringfield <= ? ";
232                 }
233                 elsif ($filter=~/_begin$/){
234                         $string = " $stringfield >= ? ";
235                 }
236                 else {
237                         $string = " $stringfield LIKE ? ";
238                 }
239                 if ($string){
240                         push @sqlwhere, $string;
241                         push @sqlparams, $$filters_hashref{$filter};
242                 }
243         }
244
245         $strcalc .= " WHERE ".join(" AND ",@sqlwhere) if (@sqlwhere);
246         $strcalc .= " AND (".join(" OR ",@sqlor).")" if (@sqlor);
247         $strcalc .= " GROUP BY line, col )";
248         my $dbcalc = $dbh->prepare($strcalc);
249         push @loopfilter, {crit=>'SQL =', sql=>1, filter=>$strcalc};
250         @sqlparams=(@sqlparams,@sqlorparams);
251         $dbcalc->execute(@sqlparams);
252         my $data = $dbcalc->fetchall_hashref([qw(line col)]);
253         my %cols_hash;
254         foreach my $row (keys %$data){
255                 push @loopline, $row;
256                 foreach my $col (keys %{$$data{$row}}){
257                         $$data{$row}{totalrow}+=$$data{$row}{$col}{calculation};
258                         $grantotal+=$$data{$row}{$col}{calculation};
259                         $cols_hash{$col}=1 ;
260                 }
261         }
262         my $urlbase="do_it=1&amp;".join("&amp;",map{"filter_$_=$$filters_hashref{$_}"} keys %$filters_hashref);
263         foreach my $row (sort @loopline) {
264                 my @loopcell;
265                 #@loopcol ensures the order for columns is common with column titles
266                 # and the number matches the number of columns
267                 foreach my $col (sort keys %cols_hash) {
268                         push @loopcell, {value =>( $$data{$row}{$col}{calculation} or ""),
269         #                                               url_complement=>($urlbase=~/&amp;$/?$urlbase."&amp;":$urlbase)."filter_$linefield=$row&amp;filter_$colfield=$col"
270                                                         }
271                 }
272                 push @looprow, {
273                         'rowtitle_display' => display_value($linefield,$row),
274                         'rowtitle' => $row,
275                         'loopcell' => \@loopcell,
276                         'totalrow' => $$data{$row}{totalrow}
277                 };
278         }
279         for my $col ( sort keys %cols_hash ) {
280                 my $total = 0;
281                 foreach my $row (@loopline) {
282                         $total += $data->{$row}{$col}{calculation} if $data->{$row}{$col}{calculation};
283                 }
284                 push @loopfooter, {'totalcol' => $total};
285                 push @loopcol, {'coltitle' => $col,
286                                                 coltitle_display=>display_value($colfield,$col)};
287         }
288         # the header of the table
289         $globalline{loopfilter}=\@loopfilter;
290         # the core of the table
291         $globalline{looprow} = \@looprow;
292         $globalline{loopcol} = \@loopcol;
293         #       # the foot (totals by borrower type)
294         $globalline{loopfooter} = \@loopfooter;
295         $globalline{total}  = $grantotal;
296         $globalline{line}   = $linefield;
297         $globalline{column} = $colfield;
298         return [(\%globalline)];
299 }
300
301 sub display_value {
302     my ( $crit, $value ) = @_;
303     my $locations = { map { ( $_->{authorised_value} => $_->{lib} ) } Koha::AuthorisedValues->get_descriptions_by_koha_field( { frameworkcode => '', kohafield => 'items.location' }, { order_by => ['description'] } ) };
304     my $ccodes = { map { ( $_->{authorised_value} => $_->{lib} ) } Koha::AuthorisedValues->get_descriptions_by_koha_field( { frameworkcode => '', kohafield => 'items.ccode' }, { order_by => ['description'] } ) };
305     my $Bsort1 = GetAuthorisedValues("Bsort1");
306     my $Bsort2 = GetAuthorisedValues("Bsort2");
307     my $display_value =
308         ( $crit =~ /ccode/ )         ? $ccodes->{$value}
309       : ( $crit =~ /location/ )      ? $locations->{$value}
310       : ( $crit =~ /itemtype/ )      ? Koha::ItemTypes->find( $value )->translated_description
311       : ( $crit =~ /branch/ )        ? Koha::Libraries->find($value)->branchname
312       : ( $crit =~ /reservestatus/ ) ? reservestatushuman($value)
313       :                                $value;    # default fallback
314     if ($crit =~ /sort1/) {
315         foreach (@$Bsort1) {
316             ($value eq $_->{authorised_value}) or next;
317             $display_value = $_->{lib} and last;
318         }
319     }
320     elsif ($crit =~ /sort2/) {
321         foreach (@$Bsort2) {
322             ($value eq $_->{authorised_value}) or next;
323             $display_value = $_->{lib} and last;
324         }
325     }
326     elsif ( $crit =~ /category/ ) {
327         my @patron_categories = Koha::Patron::Categories->search_with_library_limits({}, {order_by => ['description']})->as_list;
328         foreach my $patron_category ( @patron_categories ) {
329             ( $value eq $patron_category->categorycode ) or next;
330             $display_value = $patron_category->description and last;
331         }
332     }
333     return $display_value;
334 }
335
336 sub reservestatushuman{
337         my ($val)=@_;
338         my %hashhuman=(
339         1=>"1- placed",
340         2=>"2- processed",
341         3=>"3- pending",
342         4=>"4- satisfied",
343         5=>"5- cancelled",
344         6=>"6- not a status"
345         );
346         $hashhuman{$val};
347 }
348
349 sub changeifreservestatus{
350         my ($val)=@_;
351         ($val=~/reservestatus/
352                 ?$val=qq{ case 
353                                         when priority>0 then 1 
354                                         when priority=0 then
355                                                 (case 
356                                                    when found='f' then 4
357                                                    when found='w' then 
358                                                    (case 
359                                                     when cancellationdate is null then 3
360                                                         else 5
361                                                         end )
362                                                    else 2 
363                                                  end )
364                                     else 6 
365                                         end }
366                 :$val);
367 }