fbd2644e494dda8072fe0a3e35885f9a42c03e47
[migration-tools.git] / mig-bin / mig-reporter
1 #!/usr/bin/perl
2
3 ###############################################################################
4 =pod
5
6 =item B<reporter> --analyst "Analyst Name" --report_title "Report Title"
7
8 Generates an asciidoc file in the git working directory that can be converted to 
9 any appropriate format.  The analyst and report parameters are required.
10
11 Optional parameters are : 
12
13 --added_page_title and --added_page_file 
14
15 If one is used both must be.  The added page file can be plain text or asciidoc.  This
16 adds an extra arbitrary page of notes to the report.  Mig assumes the page file is in the mig git directory.
17
18 --tags
19
20 This will define a set of tags to use, if not set it will default to Circs, 
21 Holds, Actors, Bibs, Assets & Money. 
22
23 --debug on
24
25 Gives more information about what is happening. Defaults to off.
26
27 --reports_xml 
28
29 Allows you to override the default evergreen_staged_report.xml in the mig-xml folder.
30
31 --captions on OR --captions off 
32
33 Adds the captions tag to asciidoc header to turn off captions in generated output.
34 Defaults to off.
35
36 =back
37
38 =cut
39
40 ###############################################################################
41
42 use strict;
43 use warnings;
44
45 use DBI;
46 use Data::Dumper;
47 use XML::LibXML;
48 use Env qw(
49     HOME PGHOST PGPORT PGUSER PGDATABASE MIGSCHEMA
50     MIGBASEWORKDIR MIGBASEGITDIR MIGGITDIR MIGWORKDIR
51 );
52 use Pod::Usage;
53 use Switch;
54 use Getopt::Long; 
55 use Cwd 'abs_path';
56 use Cwd qw(getcwd);
57 use FindBin;
58 my $mig_bin = "$FindBin::Bin/";
59 use lib "$FindBin::Bin/";
60 use Mig;
61 use open ':encoding(utf8)';
62
63 pod2usage(-verbose => 2) if defined $ARGV[0] && $ARGV[0] eq '--help';
64 pod2usage(-verbose => 1) if ! $ARGV[1];
65
66 my $analyst = 'Equinox Open Library Initiative';;
67 my $report_title;
68 my $reports_xml = 'evergreen_staged_report.xml';
69 my $tags;
70 my $added_page_title;
71 my $added_page_file;
72 my $captions = 'off';  
73 my $i = 0;
74 my $parser = XML::LibXML->new();
75 my $lines_per_page = 42;
76 my $debug = 'off';
77 my $workbook;
78 my $fh;
79
80 my $ret = GetOptions(
81     'analyst:s'           => \$analyst,
82     'report_title:s'      => \$report_title,
83     'title:s'             => \$report_title,
84     'reports_xml:s'       => \$reports_xml,
85     'tags:s'              => \$tags,
86     'added_page_title:s'  => \$added_page_title,
87     'added_page_file:s'   => \$added_page_file,
88     'captions:s'          => \$captions,
89         'debug:s'             => \$debug
90 );
91
92 if (!defined $tags) {$tags = 'circs.holds.actors.bibs.assets.money.notices'};
93 if (!defined $report_title) { abort('--report_title or --title must be supplied'); }
94 if (!defined $analyst) { abort('--analyst must be supplied'); }
95
96 my $mig_path = abs_path($0);
97 $mig_path =~ s|[^/]+$||;
98 $reports_xml = find_xml($reports_xml,$mig_path);
99 if (!defined $reports_xml) { abort("Can not find xml reports file."); }
100 my $dom = $parser->parse_file($reports_xml);
101
102 if (defined $added_page_file or defined $added_page_title) {
103     abort('must specify --added_page_file and --added_page_title') unless defined $added_page_file and defined $added_page_title;
104     }
105 if (defined $added_page_file) { $added_page_file = $MIGGITDIR . $added_page_file; }
106
107 my $dbh = Mig::db_connect();
108 my $report_file = create_report_name($report_title);
109 $report_file = $MIGGITDIR . $report_file;
110
111 open($fh, '>', $report_file) or abort("Could not open output file $report_file!");
112 write_title_page($report_title,$fh,$analyst,$captions);
113
114 if (defined $added_page_file and defined $added_page_title) { 
115     print $fh "<<<\n";
116     print $fh "== $added_page_title\n";
117     print "$added_page_file\t$added_page_title\n";
118     open(my $an,'<:encoding(UTF-8)', $added_page_file) or abort("Could not open $added_page_file!");
119     while ( my $line = <$an> ) {
120         print $fh $line;
121     }
122     print $fh "\n";
123     close $an;
124 }
125
126 foreach my $func ($dom->findnodes('//function')) {
127     my $fdrop = $func->findvalue('./drop');
128     my $fcreate = $func->findvalue('./create');    
129     my $fname = $func->findvalue('./name');
130     my $sdrop = $dbh->prepare($fdrop);
131     my $screate = $dbh->prepare($fcreate);
132     print "dropping function $fname ... ";
133     $sdrop->execute();
134     print "creating function $fname\n\n";
135     $screate->execute();
136 }
137
138 foreach my $table ($dom->findnodes('//table')) {
139     my $tdrop = $table->findvalue('./drop');
140     my $tcreate = $table->findvalue('./create');
141     my $tname = $table->findvalue('./name');
142     my $sdrop = $dbh->prepare($tdrop);
143     my $screate = $dbh->prepare($tcreate);
144     print "dropping table $tname ... ";
145     $sdrop->execute();
146     print "creating table $tname\n\n";
147     $screate->execute();
148 }
149
150 $tags = lc($tags);
151 my @report_tags = split(/\./,$tags);
152 foreach my $t (@report_tags) {
153     print "\n\n=========== Starting to process tag $t\n";
154     print   "==========================================\n\n";
155
156     my @asset_files;
157     foreach my $asset ($dom->findnodes('//asset')) {
158         if (index($asset->findvalue('./tag'),$t) != -1) {
159             push @asset_files, $asset->findvalue('./file');
160         }
161     }
162
163     foreach my $fname (@asset_files) {
164         my $asset_path = $mig_path . '../mig-asc/' . $fname;
165         open my $a, $asset_path or abort("Could not open $fname.");
166         while ( my $l = <$a> ) {
167             print $fh $l;
168         }
169     print $fh "<<<\n";
170     }
171
172     print_section_header(ucfirst($t),$fh); 
173     my $linecount = $lines_per_page;
174     my $r;
175
176     undef @asset_files;
177     foreach my $asset ($dom->findnodes('//asset')) {
178         if (index($asset->findvalue('./tag'),$t) != -1) {
179             push @asset_files, $asset->findvalue('./file');
180         }
181     }
182
183     my @report_names;
184     foreach my $report ($dom->findnodes('//report')) {
185         if (index($report->findvalue('./tag'),$t) != -1 and $report->findvalue('./iteration') eq '0') {
186             push @report_names, $report->findvalue('./name');
187         }
188     }
189
190     #only has one level of failover now but could change to array of hashes and loops
191     #but this keeps it simple and in practice I haven't needed more than two
192     
193
194     foreach my $rname (@report_names) {
195         my %report0;
196         my %report1;
197         my $check_tables0;
198         my $check_tables1;
199
200         if ($debug eq 'on') {print "\nchecking for $rname ... ";}
201         %report0 = find_report($dom,$t,$rname,'0',$debug);
202         $check_tables0 = check_table($report0{query},$MIGSCHEMA,$debug,$rname);
203         if ($check_tables0 == 1) { $r =  print_query($fh,%report0); } else {
204                 %report1 = find_report($dom,$t,$rname,'1',$debug);
205             if (defined $report1{query}) {
206                 $check_tables1 = check_table($report1{query},$MIGSCHEMA,$debug,$rname);
207                 if ($check_tables1 == 1) { $r = print_query($fh,%report1); }
208             }
209         }
210     }
211     
212 }
213
214 print "\n";
215
216 foreach my $table ($dom->findnodes('//table')) {
217     my $tdrop = $table->findvalue('./drop');
218     my $tname = $table->findvalue('./name');
219     my $sdrop = $dbh->prepare($tdrop);
220     print "cleaning up table $tname ... \n";
221     $sdrop->execute();
222 }
223
224 close $fh;
225
226 ############ end of main logic
227
228 sub find_xml {
229     my $reports_xml = shift;
230     my $mig_path = shift;
231
232     if ($reports_xml =~ m/\//) { return $reports_xml; }
233
234     my $mig_test_file =  $mig_path . '/../mig-xml/' . $reports_xml;
235     my $working_test_dir = getcwd();
236     my $working_test_file = $working_test_dir . '/' . $reports_xml;
237
238     if (-e $mig_test_file) { return $mig_test_file; }
239     if (-e $working_test_file) { return $working_test_file; }
240
241     return undef;
242 }
243
244 sub find_report {
245     my $dom = shift;
246     my $tag = shift;
247     my $name = shift;
248     my $iteration = shift;
249     my $debug = shift;
250     my %report;
251
252     if ($debug eq 'on') {print "iteration $iteration ";}
253     foreach my $node ($dom->findnodes('//report')) {
254         if ($node->findvalue('./tag') =~ $tag and $node->findvalue('./iteration') eq $iteration and $node->findvalue('./name') eq $name) {
255             if ($debug eq 'on') {print "succeeded ... \n";}
256             %report = (
257                 name => $node->findvalue('./name'),
258                 report_title => $node->findvalue('./report_title'),
259                 query => $node->findvalue('./query'),
260                 heading => $node->findvalue('./heading'),
261                 tag => $node->findvalue('./tag'),
262                 iteration => $node->findvalue('./iteration'),
263                 note => $node->findvalue('./note'),
264             );
265             return %report;
266         }
267     }
268     if ($debug eq 'on') {print "failed ... \n";}
269     return %report = (
270         name => "eaten by grue"
271     );
272 }
273
274 sub print_section_header {
275     my $t = shift;
276     my $fh = shift;
277
278     $t =~ s/_/ /g;
279     #$t =~ s/(\w+)/\u$1/g;;
280     print $fh "<<<\n";
281     print $fh "== $t Reports\n";
282     return;
283 }
284
285 sub create_report_name {
286     my $rt = shift;
287
288     my @abbr = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
289     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
290     $year += 1900;
291     my $date = $year . '_' . $abbr[$mon] . '_' . $mday;
292     my $report_file;
293     $report_file = $rt . ' ' . $date . '.asciidoc';
294     $report_file =~ s/ /_/g;
295     return $report_file;
296 }
297
298 sub write_title_page {
299     my $rt = shift;
300     my $fh = shift;
301     my $a = shift;
302     my $captions = shift;
303
304     my @abbr = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
305     my $l = length($report_title);
306     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
307     $year += 1900;
308     print $fh "= $rt\n"; 
309     print $fh "$mday $abbr[$mon] $year\n";
310     print $fh "$a\n";
311     #print $fh ":title-logo-image: image::eolilogosmall.png[pdfwidth=3in]\n";
312     print $fh ":toc:\n";
313     if ($captions eq 'on') { print $fh ":caption:\n"; }
314     print $fh "\n";
315 }
316
317 sub check_table {
318     my $query = shift;
319     my $MIGSCHEMA = shift;
320     my $debug = shift;
321     my $report_name = shift;
322
323     if ($debug eq 'on') {print "$query\n";}
324
325     my $i;
326     my $return_flag = 1;   
327     my @qe = split(/ /,$query);
328     $i = @qe;
329     $i--;
330     my @tables;
331     while ($i > -1) {
332         if ($qe[$i] eq 'FROM' or $qe[$i] eq 'JOIN') {
333             my $q = $i + 1;
334             if ($qe[$q] ne '(SELECT') {
335                 push @tables, $qe[$q];            
336             }
337         }
338         $i--;
339     }
340     if ($debug eq 'on') {print "checking tables ... ";}
341
342     $i = 0;
343     foreach my $table (@tables) {
344         my $sql;
345         my $schema;
346         if (index($table,'.') != -1) {
347             $schema = (split /\./,$table)[0];
348             $table = (split /\./,$table)[1];
349         }
350         $table = clean_query_string($table); 
351         if (defined $schema) {
352             $schema = clean_query_string($schema);
353             $sql = 'SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = \'' . $schema . '\' AND table_name = \'' . $table . '\');';
354         } else {
355             $sql = 'SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = \'' . $MIGSCHEMA . '\' AND table_name = \'' . $table . '\');';
356         }
357         my $sth = $dbh->prepare($sql);
358         $sth->execute();
359         while (my @row = $sth->fetchrow_array) {
360             if ($row[0] eq '1') {
361                     next;
362                 } else {
363                     $return_flag = 0;
364                     if ($debug eq 'on') {print "detecting $table failed...\n";}
365                 }
366             if ($row[0] eq '0') {$return_flag = 0;}
367         }
368     }
369     if ($return_flag == 1 and $debug eq 'on') {print "succeeded ...\n";}
370     if ($return_flag == 0) {print "! a table failed the find test for report $report_name\n\n";}
371     return $return_flag;
372 }
373
374 sub clean_query_string {
375     my $str = shift;
376     
377     $str =~ s/(?!_)[[:punct:]]//g; #remove punct except underscores
378     $str =~ s/\n//g;
379     $str =~ s/\r//g;
380     return $str;
381 }
382
383 sub print_query {
384     my $fh = shift;
385     my %report = @_;
386     my $query = $report{query};
387     my $sth = $dbh->prepare($query);
388     $sth->execute();
389
390     my $header_flag = 0;
391
392     while (my @row = $sth->fetchrow_array) {
393             if ($header_flag == 0) {
394                 print $fh "\n.*$report{report_title}*\n";
395                 print $fh "|===\n";
396                 my @h = split(/\./,$report{heading});
397                 my $h_length = @h;
398                 my $h_count = 1;
399                 while ($h_count <= $h_length) {
400                     print $fh "|*$h[$h_count-1]* ";
401                     $h_count++;
402                 }
403                 print $fh "\n";
404                 $header_flag = 1;
405             }
406             my $row_length = @row;
407             my $r = 1;
408             while ($r <= $row_length) {
409                 if (! defined $row[$r-1] ) {
410                     $row[$r-1] = 'none';
411                 }
412                 print $fh "|$row[$r-1] ";
413                 $r++;
414             }
415             print $fh "\n";
416         }
417     if ($header_flag == 1) { 
418         print $fh "|===\n\n"; 
419         print $fh $report{note};
420         print $fh "\n\n";
421     }
422     print "successfully wrote output for $report{name}.\n\n";
423 }
424
425 sub give_column {
426     my $i = shift;
427     my $col = "";
428
429     do {
430         $col .= chr( ( $i % 26 ) + ord('A') );
431         $i = int( $i / 26 ) - 1;
432     } while ( $i >= 0 );
433
434     return scalar reverse $col;
435 }
436
437 sub abort {
438     my $msg = shift;
439     print STDERR "$0: $msg", "\n";
440     exit 1;
441 }
442
443