953a46a088
This patch removes a hardcoded 'LIMIT 20' which was added to all report queries thus limiting all reports to only the first twenty rows of applicable data. In its place this patch introduces code to paginate through all applicable data, regardless of how many rows are available. The code will also honor any user defined 'LIMIT' in reports based on SQL entered directly by the user. This patch also adds column labels to 'tab' and 'csv' files generated by reports. NOTE: Only user defined 'LIMIT's apply to 'tab,' 'csv,' and 'text' files. Signed-off-by: Galen Charlton <galen.charlton@liblime.com> Signed-off-by: Joshua Ferraro <jmf@liblime.com>
419 lines
11 KiB
Perl
Executable file
419 lines
11 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
# Copyright 2007 Liblime ltd
|
|
#
|
|
# This file is part of Koha.
|
|
#
|
|
# Koha is free software; you can redistribute it and/or modify it under the
|
|
# terms of the GNU General Public License as published by the Free Software
|
|
# Foundation; either version 2 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
|
|
# Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
use strict;
|
|
use CGI;
|
|
use C4::Reports;
|
|
use C4::Auth;
|
|
use C4::Output;
|
|
use C4::Dates qw( DHTMLcalendar );
|
|
use C4::Debug;
|
|
|
|
=head1 NAME
|
|
|
|
Script to control the guided report creation
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
|
=over2
|
|
|
|
=cut
|
|
|
|
my $input = new CGI;
|
|
my $referer = $input->referer();
|
|
|
|
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
|
|
{
|
|
template_name => "reports/guided_reports_start.tmpl",
|
|
query => $input,
|
|
type => "intranet",
|
|
authnotrequired => 0,
|
|
flagsrequired => { editcatalogue => 1 },
|
|
debug => 1,
|
|
}
|
|
);
|
|
|
|
my $phase = $input->param('phase');
|
|
my $no_html = 0; # this will be set if we dont want to print out an html::template
|
|
|
|
if ( !$phase ) {
|
|
$template->param( 'start' => 1 );
|
|
|
|
# show welcome page
|
|
}
|
|
|
|
elsif ( $phase eq 'Build new' ) {
|
|
|
|
# build a new report
|
|
$template->param( 'build1' => 1 );
|
|
|
|
# get report areas
|
|
my $areas = C4::Reports::get_report_areas();
|
|
$template->param( 'areas' => $areas );
|
|
|
|
}
|
|
|
|
elsif ( $phase eq 'Used saved' ) {
|
|
|
|
# use a saved report
|
|
# get list of reports and display them
|
|
$template->param( 'saved1' => 1 );
|
|
my $reports = get_saved_reports();
|
|
$template->param( 'savedreports' => $reports );
|
|
}
|
|
|
|
elsif ( $phase eq 'Delete Saved') {
|
|
|
|
# delete a report from the saved reports list
|
|
$no_html = 1;
|
|
my $id = $input->param('reports');
|
|
delete_report($id);
|
|
print $input->redirect("/cgi-bin/koha/reports/guided_reports.pl?phase=Used%20saved");
|
|
|
|
}
|
|
|
|
elsif ( $phase eq 'Show SQL'){
|
|
|
|
my $id = $input->param('reports');
|
|
my $sql = get_sql($id);
|
|
$template->param(
|
|
'sql' => $sql,
|
|
'showsql' => 1,
|
|
);
|
|
}
|
|
|
|
elsif ($phase eq 'retrieve results') {
|
|
my $id = $input->param('id');
|
|
my ($results,$name,$notes) = format_results($id);
|
|
# do something
|
|
$template->param(
|
|
'retresults' => 1,
|
|
'results' => $results,
|
|
'name' => $name,
|
|
'notes' => $notes,
|
|
);
|
|
|
|
}
|
|
|
|
elsif ( $phase eq 'Report on this Area' ) {
|
|
|
|
# they have choosen a new report and the area to report on
|
|
# get area
|
|
my $area = $input->param('areas');
|
|
$template->param(
|
|
'build2' => 1,
|
|
'area' => $area
|
|
);
|
|
|
|
# get report types
|
|
my $types = C4::Reports::get_report_types();
|
|
$template->param( 'types' => $types );
|
|
}
|
|
|
|
elsif ( $phase eq 'Choose this type' ) {
|
|
|
|
# they have chosen type and area
|
|
# get area and type and pass them to the template
|
|
my $area = $input->param('area');
|
|
my $type = $input->param('types');
|
|
$template->param(
|
|
'build3' => 1,
|
|
'area' => $area,
|
|
'type' => $type,
|
|
);
|
|
|
|
# get columns
|
|
my $columns = get_columns($area,$input);
|
|
$template->param( 'columns' => $columns );
|
|
}
|
|
|
|
elsif ( $phase eq 'Choose these columns' ) {
|
|
|
|
# we now know type, area, and columns
|
|
# next step is the constraints
|
|
my $area = $input->param('area');
|
|
my $type = $input->param('type');
|
|
my @columns = $input->param('columns');
|
|
my $column = join( ',', @columns );
|
|
my $definitions = get_from_dictionary($area);
|
|
$template->param(
|
|
'build4' => 1,
|
|
'area' => $area,
|
|
'type' => $type,
|
|
'column' => $column,
|
|
);
|
|
my $criteria = get_criteria($area,$input);
|
|
$template->param( 'criteria' => $criteria,
|
|
'definitions' => $definitions);
|
|
}
|
|
|
|
elsif ( $phase eq 'Choose these criteria' ) {
|
|
my $area = $input->param('area');
|
|
my $type = $input->param('type');
|
|
my $column = $input->param('column');
|
|
my @definitions = $input->param('definition');
|
|
my $definition = join (',',@definitions);
|
|
my @criteria = $input->param('criteria_column');
|
|
my $query_criteria;
|
|
foreach my $crit (@criteria) {
|
|
my $value = $input->param( $crit . "_value" );
|
|
if ($value) {
|
|
if ($value =~ C4::Dates->regexp(C4::Context->preference('dateformat'))) {
|
|
my $date = C4::Dates->new($value);
|
|
$value = $date->output("iso");
|
|
}
|
|
$query_criteria .= " AND $crit='$value'";
|
|
}
|
|
}
|
|
|
|
$template->param(
|
|
'build5' => 1,
|
|
'area' => $area,
|
|
'type' => $type,
|
|
'column' => $column,
|
|
'definition' => $definition,
|
|
'criteriastring' => $query_criteria,
|
|
);
|
|
|
|
# get columns
|
|
my @columns = split( ',', $column );
|
|
my @total_by;
|
|
|
|
# build structue for use by tmpl_loop to choose columns to order by
|
|
# need to do something about the order of the order :)
|
|
# we also want to use the %columns hash to get the plain english names
|
|
foreach my $col (@columns) {
|
|
my %total;
|
|
$total{'name'} = $col;
|
|
my @selects;
|
|
my %select1;
|
|
$select1{'value'} = 'sum';
|
|
push @selects, \%select1;
|
|
my %select2;
|
|
$select2{'value'} = 'min';
|
|
push @selects, \%select2;
|
|
my %select3;
|
|
$select3{'value'} = 'max';
|
|
push @selects, \%select3;
|
|
my %select4;
|
|
$select4{'value'} = 'avg';
|
|
push @selects, \%select4;
|
|
my %select5;
|
|
$select5{'value'} = 'count';
|
|
push @selects, \%select5;
|
|
|
|
$total{'select'} = \@selects;
|
|
push @total_by, \%total;
|
|
}
|
|
|
|
$template->param( 'total_by' => \@total_by );
|
|
}
|
|
|
|
elsif ( $phase eq 'Choose These Operations' ) {
|
|
my $area = $input->param('area');
|
|
my $type = $input->param('type');
|
|
my $column = $input->param('column');
|
|
my $criteria = $input->param('criteria');
|
|
my $definition = $input->param('definition');
|
|
my @total_by = $input->param('total_by');
|
|
my $totals;
|
|
foreach my $total (@total_by) {
|
|
my $value = $input->param( $total . "_tvalue" );
|
|
$totals .= "$value($total),";
|
|
}
|
|
|
|
$template->param(
|
|
'build6' => 1,
|
|
'area' => $area,
|
|
'type' => $type,
|
|
'column' => $column,
|
|
'criteriastring' => $criteria,
|
|
'totals' => $totals,
|
|
'definition' => $definition,
|
|
);
|
|
|
|
# get columns
|
|
my @columns = split( ',', $column );
|
|
my @order_by;
|
|
|
|
# build structue for use by tmpl_loop to choose columns to order by
|
|
# need to do something about the order of the order :)
|
|
foreach my $col (@columns) {
|
|
my %order;
|
|
$order{'name'} = $col;
|
|
my @selects;
|
|
my %select1;
|
|
$select1{'value'} = 'asc';
|
|
push @selects, \%select1;
|
|
my %select2;
|
|
$select2{'value'} = 'desc';
|
|
push @selects, \%select2;
|
|
$order{'select'} = \@selects;
|
|
push @order_by, \%order;
|
|
}
|
|
|
|
$template->param( 'order_by' => \@order_by );
|
|
}
|
|
|
|
elsif ( $phase eq 'Build Report' ) {
|
|
|
|
# now we have all the info we need and can build the sql
|
|
my $area = $input->param('area');
|
|
my $type = $input->param('type');
|
|
my $column = $input->param('column');
|
|
my $crit = $input->param('criteria');
|
|
my $totals = $input->param('totals');
|
|
my $definition = $input->param('definition');
|
|
# my @criteria = split( ',', $crit );
|
|
my $query_criteria=$crit;
|
|
# split the columns up by ,
|
|
my @columns = split( ',', $column );
|
|
my @order_by = $input->param('order_by');
|
|
|
|
my $query_orderby;
|
|
foreach my $order (@order_by) {
|
|
my $value = $input->param( $order . "_ovalue" );
|
|
if ($query_orderby) {
|
|
$query_orderby .= ",$order $value";
|
|
}
|
|
else {
|
|
$query_orderby = " ORDER BY $order $value";
|
|
}
|
|
}
|
|
|
|
# get the sql
|
|
my $sql =
|
|
build_query( \@columns, $query_criteria, $query_orderby, $area, $totals, $definition );
|
|
$template->param(
|
|
'showreport' => 1,
|
|
'sql' => $sql,
|
|
'type' => $type
|
|
);
|
|
}
|
|
|
|
elsif ( $phase eq 'Save' ) {
|
|
# Save the report that has just been built
|
|
my $sql = $input->param('sql');
|
|
my $type = $input->param('type');
|
|
$template->param(
|
|
'save' => 1,
|
|
'sql' => $sql,
|
|
'type' => $type
|
|
);
|
|
}
|
|
|
|
elsif ( $phase eq 'Save Report' ) {
|
|
# save the sql pasted in by a user
|
|
my $sql = $input->param('sql');
|
|
my $name = $input->param('reportname');
|
|
my $type = $input->param('type');
|
|
my $notes = $input->param('notes');
|
|
save_report( $sql, $name, $type, $notes );
|
|
$template->param(
|
|
'save_successful' => 1,
|
|
);
|
|
}
|
|
|
|
# This condition is not used currently
|
|
#elsif ( $phase eq 'Execute' ) {
|
|
# # run the sql, and output results in a template
|
|
# my $sql = $input->param('sql');
|
|
# my $type = $input->param('type');
|
|
# my $results = execute_query($sql, $type);
|
|
# $template->param(
|
|
# 'results' => $results,
|
|
# 'sql' => $sql,
|
|
# 'execute' => 1,
|
|
# );
|
|
#}
|
|
|
|
elsif ($phase eq 'Run this report'){
|
|
# execute a saved report
|
|
# FIXME: The default limit should not be hardcoded...
|
|
my $limit = 20;
|
|
my $offset;
|
|
my $report = $input->param('reports');
|
|
# offset algorithm
|
|
if ($input->param('page')) {
|
|
$offset = ($input->param('page') - 1) * 20;
|
|
} else {
|
|
$offset = 0;
|
|
}
|
|
my ($sql,$type,$name,$notes) = get_saved_report($report);
|
|
my ($results, $total) = execute_query($sql, $type, $offset, $limit);
|
|
my $totpages = int($total/$limit) + (($total % $limit) > 0 ? 1 : 0);
|
|
my $url = "/cgi-bin/koha/reports/guided_reports.pl?reports=$report&phase=Run%20this%20report";
|
|
$template->param(
|
|
'results' => $results,
|
|
'sql' => $sql,
|
|
'execute' => 1,
|
|
'name' => $name,
|
|
'notes' => $notes,
|
|
'pagination_bar' => pagination_bar($url, $totpages, $input->param('page'), "page"),
|
|
);
|
|
}
|
|
|
|
elsif ($phase eq 'Export'){
|
|
# export results to tab separated text
|
|
my $sql = $input->param('sql');
|
|
$no_html=1;
|
|
print $input->header( -type => 'application/octet-stream',
|
|
-attachment=>'reportresults.csv');
|
|
my $format=$input->param('format');
|
|
my $results = execute_query($sql,1,0,0,$format);
|
|
print $results;
|
|
|
|
}
|
|
|
|
elsif ($phase eq 'Create report from SQL'){
|
|
# alllow the user to paste in sql
|
|
$template->param('create' => 1);
|
|
my $types = C4::Reports::get_report_types();
|
|
$template->param( 'types' => $types );
|
|
}
|
|
|
|
elsif ($phase eq 'Create Compound Report'){
|
|
my $reports = get_saved_reports();
|
|
$template->param( 'savedreports' => $reports,
|
|
'compound' => 1,
|
|
);
|
|
}
|
|
|
|
elsif ($phase eq 'Save Compound'){
|
|
my $master = $input->param('master');
|
|
my $subreport = $input->param('subreport');
|
|
# my $compound_report = create_compound($master,$subreport);
|
|
# my $results = run_compound($compound_report);
|
|
my ($mastertables,$subtables) = create_compound($master,$subreport);
|
|
$template->param( 'save_compound' => 1,
|
|
master=>$mastertables,
|
|
subsql=>$subtables
|
|
);
|
|
}
|
|
|
|
|
|
$template->param( 'referer' => $referer,
|
|
'DHTMLcalendar_dateformat' => C4::Dates->DHTMLcalendar(),
|
|
);
|
|
|
|
|
|
if (!$no_html){
|
|
output_html_with_http_headers $input, $cookie, $template->output;
|
|
}
|