package Ernad::Indat::Sborn; use strict; use warnings; use base 'Ernad::Indat'; use Carp qw(confess); use Clone 'clone'; use Data::Dumper; use XML::LibXML; #use Ernad::Indat; use Ernad::Dates; use Ernad::Files; #use Ernad::FileInfo; #use Ernad::Store; use Ernad::Pages; use Krichel::Shoti; our $e=$main::e; ## prepare sborn joss sub issue { my $i=shift; my $what=shift // ''; if(not ($what eq 'complete' or $what eq 'in_labor')) { confess "I don't know what to do with what '$what'."; } #$i->clear(); $i->{'what'}=$what; ## this really is only for incremental processing at complete level, ## we don't really need it for in_labor processing. my $given_issuedate=shift // confess "I need this set here."; ## why main::e here? my $e=$main::e // $i->{'e'} ;; if(not $e) { confess "I don't see the erimp."; } #my $sborn_json=$i->{'dir'}->{'indat'}."/$what.json"; my $is_complete=1; my $issue_json; my $in_labor_json=$i->{'dir'}->{'issues'}.'/'.$given_issuedate.'.json'; my $complete_json=$i->{'dir'}->{'issues'}.'/complete/'.$given_issuedate.'.json'; if(-f $complete_json) { $e->echo(__LINE__,"I see $complete_json, I set issue_json to it."); $issue_json=$complete_json; } else { $is_complete=0; $issue_json=$in_labor_json; if($i->{'what'} eq 'complete') { $e->echo(__LINE__,"$given_issuedate does not belong into $what."); return 0; } } my $issue_json_age=-M $issue_json; ## let's delete the old data if($i->{$what}->{'d'}->{$given_issuedate}) { $e->echo(__LINE__,"I remove my $what data for $given_issuedate."); delete $i->{$what}->{'d'}->{$given_issuedate}; } ## this needs to go, otherwise we can't correct bad data in the file if(not $main::do_test) { my $sborn_json=$i->{'dir'}->{'indat'}."/$what.json"; my $sborn_age=$i->{'sborn_age'} // ''; if($sborn_age and $issue_json_age > $sborn_age) { $e->echo(__LINE__,"I don't renew $sborn_json over $issue_json for $what."); return 0; } } ## delete data for an issuedate that is no longer in_labor ## the adding in complete is done by the caller if($what eq 'in_labor') { if(-f $complete_json and not -f $in_labor_json) { $e->echo(__LINE__," I delete in_labor for $given_issuedate coz I see $issue_json."); delete $i->{'in_labor'}->{'d'}->{$given_issuedate}; # print Dumper $i->{$what}; $e->echo(__LINE__," I leave sborn_issue."); return 1; } } $e->echo(__LINE__,"I load $issue_json."); $i->{'i'}=&Ernad::Store::load($issue_json); $i->remove_namf_papids(); $i->remove_report_papids(); my $d=$i->{'i'}->{'d'}; $i->{$what}->{'d'}->{$given_issuedate}=clone($d); #print Dumper $i->{$what}->{'d'}; #delete $i->{'i'}; return 1; } sub needs_renewal { my $i=shift; my $what=shift; my $e=$i->{'e'}; my $sborn_json=$i->{'dir'}->{'indat'}."/$what.json"; my @dones=(); if($what eq 'complete') { my $glob=$i->{'dir'}->{'complete'}.'/*.json'; print "$glob\n"; ## no @dones=glob($glob) !! This is how we do it: push(@dones,glob($glob)); } elsif( $what eq 'in_labor') { my $glob=$i->{'dir'}->{'issues'}.'/*.json'; ## no @dones=glob($glob) !! This is how we do it: push(@dones,glob($glob)); } else { confess "I don't know what to do with what '$what'."; } if(&Ernad::Files::does_file_need_renewal($sborn_json,@dones)) { return 1; } $e->echo(__LINE__,"I don't renew $sborn_json of the $what json files."); return 0; } sub compile { my $i=shift; my $what=shift // ''; if(not $i->needs_renewal($what)) { return 1; } my $given_issuedate=shift // ''; my $e=$i->{'e'}; # $i->clear(); $i->{'what'}=$what; my $sborn_json=$i->{'dir'}->{'indat'}."/$what.json"; my $sborn_age; if(-f $sborn_json) { $sborn_age=-M $sborn_json; $i->{'sborn_age'}=$sborn_age; } if(not $i->{$what}) { if(-f $sborn_json) { $i->{$what}=&Ernad::Store::load($sborn_json); } else { $i->{$what}->{'d'}={}; $i->{$what}->{'v'}=&Krichel::Shoti::now; } } ## this really is only for incremental processing at complete level, ## we don't really need it for in_labor processing. my $issuedates=$i->known_issuedates($what); #print "what is $what\n"; #print Dumper $issuedates; #die; my $do_save=0; foreach my $issuedate (@$issuedates) { $e->echo(__LINE__,"I look at $issuedate.",10); if(-f $sborn_json) { if($given_issuedate and $issuedate ne $given_issuedate) { $e->echo(__LINE__,"I skip $issuedate because I look for $given_issuedate.",10); next; } } $e->echo(__LINE__,"I sborn $what work on $issuedate."); $do_save+=$i->issue($what,$issuedate); $e->echo(__LINE__,"I am done with sborn_issue for $what."); } if($do_save) { $e->echo(__LINE__,"I save i->{$what} to $sborn_json."); if($what eq 'in_labor') { $i->check_for_empty_report($what); } &Ernad::Store::save($i->{$what},$sborn_json); } else { $e->echo(__LINE__,"I don't save i->{$what} to $sborn_json."); } $e->echo(__LINE__,"I delete \$i->{$what}."); delete $i->{$what}; } ## delete empty report values. They come from the issue data as loaded. ## the issuedata appears to fix them, but the bad data appears to be stuck ## unless it is deleted here (hopefully). In despair waiting with chicken ## for Saipin 2018-07-14 sub check_for_empty_report { my $i=shift; my $what=shift; foreach my $issuedate (keys %{$i->{$what}->{'d'}}) { foreach my $repcode (keys %{$i->{$what}->{'d'}->{$issuedate}->{'report'}}) { my $report=$i->{$what}->{'d'}->{$issuedate}->{'report'}->{$repcode}; foreach my $type ('class','sent') { my $value=$report->{$type}; my $count_values=scalar keys %$value; if(not $count_values) { delete $i->{$what}->{'d'}->{$issuedate}->{'report'}->{$repcode}; last; confess "empty $type at $issuedate $repcode\n"; } #else { # confess "ok $count_values"; #} } } } } ## loads json sub load { my $i=shift; my $what=shift // confess "I need to load what?"; ## save to use with output of document $i->{'what'}=$what; my $e=$i->{'e'}; my $in_json=$i->{'dir'}->{'indat'}."/$what.json"; my $out_xml=$i->{'dir'}->{'indat'}."/$what.xml"; if(not $in_json) { confess "I don't know $what to get for you."; } my $data={}; if(-f $in_json) { $data=&Ernad::Store::load($in_json); if(not $data->{'d'}) { $e->echo(__LINE__,"The data in $in_json has no ->{'d'}"); $i->{'i'}->{'d'}=$data; } } else { $e->echo(__LINE__,"I don't see the file $in_json."); } $i->{'i'}=$data; } sub clean { my $i=shift; foreach my $date (keys %{$i->{'i'}->{'d'}}) { #print "$date\n"; foreach my $repcode (keys %{$i->{'i'}->{'d'}->{$date}->{'report'}}) { #print "report $repcode\n"; # print Dumper $i->{'i'}->{'d'}->{$date}->{'report'}->{$repcode}; my $report=$i->{'i'}->{'d'}->{$date}->{'report'}->{$repcode}; foreach my $type ('class','sent') { my $value=$report->{$type}; if(not scalar keys %$value) { print "empty $type at $date $repcode\n"; } } } #foreach my $key (keys %{$i->{'i'}->{'d'}->{$date}}) { # print "key $key\n"; # #print Dumper $i->{'i'}->{'d'}->{$date}->{$report}; #} } } sub report_xml { my $i=shift; my $otria_list=shift // confess "I need this to be defined."; my $special_output=shift; ## done to prepare for lafise my $data=&Ernad::Store::load($i->{'file'}->{'json'}->{'reports'})->{'d'}; my $doc=XML::LibXML::Document->new('1.0','utf-8'); ## no update information needs setting #$sborn_ele->appendText("\n "); my $e=$i->{'e'}; my $amf_ns=$e->{'const'}->{'amf_ns'}; my $ernad_ns=$e->{'const'}->{'ernad_ns'}; my $xpc = $e->{'x'}->{'xpc'} // confess 'I need an Xpath here.'; my $amf_doc=$e->{'amfdoc'} // confess 'I need this defined here.'; my $impcol=$e->get_impcol()->cloneNode(1); foreach my $repcode (@$otria_list) { my $doc=$e->{'r'}->doc($repcode) // confess 'I need a repdoc here.'; my $coll_ele=$doc->getElementsByTagNameNS($amf_ns,'collection')->[0]; ## this removal is probably not required after osbore my $erimp_xp='/amf:amf/amf:collection/amf:ispartof'; my $erimp_ele=$xpc->find($erimp_xp,$doc)->[0]; ## normally not found if($erimp_ele) { $coll_ele->removeChild($erimp_ele); } if(not $data->{$repcode}) { next; } foreach my $issuedate (reverse sort keys %{$data->{$repcode}}) { my $issue_data=$data->{$repcode}->{$issuedate}->{'d'}; my $collection_ele=$doc->createElementNS($amf_ns,'collection'); my $id=$e->{'impna'}.':'.$repcode.':'.$issuedate; my $issue_ele=$doc->createElementNS($ernad_ns,'issue'); $issue_ele->setAttribute('repcode',$repcode); $issue_ele->setAttribute('issuedate',$issuedate); my $issuedate_pretty=&Ernad::Dates::pretty_date($issuedate); $issue_ele->setAttribute('issuedate_pretty',$issuedate_pretty); $issue_ele->setAttribute('total',$issue_data->{'total'}); my $shoti=$issue_data->{'shoti'}; $issue_ele->setAttribute('shoti',$shoti); my $issue_time=&Krichel::Shoti::pretty($shoti); $issue_ele->setAttribute('sent',$issue_time); $collection_ele->setAttribute('id',$id); $collection_ele->appendText("\n"); $collection_ele->appendChild($issue_ele); $collection_ele->appendText("\n"); my $haspart_ele=$doc->createElementNS($amf_ns,'haspart'); $haspart_ele->appendText("\n"); $haspart_ele->appendChild($collection_ele); $haspart_ele->appendText("\n"); $coll_ele->appendChild($haspart_ele); $coll_ele->appendText("\n"); } $coll_ele->appendText("\n"); my $haspart_ele=$doc->createElementNS($amf_ns,'haspart'); $haspart_ele->appendText("\n"); $haspart_ele->appendChild($coll_ele); $impcol->appendChild($haspart_ele); } $amf_doc->setDocumentElement($impcol); ## FixMe: this checks for update changes, there should be a version ## that does not do that. if($special_output) { &Ernad::Pages::install($amf_doc,$i->{'file'}->{'xml'}->{$special_output}); } else { &Ernad::Pages::install($amf_doc,$i->{'file'}->{'xml'}->{'reports'}); } return $amf_doc; } sub to_xml { my $i=shift; my $what=shift // ''; my $e=$i->{'e'}; my $d=$i->{'i'}->{'d'}; if(not $d) { ## we still have to produce the file for the XSLT transformation $e->echo(__LINE__,"I have no data for $what."); } my $doc=XML::LibXML::Document->new('1.0','utf-8'); my $sborn_ele=$doc->createElement('sborn'); ## no update information needs setting $sborn_ele->appendText("\n "); foreach my $issuedate (reverse sort keys %$d) { $e->echo(__LINE__,"I work for $what on $issuedate.",10); if(not $issuedate=~m|^\d{4}-\d{2}-\d{2}$|) { confess "I can't deal with your issuedate '$issuedate'."; } if($what eq 'complete') { my $json_file=$i->{'dir'}->{'issues'}."/$issuedate.json"; if(-f $json_file) { $e->echo(__LINE__,"I see an in_labor $json_file, I skip work on $what."); next; } } elsif($what eq 'in_labor') { my $json_file=$i->{'dir'}->{'issues'}."/complete/$issuedate.json"; if(-f $json_file) { $e->echo(__LINE__,"I see a complete $json_file, I skip work on $what."); next; } } else { confess "I don't understand your what '$what'"; } my $di=$d->{$issuedate}; my $issue_ele=$doc->createElement('issue'); $issue_ele->setAttribute('date',$issuedate); $issue_ele->setAttribute('date_pretty',&Ernad::Dates::pretty_date($issuedate)); #$issue_ele->appendText("\n"); my $repcodes; ## I used to get them by snore, but that is not always available, ## by virtue of some bug if($di->{'about'}->{'snore'}) { $repcodes=$i->get_sort_by_snore($di); } else { $repcodes=$e->{'r'}->list(); } foreach my $repcode (@$repcodes) { my @sents=sort keys %{$di->{'report'}->{$repcode}->{'sent'}}; my $first_shoti=$sents[0] // ''; if(not $first_shoti) { my $to_log= Dumper $di->{'report'}->{$repcode}; $to_log = '$di->{\'report\'}->{$repcode} is ' . $to_log; $e->echo(__LINE__,"I have no first shoti for $repcode, $to_log"); next; } $issue_ele->appendText("\n "); my $dir=$di->{'report'}->{$repcode}->{'sent'}->{$first_shoti}->{'d'}; my $vypot_ele=$doc->createElement('vypot'); $vypot_ele->setAttribute('repcode',$repcode); my $total=$dir->{'total'}; if(not defined($total)) { $e->echo(__LINE__,"I have no total for $repcode on $issuedate."); next; } $vypot_ele->setAttribute('total',$total); my $sent_shoti=$dir->{'sent'} // $first_shoti; my $sent_pretty=&Krichel::Shoti::pretty($sent_shoti); $vypot_ele->setAttribute('sent',$sent_pretty); $issue_ele->appendChild($vypot_ele); ## was in code but unclear as to why delete $di->{'report'}->{$repcode}->{'sent'}->{$first_shoti}; } $issue_ele->appendText("\n "); my $missing=''; foreach my $repcode (sort keys %{$di->{'about'}->{'snorlabo'}}) { my $vypot_ele=$doc->createElement('snorlabo'); $vypot_ele->setAttribute('repcode',$repcode); $issue_ele->appendChild($vypot_ele); $issue_ele->appendText("\n "); $missing.="$repcode "; } chop $missing; $issue_ele->setAttribute('missing',$missing); $sborn_ele->appendChild($issue_ele); $sborn_ele->appendText("\n "); } $doc->setDocumentElement($sborn_ele); ## save if we know what if($what) { my $out_file=$i->{'file'}->{'xml'}->{$what}; if(not $out_file) { confess "I don't know about your xml out file for '$what'"; } ## FixMe: this should be changed to a function that uses text::diff &Ernad::Files::save_if_diff($out_file,$doc); $e->echo(__LINE__,"I saved $out_file",1); } else { $e->echo(__LINE__,"I did not save $what xml",1); } return $doc; } sub get_sort_by_snore { my $i=shift; my $di=shift; my $type=shift // 'array'; my $e=$i->{'e'}; my $l; my @codes=keys %{$di->{'report'}} // confess "I need thes here."; ## FixMe: there should be a standard way to do that my $repcodes; my $count=0; my $snore=$di->{'about'}->{'snore'}; #print Dumper $di; #exit; #->{'about'}; if(not $snore) { confess "I have no snore data."; } foreach my $code (keys %{$di->{'report'}}) { if(not $snore->{$code}) { $e->echo(__LINE__,"I don't have a snore for $code."); next; } $repcodes->[$count++]=$code; } my $out; $count=0; foreach my $repcode (sort {$snore->{$a} <=> $snore->{$b}} @$repcodes) { if($type eq 'array') { $out->[$count++]=$repcode; } elsif($type eq 'hash') { $out->$repcode=1; } else { confess 'My type argument has to be "array" (default) or "hash".'; } } return $out; } sub show_all { my $i=shift; foreach my $what (@{$i->{'whats'}}) { $i->show($what); } } ## sub show { my $i=shift; my $what=shift // confess "Tell me what to show, in_labor or complete or reports."; ## doc to show my $in_doc=shift // ''; my $sheet_name='issue_sborn'; if($what eq 'reports') { $sheet_name='report_sborn'; } #if($what eq 'reports_lafise') { # $sheet_name='report_sborn_lafise'; #} my $xml_file=$i->{'file'}->{'xml'}->{$what}; my $html_file=$i->{'file'}->{'html'}->{$what}; if(-f $xml_file and -f $html_file and -M $html_file <= -M $xml_file) { $e->echo(__LINE__,"$html_file is younger than $xml_file, I skip"); return 0; } my $e=$i->{'e'}; ## out doc my $doc; ## the xml to transform my $xml; my $update=&Ernad::Dates::pretty_time(time); ## better use now as to be able to invoque show before the save of the XML ## file #if(-f $xml_file) { # my $mtime=&Ernad::Dates::mtime($xml_file); # $update=&Ernad::Dates::pretty_time($mtime); #} my $params={'update' => "'$update'",'type'=>"'$what'",'mod'=>"'changed'"}; if($in_doc) { $doc=$e->{'t'}->transform($in_doc,$sheet_name,$params); } else { $i->load($what); $e->echo(__LINE__,"I am runnig ->to_xml()"); $i->to_xml($what); if(not -f $xml_file) { confess "The file $xml_file must now be here."; } my $update=&Ernad::Dates::mtime($xml_file); $doc=$e->{'t'}->transform_file($xml_file,$sheet_name,$params); } ## FixMe: This is awkward. It must be read from an xslt parameter my $file_name; my $sub_dir='/sborn'; if($what eq 'in_labor') { $file_name='current'; } elsif($what eq 'complete') { $file_name='past'; } elsif($what eq 'reports') { # $sub_dir=''; $file_name='reports'; } #elsif($what eq 'reports_lafise') { # # $sub_dir=''; # $file_name='reports_lafise'; #} else { confess "I don't get a sense of your \$what '$what'"; } my $out_file_html=$e->{'dir'}->{'blatt'}."$sub_dir/$file_name.html"; &Ernad::Pages::install($doc,$out_file_html,$e->{'testing'}); } sub known_issuedates { my $i=shift; my $what=shift // confess "I need to know what issues"; if($what ne 'in_labor' and $what ne 'complete') { confess "I need 'in_labor' or 'complete' here."; } my $type=shift // 'array'; my $l; if($type eq 'array') { $l=[]; } elsif($type eq 'hash') { $l={}; } else { confess 'My argument has to be "array" (default) or "hash".'; } my $issues_dir=$i->{'dir'}->{'issues'} // confess "I need this here."; if($what eq 'complete') { $issues_dir=$issues_dir.'/complete'; } my $count=0; foreach my $file (sort glob("$issues_dir/*")) { chomp $file; if(not $file=~m|(\d{4}-\d{2}-\d{2})\.json$|) { next; } $l->[$count++]=$1; } $i->{'issues'}=clone($l); return $l; } sub remove_report_papids { my $i=shift; foreach my $repcode (keys %{$i->{'i'}->{'d'}->{'report'}}) { foreach my $state (keys %{$i->{'i'}->{'d'}->{'report'}->{$repcode}}) { if($state ne 'sent' and $state ne 'class') { delete $i->{'i'}->{'d'}->{'report'}->{$repcode}->{$state}; next; } foreach my $shoti (keys %{$i->{'i'}->{'d'}->{'report'}->{$repcode}->{$state}}) { delete $i->{'i'}->{'d'}->{'report'}->{$repcode}->{$state}->{$shoti}->{'d'}->{'papid'}; delete $i->{'i'}->{'d'}->{'report'}->{$repcode}->{$state}->{$shoti}->{'d'}->{'papids'}; } } ## should only happen for incomplete, but we do it everywhere if(not scalar keys %{$i->{'i'}->{'d'}->{'report'}->{$repcode}}) { delete $i->{'i'}->{'d'}->{'report'}->{$repcode}; } } } ## this is done to keep the sborns down sub remove_namf_papids { my $i=shift; my $to_go=$i->{'i'}->{'d'}->{'namf'}->{'papid'}; if(not $i->{'i'}->{'d'}->{'namf'}->{'d'}->{'papid'}) { $i->{'e'}->echo(__LINE__,"I see no namf papid data to remove."); } delete $i->{'i'}->{'d'}->{'namf'}->{'d'}->{'papid'}; } 1;