first cut of the new clark
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Thu, 28 Sep 2006 08:09:38 +0000 (08:09 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Thu, 28 Sep 2006 08:09:38 +0000 (08:09 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@6246 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/reporter/clark-kent.pl

index fb6f0f1..233e24a 100755 (executable)
@@ -10,39 +10,53 @@ use DateTime;
 use DateTime::Format::ISO8601;
 use JSON;
 use Data::Dumper;
-use OpenILS::WWW::Reporter::transforms;
 use Text::CSV_XS;
 use Spreadsheet::WriteExcel::Big;
 use OpenSRF::EX qw/:try/;
 use OpenSRF::Utils qw/:daemon/;
 use OpenSRF::Utils::Logger qw/:level/;
+use OpenSRF::System;
+use OpenSRF::AppSession;
+use OpenSRF::Utils::SettingsClient;
+use OpenILS::Reporter::SQLBuilder;
 use POSIX;
 use GD::Graph::pie;
 use GD::Graph::bars3d;
 use GD::Graph::lines3d;
+use Tie::IxHash;
 
 use open ':utf8';
 
 
-my ($base_xml, $count, $daemon) = ('/openils/conf/reporter.xml', 1);
+my ($count, $config, $lockfile, $daemon) = (1, '/openils/conf/bootstrap.conf', '/tmp/reporter-LOCK');
 
 GetOptions(
-       "file=s"        => \$base_xml,
        "daemon"        => \$daemon,
        "concurrency=i" => \$count,
+       "boostrap=s"    => \$config,
+       "lockfile=s"    => \$lockfile,
 );
 
-my $parser = XML::LibXML->new;
-$parser->expand_xinclude(1);
+if (-e $lockfile) {
+       die "I seem to be running already. If not remove $lockfile, try again\n";
+}
+
+open(F, ">$lockfile");
+print F $$;
+close F;
 
-my $doc = $parser->parse_file($base_xml);
+OpenSRF::System->bootstrap_client( config_file => $config );
 
-my $db_driver = $doc->findvalue('/reporter/setup/database/driver');
-my $db_host = $doc->findvalue('/reporter/setup/database/host');
-my $db_port = $doc->findvalue('/reporter/setup/database/port') || '5432';
-my $db_name = $doc->findvalue('/reporter/setup/database/name');
-my $db_user = $doc->findvalue('/reporter/setup/database/user');
-my $db_pw = $doc->findvalue('/reporter/setup/database/password');
+# XXX Get this stuff from the settings server
+my $sc = OpenSRF::Utils::SettingsClient->new;
+my $db_driver = $sc->config_value( reporter => setup => database => 'driver' );
+my $db_host = $sc->config_value( reporter => setup => database => 'host' );
+my $db_port = $sc->config_value( reporter => setup => database => 'port' );
+my $db_name = $sc->config_value( reporter => setup => database => 'name' );
+my $db_user = $sc->config_value( reporter => setup => database => 'user' );
+my $db_pw = $sc->config_value( reporter => setup => database => 'password' );
+
+my $output_base = $sc->config_value( reporter => setup => files => 'output_base' );
 
 my $dsn = "dbi:" . $db_driver . ":dbname=" . $db_name .';host=' . $db_host . ';port=' . $db_port;
 
@@ -55,34 +69,12 @@ DAEMON:
 $dbh = DBI->connect($dsn,$db_user,$db_pw, {pg_enable_utf8 => 1, RaiseError => 1});
 
 $current_time = DateTime->from_epoch( epoch => time() )->strftime('%FT%T%z');
-# Move new reports into the run queue
-$dbh->do(<<'SQL', {}, $current_time);
-INSERT INTO reporter.output ( stage3, state ) 
-       SELECT  id, 'wait'
-         FROM  reporter.stage3 
-         WHERE runtime <= $1
-               AND NOT disable
-               AND (   (       recurrence = '0 seconds'::INTERVAL
-                               AND (
-                                       id NOT IN ( SELECT stage3 FROM reporter.output )
-                                       OR rerun IS TRUE
-                               )
-                       )
-                       OR (    recurrence > '0 seconds'::INTERVAL
-                               AND id NOT IN (
-                                       SELECT  stage3
-                                         FROM  reporter.output
-                                         WHERE state <> 'complete')
-                       )
-               )
-         ORDER BY runtime;
-SQL
 
 # make sure we're not already running $count reports
 ($running) = $dbh->selectrow_array(<<SQL);
 SELECT count(*)
-  FROM reporter.output
-  WHERE        state = 'running';
+  FROM reporter.schedule
+  WHERE        start_time IS NOT NULL AND complete_time IS NULL;
 SQL
 
 if ($count <= $running) {
@@ -102,9 +94,9 @@ $run = $count - $running;
 
 $sth = $dbh->prepare(<<SQL);
 SELECT *
-  FROM reporter.output
-  WHERE        state = 'wait'
-  ORDER BY queue_time
+  FROM reporter.schedule
+  WHERE        start_time IS NULL AND run_time < NOW()
+  ORDER BY run_time
   LIMIT $run;
 SQL
 
@@ -112,18 +104,21 @@ $sth->execute;
 
 @reports = ();
 while (my $r = $sth->fetchrow_hashref) {
-       my $s3 = $dbh->selectrow_hashref(<<"    SQL", {}, $r->{stage3});
-               SELECT * FROM reporter.stage3 WHERE id = ?;
+       my $s3 = $dbh->selectrow_hashref(<<"    SQL", {}, $r->{report});
+               SELECT * FROM reporter.report WHERE id = ?;
        SQL
 
-       my $s2 = $dbh->selectrow_hashref(<<"    SQL", {}, $s3->{stage2});
-               SELECT * FROM reporter.stage2 WHERE id = ?;
+       my $s2 = $dbh->selectrow_hashref(<<"    SQL", {}, $s3->{template});
+               SELECT * FROM reporter.template WHERE id = ?;
        SQL
 
-       $s3->{stage2} = $s2;
-       $r->{stage3} = $s3;
+       $s3->{template} = $s2;
+       $r->{report} = $s3;
 
-       generate_query( $r );
+       my $b = OpenILS::Reporter::SQLBuilder->new;
+       $b->register_params( JSON->JSON2perl( $r->{report}->{data} ) );
+
+       $r->{resultset} = $b->parse_report( JSON->JSON2perl( $r->{report}->{template}->{data} ) );
        push @reports, $r;
 }
 
@@ -137,86 +132,90 @@ for my $r ( @reports ) {
        next if (safe_fork());
 
        # This is the child (runner) process;
-       my $p = JSON->JSON2perl( $r->{stage3}->{params} );
-       daemonize("Clark Kent reporting: $p->{reportname}");
+       daemonize("Clark Kent reporting: $r->{report}->{name}");
 
        $dbh = DBI->connect($dsn,$db_user,$db_pw, {pg_enable_utf8 => 1, RaiseError => 1});
 
        try {
-               $dbh->do(<<'            SQL',{}, $r->{sql}->{'select'}, $$, $r->{id});
-                       UPDATE  reporter.output
-                         SET   state = 'running',
-                               run_time = 'now',
-                               query = ?,
-                               run_pid = ?
+               $dbh->do(<<'            SQL',{}, $r->{id});
+                       UPDATE  reporter.schedule
+                         SET   start_time = 'now',
                          WHERE id = ?;
                SQL
 
-               my ($runtime) = $dbh->selectrow_array("SELECT run_time FROM reporter.output WHERE id = ?",{},$r->{id});
-               $r->{run_time} = $runtime;
-
-               $sth = $dbh->prepare($r->{sql}->{'select'});
+               $sth = $dbh->prepare($r->{resultset}->toSQL);
 
-               $sth->execute(@{ $r->{sql}->{'bind'} });
+               $sth->execute;
                $r->{data} = $sth->fetchall_arrayref;
 
-               pivot_data($r);
+               $r->{column_labels} = [$r->{resultset}->column_label_list];
 
-               my $base = $doc->findvalue('/reporter/setup/files/output_base');
-               my $s1 = $r->{stage3}->{stage2}->{stage1};
-               my $s2 = $r->{stage3}->{stage2}->{id};
-               my $s3 = $r->{stage3}->{id};
-               my $output = $r->{id};
+               if ($r->{resultset}->pivot_data && $r->{resultset}->pivot_label) {
+                       my @labels = $r->{resultset}->column_label_list;
+                       my $newdata = pivot_data(
+                               { columns => $r->{column_labels}, data => $r->{data}},
+                               $r->{resultset}->pivot_label,
+                               $r->{resultset}->pivot_data,
+                               $r->{resultset}->pivot_default
+                       );
 
-               mkdir($base);
-               mkdir("$base/$s1");
-               mkdir("$base/$s1/$s2");
-               mkdir("$base/$s1/$s2/$s3");
-               mkdir("$base/$s1/$s2/$s3/$output");
-       
-               my @formats;
-               if (ref $p->{output_format}) {
-                       @formats = @{ $p->{output_format} };
-               } else {
-                       @formats = ( $p->{output_format} );
+                       $r->{column_labels} = $newdata->{columns};
+                       $r->{data} = $newdata->{data};
                }
+
+               my $s2 = $r->{report}->{template}->{id};
+               my $s3 = $r->{report}->{id};
+               my $output = $r->{id};
+
+               mkdir($output_base);
+               mkdir("$output_base/$s2");
+               mkdir("$output_base/$s2/$s3");
+               mkdir("$output_base/$s2/$s3/$output");
        
-               if ( grep { $_ eq 'csv' } @formats ) {
-                       build_csv("$base/$s1/$s2/$s3/$output/report-data.csv", $r);
+               my $output_dir = "$output_base/$s2/$s3/$output";
+
+               if ( $r->{csv_format} eq 't') {
+                       build_csv("$output_dir/report-data.csv", $r);
                }
 
-               if ( grep { $_ eq 'excel' } @formats ) {
-                       build_excel("$base/$s1/$s2/$s3/$output/report-data.xls", $r);
+               if ( $r->{excel_format} eq 't') {
+                       build_excel("$output_dir/report-data.xls", $r);
                }
 
-               if ( grep { $_ eq 'html' } @formats ) {
-                       mkdir("$base/$s1/$s2/$s3/$output/html");
-                       build_html("$base/$s1/$s2/$s3/$output/report-data.html", $r);
+               if ( $r->{html_format} eq 't') {
+                       mkdir("$output_dir/html");
+                       build_html("$output_dir/report-data.html", $r);
                }
 
 
                $dbh->begin_work;
-               #$dbh->do(<<'           SQL',{}, $r->{run_time}, $r->{stage3}->{id});
-               #       UPDATE  reporter.stage3
-               #         SET   runtime = CAST(? AS TIMESTAMP WITH TIME ZONE) + recurrence
-               #         WHERE id = ? AND recurrence > '0 seconds'::INTERVAL;
-               #SQL
-               $dbh->do(<<'            SQL',{}, $r->{stage3}->{id});
-                       UPDATE  reporter.stage3
-                         SET   runtime = runtime + recurrence
-                         WHERE id = ? AND recurrence > '0 seconds'::INTERVAL;
-               SQL
-               $dbh->do(<<'            SQL',{}, $r->{stage3}->{id});
-                       UPDATE  reporter.stage3
-                         SET   rerun = FALSE
-                         WHERE id = ? AND rerun = TRUE;
-               SQL
+
+               if ($r->{report}->{recur} eq 't') {
+                       my $sql = <<'                   SQL';
+                               INSERT INTO reporter.schedule ( report, folder, runner, run_time, email, csv_format, excel_format, html_format)
+                                       VALUES ( ?, ?, ?, NOW() + ?, ?, ?, ?, ? );
+                       SQL
+
+                       $dbh->do(
+                               $sql,
+                               {},
+                               $r->{report}->{id},
+                               $r->{folder},
+                               $r->{runner},
+                               $r->{report}->{recurance},
+                               $r->{email},
+                               $r->{csv_format},
+                               $r->{excel_format},
+                               $r->{html_format}
+                       );
+               }
+
                $dbh->do(<<'            SQL',{}, $r->{id});
-                       UPDATE  reporter.output
-                         SET   state = 'complete',
-                               complete_time = 'now'
+                       UPDATE  reporter.schedule
+                         SET   complete_time = 'now'
                          WHERE id = ?;
                SQL
+
                $dbh->commit;
 
 
@@ -224,11 +223,10 @@ for my $r ( @reports ) {
                my $e = shift;
                $dbh->rollback;
                $dbh->do(<<'            SQL',{}, $e, $r->{id});
-                       UPDATE  reporter.output
-                         SET   state = 'error',
-                               error_time = 'now',
-                               error = ?,
-                               run_pid = NULL
+                       UPDATE  reporter.schedule
+                         SET   error_text = ?,
+                               complete_time = 'now',
+                               error_code = 1,
                          WHERE id = ?;
                SQL
        };
@@ -247,126 +245,6 @@ if ($daemon) {
 
 #-------------------------------------------------------------------
 
-sub pivot_data {
-       my $r = shift;
-       my $p = JSON->JSON2perl( $r->{stage3}->{params} );
-       my $settings = $r->{sql};
-       my $data = $r->{data};
-
-       return unless (defined($settings->{pivot}));
-
-       my @groups = (map { ($_ - 1) } @{ $settings->{groupby} });
-       my @values = (0 .. (scalar(@{$settings->{columns}}) - 1));
-       splice(@values,$_,1) for (reverse @groups);
-
-       # remove pivot from group-by
-       my $count = 0;
-       my $pivot_groupby;
-       while ($count < scalar(@{$settings->{groupby}})) {
-               if (defined $pivot_groupby) {
-                       $settings->{groupby}->[$count] -= 1;
-                       if ($settings->{groupby}->[$count] >= $values[0] + 1) {
-                               $settings->{groupby}->[$count] -= 1;
-                       }
-               } elsif ($settings->{groupby}->[$count] == $settings->{pivot} + 1) {
-                       $pivot_groupby = $count;
-               }
-               $count++;
-       }
-
-
-       # grab positions of non-group-bys
-       @values = (0 .. (scalar(@{$settings->{columns}}) - 1));
-       splice(@values,$_,1) for (reverse @groups);
-       
-       # we're only doing one "value" for now, so grab that and remove from headings
-       my ($val_col) = @values;
-
-       my @remove_me = sort
-               { $b <=> $a }
-               ($val_col, $settings->{groupby}->[$pivot_groupby] - 1);
-
-       # get the groups-to-be
-       my @temp_groupby = @groups;
-       splice(@temp_groupby, $pivot_groupby, 1);
-
-       @groups = map { ($_ - 1) } @{ $settings->{groupby} };
-
-       my %p_header;
-       for my $row (@$data) {
-               $p_header{ $$row[$settings->{pivot}] } = [] unless exists($p_header{ $$row[$settings->{pivot}] });
-               
-               # add the header from this row's pivot
-               push @{ $p_header{ $$row[$settings->{pivot}] } },
-                       { val => $$row[$val_col], fp => join('', map { defined($_) ? $_ : '' } @$row[@temp_groupby]) };
-               
-               splice(@$row,$_,1) for (@remove_me);
-       }
-
-       push @{ $settings->{columns} }, sort keys %p_header;
-
-       # remove from headings;
-       splice(@{$settings->{columns}},$_,1) for (@remove_me);
-
-       # remove pivot from groupby
-       splice(@{$settings->{groupby}}, $pivot_groupby, 1);
-       @groups = (map { ($_ - 1) } @{ $settings->{groupby} });
-
-       my %seenit;
-       my @new_data;
-       {       no warnings;
-               for my $row (@$data) {
-
-                       my $fingerprint = join('',@$row[@groups]);
-                       next if $seenit{$fingerprint};
-
-                       $seenit{$fingerprint}++;
-
-                       for my $h ( sort keys %p_header ) {
-                               my $found = 0;
-                               my $bcount = 0;
-                               for my $blob (@{ $p_header{$h} }) {
-                                       $fingerprint = join('', map { defined($_) ? $_ : '' } @$row[@groups]);
-
-                                       if ($blob->{fp} eq $fingerprint ) {
-                                               push @$row, $blob->{val};
-                                               $found++;
-                                               splice(@{ $p_header{$h} }, $bcount, 1);
-                                               last;
-                                       }
-                                       $bcount++;
-                               }
-                               push @$row, 0 if (!$found);
-                       }
-
-                       push @new_data, [@$row];
-               }
-       }
-
-       @new_data = sort { data_sorter($a,$b,\@groups) } @new_data;
-
-       #replace old data with new
-       $r->{data} = \@new_data;
-
-}
-
-sub data_sorter {
-       no warnings;
-
-       my $_a = shift;
-       my $_b = shift;
-       my $sort_cols = shift;
-
-       for my $col (@$sort_cols) {
-               return -1 if (!defined($$_a[$col]));
-               return 1 if (!defined($$_b[$col]));
-
-               return -1 if ($$_a[$col] lt $$_b[$col]);
-               return 1 if ($$_a[$col] gt $$_b[$col]);
-       }
-       return 0;
-}
-
 sub build_csv {
        my $file = shift;
        my $r = shift;
@@ -377,7 +255,7 @@ sub build_csv {
        
        my $f = new FileHandle (">$file");
 
-       $csv->print($f, $r->{sql}->{columns});
+       $csv->print($f, $r->{column_labels});
        $csv->print($f, $_) for (@{$r->{data}});
 
        $f->close;
@@ -385,16 +263,14 @@ sub build_csv {
 sub build_excel {
        my $file = shift;
        my $r = shift;
-       my $p = JSON->JSON2perl( $r->{stage3}->{params} );
-
        my $xls = Spreadsheet::WriteExcel::Big->new($file);
 
-       my $sheetname = substr($p->{reportname},1,31);
+       my $sheetname = substr($r->{report}->{name},1,31);
        $sheetname =~ s/\W/_/gos;
        
        my $sheet = $xls->add_worksheet($sheetname);
 
-       $sheet->write_row('A1', $r->{sql}->{columns});
+       $sheet->write_row('A1', $r->{column_labels});
 
        $sheet->write_col('A2', $r->{data});
 
@@ -404,7 +280,6 @@ sub build_excel {
 sub build_html {
        my $file = shift;
        my $r = shift;
-       my $p = JSON->JSON2perl( $r->{stage3}->{params} );
 
        my $index = new FileHandle (">$file");
        my $raw = new FileHandle (">$file.raw.html");
@@ -413,7 +288,7 @@ sub build_html {
        print $index <<"        HEADER";
 <html>
        <head>
-               <title>$$p{reportname}</title>
+               <title>$$r{report}{name}</title>
                <style>
                        table { border-collapse: collapse; }
                        th { background-color: lightgray; }
@@ -422,15 +297,16 @@ sub build_html {
                </style>
        </head>
        <body>
-               <h2><u>$$p{reportname}</u></h2>
+               <h2><u>$$r{report}{name}</u></h2>
+               $$r{report}{description}<br/><br/><br/>
        HEADER
 
        
        # add a link to the raw output html
-       print $index "<a href='report-data.html.raw.html'>Raw output data</a><br/><br/><br/><br/>";
+       print $index "<a href='report-data.html.raw.html'>Tabular Output</a><br/><br/><br/><br/>";
 
        # create the raw output html file
-       print $raw "<html><head><title>$$p{reportname}</title>";
+       print $raw "<html><head><title>$$r{report}{name}</title>";
 
        print $raw <<'  CSS';
                <style>
@@ -444,47 +320,37 @@ sub build_html {
        print $raw "</head><body><table>";
 
        {       no warnings;
-               print $raw "<tr><th>".join('</th><th>',@{$r->{sql}->{columns}}).'</th></tr>';
-               print $raw "<tr><td>".join('</td><td>',@$_                    ).'</td></tr>' for (@{$r->{data}});
+               print $raw "<tr><th>".join('</th><th>',@{$r->{column_labels}}).'</th></tr>';
+               print $raw "<tr><td>".join('</td><td>',@$_                   ).'</td></tr>' for (@{$r->{data}});
        }
 
        print $raw '</table></body></html>';
        
        $raw->close;
 
-       # get the graph types
-       my @graphs;
-       if (ref $$p{html_graph_type}) {
-               @graphs = @{ $$p{html_graph_type} };
-       } else {
-               @graphs = ( $$p{html_graph_type} );
-       }
-
-       if ($graphs[0]) {
-               # Time for a pie chart
-               if (grep {$_ eq 'pie'} @graphs) {
-                       my $pics = draw_pie($r, $p, $file);
-                       for my $pic (@$pics) {
-                               print $index "<img src='report-data.html.$pic->{file}' alt='$pic->{name}'/><br/><br/><br/><br/>";
-                       }
+       # Time for a pie chart
+       if ($r->{chart_pie} eq 't') {
+               my $pics = draw_pie($r, $file);
+               for my $pic (@$pics) {
+                       print $index "<img src='report-data.html.$pic->{file}' alt='$pic->{name}'/><br/><br/><br/><br/>";
                }
+       }
 
-               print $index '<br/><br/><br/><br/>';
-               # Time for a bar chart
-               if (grep {$_ eq 'bar'} @graphs) {
-                       my $pics = draw_bars($r, $p, $file);
-                       for my $pic (@$pics) {
-                               print $index "<img src='report-data.html.$pic->{file}' alt='$pic->{name}'/><br/><br/><br/><br/>";
-                       }
+       print $index '<br/><br/><br/><br/>';
+       # Time for a bar chart
+       if ($r->{chart_bar} eq 't') {
+               my $pics = draw_bars($r, $file);
+               for my $pic (@$pics) {
+                       print $index "<img src='report-data.html.$pic->{file}' alt='$pic->{name}'/><br/><br/><br/><br/>";
                }
+       }
 
-               print $index '<br/><br/><br/><br/>';
-               # Time for a bar chart
-               if (grep {$_ eq 'line'} @graphs) {
-                       my $pics = draw_lines($r, $p, $file);
-                       for my $pic (@$pics) {
-                               print $index "<img src='report-data.html.$pic->{file}' alt='$pic->{name}'/><br/><br/><br/><br/>";
-                       }
+       print $index '<br/><br/><br/><br/>';
+       # Time for a bar chart
+       if ($r->{chart_line} eq 't') {
+               my $pics = draw_lines($r, $file);
+               for my $pic (@$pics) {
+                       print $index "<img src='report-data.html.$pic->{file}' alt='$pic->{name}'/><br/><br/><br/><br/>";
                }
        }
 
@@ -496,17 +362,16 @@ sub build_html {
 
 sub draw_pie {
        my $r = shift;
-       my $p = shift;
        my $file = shift;
+
        my $data = $r->{data};
-       my $settings = $r->{sql};
 
-       my @groups = (map { ($_ - 1) } @{ $settings->{groupby} });
+       my @groups = $r->{resultset}->group_by_list(0);
        
-       my @values = (0 .. (scalar(@{$settings->{columns}}) - 1));
+       my @values = (0 .. (scalar(@{$r->{column_labels}}) - 1));
        delete @values[@groups];
 
-       my $logo = $doc->findvalue('/reporter/setup/files/chart_logo');
+       #my $logo = $doc->findvalue('/reporter/setup/files/chart_logo');
        
        my @pics;
        for my $vcol (@values) {
@@ -540,12 +405,12 @@ sub draw_pie {
                        my $pic = new GD::Graph::pie;
 
                        $pic->set(
-                               label                   => $settings->{columns}->[$vcol],
+                               label                   => $r->{column_labels}->[$vcol],
                                start_angle             => 180,
                                legend_placement        => 'R',
-                               logo                    => $logo,
-                               logo_position           => 'TL',
-                               logo_resize             => 0.5,
+                               #logo                   => $logo,
+                               #logo_position          => 'TL',
+                               #logo_resize            => 0.5,
                                show_values             => 1,
                        );
 
@@ -569,7 +434,7 @@ sub draw_pie {
 
                        push @pics,
                                { file => "pie.$vcol.$sub_graph.$format",
-                                 name => $settings->{columns}->[$vcol].' (Pie)',
+                                 name => $r->{column_labels}->[$vcol].' (Pie)',
                                } unless ($forgetit);
 
                        last if ($sub_graph == $split);
@@ -582,17 +447,15 @@ sub draw_pie {
 
 sub draw_bars {
        my $r = shift;
-       my $p = shift;
        my $file = shift;
        my $data = $r->{data};
-       my $settings = $r->{sql};
 
-       my $logo = $doc->findvalue('/reporter/setup/files/chart_logo');
+       #my $logo = $doc->findvalue('/reporter/setup/files/chart_logo');
 
-       my @groups = (map { ($_ - 1) } @{ $settings->{groupby} });
+       my @groups = $r->{resultset}->group_by_list(0);
 
        
-       my @values = (0 .. (scalar(@{$settings->{columns}}) - 1));
+       my @values = (0 .. (scalar(@{$r->{column_labels}}) - 1));
        splice(@values,$_,1) for (reverse @groups);
 
        my @pic_data;
@@ -646,7 +509,7 @@ sub draw_bars {
        return [] unless ($new_data[0] && @{$new_data[0]});
 
        for my $col (@use_me) {
-               push @leg, $settings->{columns}->[$col + @groups - 1] if (map { 1 } grep { $col == $_ } @values);
+               push @leg, $r->{column_labels}->[$col + @groups - 1] if (map { 1 } grep { $col == $_ } @values);
        }
 
        my $w = 100 + 10 * scalar(@{$new_data[0]});
@@ -659,7 +522,7 @@ sub draw_bars {
        my $pic = new GD::Graph::bars3d ($w + 250, $h + 500);
 
        $pic->set(
-               title                   => $p->{reportname},
+               title                   => $r->{report}{name},
                x_labels_vertical       => 1,
                shading                 => 1,
                bar_depth               => 5,
@@ -667,9 +530,9 @@ sub draw_bars {
                y_max_value             => $max_y,
                legend_placement        => 'TR',
                boxclr                  => 'lgray',
-               logo                    => $logo,
-               logo_position           => 'R',
-               logo_resize             => 0.5,
+               #logo                   => $logo,
+               #logo_position          => 'R',
+               #logo_resize            => 0.5,
                show_values             => 1,
                overwrite               => 1,
        );
@@ -691,24 +554,21 @@ sub draw_bars {
        close IMG;
 
        return [{ file => "bar.$format",
-                 name => $p->{reportname}.' (Bar)',
+                 name => $r->{report}{name}.' (Bar)',
                }];
 
 }
 
 sub draw_lines {
        my $r = shift;
-       my $p = shift;
        my $file = shift;
        my $data = $r->{data};
-       my $settings = $r->{sql};
-
-       my $logo = $doc->findvalue('/reporter/setup/files/chart_logo');
 
-       my @groups = (map { ($_ - 1) } @{ $settings->{groupby} });
+       #my $logo = $doc->findvalue('/reporter/setup/files/chart_logo');
 
+       my @groups = $r->{resultset}->group_by_list(0);
        
-       my @values = (0 .. (scalar(@{$settings->{columns}}) - 1));
+       my @values = (0 .. (scalar(@{$r->{column_labels}}) - 1));
        splice(@values,$_,1) for (reverse @groups);
 
        my @pic_data;
@@ -753,7 +613,7 @@ sub draw_lines {
        }
 
        for my $col (@use_me) {
-               push @leg, $settings->{columns}->[$col + @groups - 1] if (map { 1 } grep { $col == $_ } @values);
+               push @leg, $r->{column_labels}->[$col + @groups - 1] if (map { 1 } grep { $col == $_ } @values);
        }
 
        my $w = 100 + 10 * scalar(@{$new_data[0]});
@@ -766,16 +626,16 @@ sub draw_lines {
        my $pic = new GD::Graph::lines3d ($w + 250, $h + 500);
 
        $pic->set(
-               title                   => $p->{reportname},
+               title                   => $r->{report}{name},
                x_labels_vertical       => 1,
                shading                 => 1,
                line_depth              => 5,
                y_max_value             => $max_y,
                legend_placement        => 'TR',
                boxclr                  => 'lgray',
-               logo                    => $logo,
-               logo_position           => 'R',
-               logo_resize             => 0.5,
+               #logo                   => $logo,
+               #logo_position          => 'R',
+               #logo_resize            => 0.5,
                show_values             => 1,
                overwrite               => 1,
        );
@@ -797,199 +657,67 @@ sub draw_lines {
        close IMG;
 
        return [{ file => "line.$format",
-                 name => $p->{reportname}.' (Bar)',
+                 name => $r->{report}{name}.' (Bar)',
                }];
 
 }
 
-sub table_by_id {
-       my $id = shift;
-       my ($node) = $doc->findnodes("//*[\@id='$id']");
-       if ($node && $node->findvalue('@table')) {
-               ($node) = $doc->findnodes("//*[\@id='".$node->getAttribute('table')."']");
-       }
-       return $node;
-}
 
-sub generate_query {
-       my $r = shift;
+sub pivot_data {
+       my $blob = shift;
+       my $pivot_label = shift;
+       my $pivot_data = shift;
+       my $default = shift;
+       $default = 0 unless (defined $default);
 
-       my $p = JSON->JSON2perl( $r->{stage3}->{params} );
+       my $data = $$blob{data};
+       my $cols = $$blob{columns};
 
-       my @group_by = ();
-       my @aggs = ();
-       my $core = $r->{stage3}->{stage2}->{stage1};
-       my @dims = ();
+       my @keep_labels =  @$cols;
+       splice(@keep_labels, $_ - 1, 1) for (reverse sort ($pivot_label, $pivot_data));
 
-       for my $t (keys %{$$p{filter}}) {
-               if ($t ne $core) {
-                       push @dims, $t;
-               }
-       }
+       my @keep_cols = (0 .. @$cols - 1);
+       splice(@keep_cols, $_ - 1, 1) for (reverse sort ($pivot_label, $pivot_data));
 
-       for my $t (keys %{$$p{output}}) {
-               if ($t ne $core && !(grep { $t eq $_ } @dims) ) {
-                       push @dims, $t;
-               }
-       }
-
-       my @dim_select = ();
-       my @dim_from = ();
-       for my $d (@dims) {
-               my $t = table_by_id($d);
-               my $t_name = $t->findvalue('tablename');
-               push @dim_from, "$t_name AS \"$d\""
-                       unless ( grep {$_ eq "$t_name AS \"$d\""} @dim_from );
-
-               my $k = $doc->findvalue("//*[\@id='$d']/\@key");
-               push @dim_select, "\"$d\".\"$k\" AS \"${d}_${k}\""
-                       unless ( grep {$_ eq "\"$d\".\"$k\" AS \"${d}_${k}\""} @dim_select );
-
-               for my $c ( keys %{$$p{output}{$d}} ) {
-                       push @dim_select, "\"$d\".\"$c\" AS \"${d}_${c}\""
-                               unless ( grep {$_ eq "\"$d\".\"$c\" AS \"${d}_${c}\""} @dim_select );
-               }
-
-               for my $c ( keys %{$$p{filter}{$d}} ) {
-                       next if (exists $$p{output}{$d}{$c});
-                       push @dim_select, "\"$d\".\"$c\" AS \"${d}_${c}\""
-                               unless ( grep {$_ eq "\"$d\".\"$c\" AS \"${d}_${c}\""} @dim_select );
-               }
+       #first, find the unique list of pivot values
+       my %tmp;
+       for my $row (@$data) {
+               $tmp{ $$row[$pivot_label - 1] } = 1;
        }
+       my @new_cols = sort keys %tmp;
 
-       my $d_select =
-               '(SELECT ' . join(',', @dim_select) .
-               '  FROM ' . join(',', @dim_from) . ') AS dims';
-       
-       my @opord = ();
-       if (ref $$p{output_order}) {
-               @opord = @{ $$p{output_order} };
-       } else {
-               @opord = ( $$p{output_order} );
-       }
-       my @output_order = map { { (split ':')[1] => (split ':')[2] } } @opord;
-       my @p_col = split(':',$p->{pivot_col}) if $p->{pivot_col};
-       my $pivot = undef;
-
-       my $col = 1;
-       my @groupby = ();
-       my @output = ();
-       my @dim_col_names = ();
-       my @columns = ();
-       my @join = ();
-       my @join_base = ();
-       for my $pair (@output_order) {
-               my ($t_name) = keys %$pair;
-               my $t = $t_name;
-
-               $t_name = "dims" if ($t ne $core);
-
-               my $t_node = table_by_id($t);
-
-               for my $c ( values %$pair ) {
-                       my $label = $t_node->findvalue("fields/field[\@name='$c']/label");
-
-                       my $full_col = $c;
-                       $full_col = "${t}_${c}" if ($t ne $t_name);
-                       $full_col = "\"$t_name\".\"$full_col\"";
-
-                       if (my $xform_type = $$p{xform}{type}{$t}{$c}) {
-                               my $xform = $$OpenILS::WWW::Reporter::dtype_xforms{$xform_type};
-                               if ($xform->{group}) {
-                                       push @groupby, $col;
-                               }
-                               $label = "$$xform{label} -- $label";
-
-                               my $tmp = $xform->{'select'};
-                               $tmp =~ s/\?COLNAME\?/$full_col/gs;
-                               $tmp =~ s/\?PARAM\?/$$p{xform}{param}{$t}{$c}/gs;
-                               $full_col = $tmp;
-                       } else {
-                               push @groupby, $col;
-                       }
+       tie my %split_data, 'Tie::IxHash';
+       for my $row (@$data) {
 
-                       push @output, "$full_col AS \"$label\"";
-                       push @columns, $label;
-                       $pivot = scalar(@columns) - 1 if (@p_col && $t eq $p_col[1] && $c eq $p_col[2]);
-                       $col++;
-               }
+               my $row_fp = ''. join('', map { defined($$row[$_]) ? $$row[$_] : '' } @keep_cols);
+               $split_data{$row_fp} ||= [];
 
-               if ($t ne $t_name && (!@join_base || !grep{$t eq $_}@join_base)) {
-                       my $k = $doc->findvalue("//*[\@id='$t']/\@key");
-                       my $f = $doc->findvalue("//*[\@id='$t']/\@field");
-                       push @join, "dims.\"${t}_${k}\" = \"$core\".\"$f\"";
-                       push @join_base, $t;
-               }
+               push @{ $split_data{$row_fp} }, $row;
        }
 
-       my @where = ();
-       my @bind = ();
-       for my $t ( keys %{$$p{filter}} ) {
-               my $t_name = $t;
-               $t_name = "dims" if ($t ne $core);
-
-               my $t_node = table_by_id($t);
-
-               for my $c ( keys %{$$p{filter}{$t}} ) {
-                       my $label = $t_node->findvalue("fields/field[\@name='$c']/label");
 
-                       my $full_col = $c;
-                       $full_col = "${t}_${c}" if ($t ne $t_name);
-                       $full_col = "\"$t_name\".\"$full_col\"";
+       #now loop over the data, building a new result set
+       tie my %new_data, 'Tie::IxHash';
 
-                       my ($fam) = keys %{ $$p{filter}{$t}{$c} };
-                       my ($w) = keys %{ $$p{filter}{$t}{$c}{$fam} };
-                       my $val = $$p{filter}{$t}{$c}{$fam}{$w};
+       for my $fp ( keys %split_data ) {
 
-                       my $filter_code_xpath = "/reporter/widgets/widget-family[\@name='$fam']/widget[\@name='$w']/filter-code[\@type='perl']";
-                       if (my $widget_code = $doc->findvalue($filter_code_xpath)) { # widget supplys it's own filter code
-                               my ($where_clause, $bind_list) = ('',[]);
+               $new_data{$fp} = [];
 
-                               eval $widget_code;
-
-                               die "$@\n\n$widget_code" if ($@);
-
-                               push @where, $where_clause;
-                               push @bind, @$bind_list;
-
-                       } elsif (ref $val) {
-                               push @where, "$full_col IN (".join(",",map {'?'}@$val).")";
-                               push @bind, @$val;
-                       } else {
-                               push @where, "$full_col = ?";
-                               push @bind, $val;
-                       }
+               for my $col (@keep_cols) {
+                       push @{ $new_data{$fp} }, $split_data{$fp}[0][$col];
                }
 
-               if ($t ne $t_name && (!@join_base || !grep{$t eq $_}@join_base)) {
-                       my $k = $doc->findvalue("//*[\@id='$t']/\@key");
-                       my $f = $doc->findvalue("//*[\@id='$t']/\@field");
-                       push @join, "dims.\"${t}_${k}\" = \"$core\".\"$f\"";
-                       push @join_base, $t;
+               for my $col (@new_cols) {
+
+                       my ($datum) = map { $_->[$pivot_data - 1] } grep { $_->[$pivot_label - 1] eq $col } @{ $split_data{$fp} };
+                       $datum ||= $default;
+                       push @{ $new_data{$fp} }, $datum;
                }
        }
 
-       my $t = table_by_id($core)->findvalue('tablename');
-
-       my $from = " FROM $t AS \"$core\" ";
-       $from .= "RIGHT JOIN $d_select ON (". join(' AND ', @join).")" if ( @join );
-
-       my $select =
-               "SELECT ".join(',', @output). $from;
+       push @keep_labels, @new_cols;
 
-       $select .= ' WHERE '.join(' AND ', @where) if (@where);
-       $select .= ' GROUP BY '.join(',',@groupby) if (@groupby);
-
-       $r->{sql}->{'pivot'}    = $pivot;
-       $r->{sql}->{'select'}   = $select;
-       $r->{sql}->{'bind'}     = \@bind;
-       $r->{sql}->{columns}    = \@columns;
-       $r->{sql}->{groupby}    = \@groupby;
-       
+       return { columns => \@keep_labels, data => [ values %new_data ] };
 }
 
 
-
-
-
-