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