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