package Ernad::Erimp; use strict; use warnings; use Carp qw(cluck longmess shortmess croak confess); #use Data::Dumper; #use Date::Format; use File::Copy; use File::Path; use File::Slurper; use File::Basename; use List::Util qw(shuffle); #use MIME::Entity; #use XML::LibXML; #use XML::LibXSLT; #use XML::LibXML; use Ernad::Constant; use Ernad::Common; use Ernad::Dates; use Ernad::Dir; use Ernad::Files; use Ernad::Generate; use Ernad::Otria; use Ernad::Report; use Ernad::Recon; use Ernad::Rix; use Ernad::Rif; use Ernad::State; use Ernad::Xslt; use Krichel::File; use Krichel::Shoti; use Krichel::String; ## constructor sub new { my $this=shift; my $class=ref($this) || $this; my $e={}; bless $e, $class; my $params=shift; ## copy parameters into the object foreach my $key (keys %{$params}) { $e->{$key}=$params->{$key}; } my $impna=$e->{'impna'} // ''; $e->{'today'}=&Ernad::Dates::today(); ## import constants ... fixme where are they used? foreach my $field (keys %{$Ernad::Constant::c}) { $e->{'const'}->{$field}=$Ernad::Constant::c->{$field}; } $e->set_directories(); if($impna) { $e->read_config(); $e->set_conf_directories(); } # $e->get_xpc(); $e->{'amfdoc'}=&Ernad::Generate::amf_doc(); ## will be rewritten #$e->read_doc(); #if(not defined($params->{'no_reports'})) { # $e->read_reports(); #} if($e->{'conf'}->{'allport_repcode'}) { $e->set_allport_directories(); } ## can not be set with the other directories $e->{'dir'}->{'web'}=$e->{'conf'}->{'web_dir'}; $e->{'dir'}->{'eval_web'}=$e->{'conf'}->{'eval_web_dir'}; ## get variable from style/constants.xslt.xml $e->{'xslt'}=Ernad::Xslt->new({'e' => $e}); ## should be renamed impvars # $e->set_xslt_vars(); ## set the testing variable to show if we are on a test installation $e->set_testing(); ## the helpers $e->{'x'}=Ernad::Rix->new({'e'=>$e}); $e->{'x'}->init(); $e->{'r'}=Ernad::Recon->new({'e'=>$e}); $e->{'s'}=Ernad::State->new({'e'=>$e}); $e->{'t'}=Ernad::Xslt->new({'e'=>$e}); $e->{'f'}=Ernad::Rif->new({'e'=>$e}); $e->{'d'}=Ernad::Dir->new({'e'=>$e}); $e->{'o'}=Ernad::Otria->new({'e'=>$e}); $e->{'p'}=Ernad::Report->new({'e'=>$e}); ## basic summory report data #$e->{'b'}=Ernad::Report->new({'e'=>$e}); ## this shortcut is used in many places $e->{'xpc'}=$e->{'x'}->{'xpc'} // confess "I need the xpc here."; my $allport_repcode=$e->{'conf'}->{'allport_repcode'}; ## load the allport inconditionally if($allport_repcode) { $e->{'r'}->load($allport_repcode); } ## functions in the module that are for external callers will make ## sure that it is initialized ##$e->{'r'}->init(); return $e; } ## gets the critical implementation name, impna sub get_impna { my $e=shift; if(not defined($e->{'impna'})) { confess 'Give me an impna.'; } if(not $e->{'impna'}) { confess 'You gave me an empty impna parameter.'; } return $e->{'impna'}; } ## <-- ## this is only used in Indat sub get_impcol { my $e=shift; if(defined($e->{'impcol'})) { return $e->{'impcol'}; } ## also sets the impcol $e->read_impdoc(); if(not $e->{'impcol'}) { confess "I need this defined here."; } return $e->{'impcol'}; } # ## sets all the directories sub set_directories { my $e=shift; my $impna=$e->{'impna'} // ''; my $home=$ENV{'HOME'}; # for cgi! #my $home='/home/ernad'; my $dir=$e->{'dir'}; my $file=$e->{'file'}; my $const=$e->{'const'}; if(defined($home)) { $dir->{'home'}=$home; } else { ## running under fcgi $dir->{'home'}='/home/ernad'; } $dir->{'ernad'}=$dir->{'home'}.'/'.$const->{'name'}; ## still used, usually opt/css $dir->{'css'}=$dir->{'ernad'}.'/'.$const->{'css_dir'}; $dir->{'xslt'}=$dir->{'ernad'}.'/'.$const->{'xslt_dir'}; $dir->{'perl'}=$dir->{'ernad'}.'/'.$const->{'perl_dir'}; my $etc_dir; if($impna) { $etc_dir=$dir->{'ernad'}.'/'.$const->{'etc_dir'}.'/'.$impna; ## check that the etc dir exits, exit otherwise if(not -d $etc_dir) { print "I don't see $etc_dir. I suspect your impna '$impna' is wrong\n"; exit; } } $dir->{'etc'}=$etc_dir; if(not $impna) { $e->echo(__LINE__,"I have no impna.",1); return; } $dir->{'var'}=$dir->{'ernad'}.'/'.$const->{'var_dir'}.'/'.$impna; $dir->{'www'}=$dir->{'ernad'}.'/'.$const->{'www_dir'}.'/'.$impna; $dir->{'export'}=$dir->{'var'}.'/'.$const->{'export_dir'}; $dir->{'adrep'}=$dir->{'var'}.'/'.$const->{'adrep'}; $dir->{'isuad'}=$dir->{'var'}.'/'.$const->{'isuad'}; $dir->{'fut'}=$dir->{'var'}.'/'.$const->{'fut_dir'}; $dir->{'fut_issues'}=$dir->{'fut'}.'/'.$const->{'issues'}; $dir->{'fut_files'}=$dir->{'fut'}.'/archive'; $dir->{'fut_fields'}=$dir->{'fut'}.'/fields'; $dir->{'announce'}=$dir->{'var'}.'/'.$const->{'announce_dir'}; $dir->{'tmp'}=$dir->{'ernad'}.'/'.$const->{'tmp_dir'}.'/'.$impna; $dir->{'style'}=$dir->{'ernad'}.'/'.$const->{'style_dir'}.'/'.$impna; $dir->{'exfit_xslt'}=$dir->{'style'}.'/exfit'; $dir->{'db'}=$dir->{'var'}.'/'.$const->{'db'}; $dir->{'pile'}=$dir->{'var'}.'/'.$const->{'pile'}; $dir->{'monitor'}=$dir->{'var'}.'/'.$const->{'monitor_dir'}; $dir->{'issues'}=$dir->{'var'}.'/'.$const->{'issues'}; $dir->{'db_backup'}=$dir->{'var'}.'/'.$const->{'db_backup'}; $dir->{'reports'}=$dir->{'var'}.'/'.$const->{'reports_dir'}; $dir->{'learn'}=$dir->{'var'}.'/'.$const->{'learn_dir'}; $dir->{'blatt'}=$dir->{'var'}.'/'.$const->{'blatt'}; $dir->{'indat'}=$dir->{'var'}.'/'.$const->{'indat'}; # $dir->{'eval'}=$dir->{'learn'}.'/'.$const->{'eval_dir'}; $dir->{'log'}=$dir->{'var'}.'/'.$const->{'log_dir'}; $dir->{'archive'}=$dir->{'var'}.'/'.$const->{'archive_dir'}; $dir->{'confs_archive'}=$dir->{'archive'}.'/'.$const->{'confs_dir'}; $dir->{'reports_archive'}=$dir->{'archive'}.'/'.$const->{'reports_dir'}; foreach my $name (keys %{$dir}) { if(not defined($dir->{$name})) { next; } my $dir_name=$dir->{$name}; ## commented out 2017-10-06 #if(not -d $dir_name and not -l $dir_name) { # mkpath $dir_name or confess "I could not create the directory $dir_name"; #} } ## namf my $namf_dir=$dir->{'var'}.'/'.$const->{'namf_dir'}; if(-d $namf_dir) { $dir->{'namf'}=$namf_dir; # $e->{'namfi'}=1; } $e->{'dir'}=$dir; ## seed_dir only exists, if etc/seed is there my $seed_conf_dir=$dir->{'etc'}.'/'.$const->{'seed_dir'}; $dir->{'seed_conf'}=$seed_conf_dir; if(-d $seed_conf_dir) { $dir->{'seed_conf'}=$seed_conf_dir; $e->{'seedable'}=1; delete $e->{'const'}->{'source_states'}->{'unsorted'}; } else { $dir->{'seed_conf'}=''; $e->{'seedable'}=0; delete $e->{'const'}->{'source_states'}->{'seeded'}; } $dir->{'seed'}=$dir->{'var'}.'/'.$const->{'seed_dir'}; if($e->{'seedable'}) { if(not -d $dir->{'seed'}) { mkpath $dir->{'seed'}; } } else { #if(not -d $dir->{'seed'}) { # #$e->echo(__LINE__,"I delete the seed_dir, because I have no etc/seeds",2); # rmtree $dir->{'seed'}; #} } $e->{'dir'}=$dir; return $e; } sub set_allport_directories { my $e=shift; my $dir=$e->{'dir'}; my $namf_dir=$dir->{'var'}.'/'.$e->{'const'}->{'namf_dir'}; if(-d $namf_dir) { $dir->{'available'}=$namf_dir; return ''; } ## set allport directories #my $allport_repcode=$e->get_allport_repcode(); my $allport_dir=$e->get_allport_dir(); my $const=$e->{'const'}; $e->{'dir'}->{'allport'}=$allport_dir; $dir->{'allport_sent'}=$allport_dir.'/'.$const->{'sent_dir'}; # $dir->{'allport_svm'}=$allport_dir.'/'.$const->{'svm_dir'}; $dir->{'allport_source'}=$allport_dir.'/'.$const->{'source_dir'}; my $allport_source_dir=$dir->{'allport_source'}; $dir->{'allport_source_us'}=$allport_source_dir.'/'.$const->{'unsorted_dir'}; $dir->{'allport_source_ps'}=$allport_source_dir.'/'.$const->{'presorted_dir'}; $dir->{'available'}=$dir->{'allport_sent'}; } sub set_conf_directories { my $e=shift; $e->{'dir'}->{'membership'}=$e->{'conf'}->{'membership_dir'}; $e->{'dir'}->{'web'}=$e->{'conf'}->{'web_dir'}; if($e->{'conf'}->{'mailvault_dir'}) { $e->{'dir'}->{'mailvault'}=$e->{'dir'}->{'web'}.'/'.$e->{'conf'}->{'mailvault_dir'}; } $e->{'dir'}->{'backup'}=$e->{'conf'}->{'backup_dir'}; $e->{'dir'}->{'web_opt'}=$e->{'conf'}->{'web_dir'}.'/'.$e->{'const'}->{'opt_dir'}; } ## returns the allport sub get_allport_repcode { my $e=shift; my $namf_dir=$e->{'dir'}->{'var'}.'/'.$e->{'const'}->{'namf_dir'}; ## just a simple security check if(not defined($namf_dir)) { confess "I need to have this defined here."; } ## second condition added for NEP, probably not needed if(-d $namf_dir and $e->{'conf'}->{'separate_doklis'}) { return ''; } if(not $e->{'conf'}) { confess "I need to have this set here."; } my $allport_repcode=$e->{'conf'}->{'allport_repcode'} // ''; #if($e->{'conf'}->{'allport_repcode'}) { # return $e->{'conf'}->{'allport_repcode'}; #} return $allport_repcode; } ## return the allport collection sub get_allport_collection { my $e=shift; if(not $e->{'conf'}) { confess "I need this defined to check for the allport_repcode"; return ''; } if(not $e->{'conf'}->{'allport_repcode'}) { return ''; } my $allport_collection=$e->{'allport_collection'} // ''; ## already initialized? if($allport_collection) { return $allport_collection; } ## force reread, generates clone of collection $e->find_allport_collection(); return $e->{'allport_collection'}; } ## finds the allport collection, assuming it wasn't set sub find_allport_collection { my $e=shift; if(not $e->{'conf'}) { confess "I need this defined to check for the allport_repcode"; } my $allport_repcode=$e->{'conf'}->{'allport_repcode'}; if(not $allport_repcode) { return ''; } if(not $e->{'r'}) { $e->{'r'}=Ernad::Recon->new({'e'=>$e}); } $e->{'allport_collection'}=$e->{'r'}->allport_collection; } ## returns the repcodes when they exist ## will NOT list the allport_repcode, but may have it before ## this version of Erimp is official sub list_repcodes { my $e=shift; my $repcodes=$e->{'repcodes'}; if(defined($repcodes)) { return @{$repcodes}; } if(not $e->{'r'}) { $e->{'r'}=Ernad::Recon->new({'e'=>$e}); } my $r=$e->{'r'}; my $list=$r->list('array'); if($list and ref($list) eq 'ARRAY') { return @{$list}; } ## read reports gets the repcodess #$e->read_reports(); #if(defined($e->{'repcodes'})) { # return @{$e->{'repcodes'}}; #} confess 'I could not find the repcodes.'; } ## returns the allport_dir sub get_allport_dir { my $e=shift; #if($e->{'namfi'}) { # return ''; #} my $allport_dir=$e->{'dir'}->{'allport'}; if(defined($allport_dir)) { if(not -d $allport_dir) { confess "no such directory: allport_dir"; } } $e->find_allport_dir(); if($e->{'dir'}->{'allport'}) { return $e->{'dir'}->{'allport'}; } confess 'I could not find the allport_dir.'; } ## sub find_allport_dir { my $e=shift; my $reports_dir=$e->{'dir'}->{'reports'}; my $repcode=$e->get_allport_repcode(); $e->{'dir'}->{'allport'}=$reports_dir.'/'.$repcode; return $e; } ## sub read_config { my $e=shift; my $impna=$e->get_impna(); my $config_file=$e->{'dir'}->{'style'}."/conf.xslt.xml"; if(not -f $config_file) { confess "I don't see my configuration $config_file."; } my @lines=&File::Slurper::read_lines($config_file); foreach my $line (@lines) { if(not $line=~m|\s*\s*$|) { next; } my $name=&Krichel::String::de_xml($1); my $value=&Krichel::String::de_xml($2); $e->{'conf'}->{$name}=$value; } } sub run_bin { my $e=shift; my $name=shift // confess "I need argument here."; my $arg=shift // ''; my $add_log=shift // ''; my $shoti=&Krichel::Shoti::now; my $bin=$e->{'dir'}->{'perl'}.'/'.$name; my $log_file; if($add_log) { $log_file=$e->{'dir'}->{'log'}.'/'.$name.'/'.$add_log.'_'.$shoti.'.log'; } else { $log_file=$e->{'dir'}->{'log'}.'/'.$name.'_'.$shoti.'.log'; } &Krichel::File::prepare($log_file); my $err_file; if($add_log) { $err_file=$e->{'dir'}->{'log'}.'/'.$name.'/'.$add_log.'_'.$shoti.'.err'; } else { $err_file=$e->{'dir'}->{'log'}.'/'.$name.'_'.$shoti.'.err'; } my $system="$bin $arg > $log_file 2> $err_file &"; my $ret=system($system); return $ret; } ## copy of the previous, just run while the parent waits ## meaning $system has no '&' sub run_system { my $e=shift; my $name=shift // confess "I need argument here."; my $arg=shift // ''; my $add_log=shift // ''; my $shoti=&Krichel::Shoti::now; my $bin=$e->{'dir'}->{'perl'}.'/'.$name; my $log_file; if($add_log) { $log_file=$e->{'dir'}->{'log'}.'/'.$name.'/'.$add_log.'_'.$shoti.'.log'; } else { $log_file=$e->{'dir'}->{'log'}.'/'.$name.'_'.$shoti.'.log'; } &Krichel::File::prepare($log_file); my $err_file; if($add_log) { $err_file=$e->{'dir'}->{'log'}.'/'.$name.'/'.$add_log.'_'.$shoti.'.err'; } else { $err_file=$e->{'dir'}->{'log'}.'/'.$name.'_'.$shoti.'.err'; } my $system="$bin $arg > $log_file 2> $err_file"; my $ret=system($system); return $ret; } ## new version on flight 2019-03-26, flight PRG --> OVB sub set_testing { my $e=shift; if(-d '/etc/wpa_supplicant') { $e->{'testing'}='testing'; } ## this could also be set as a parameter on invocation elsif(not $e->{'testing'}) { $e->{'testing'}=''; } ## make a testing directory, fixme: why, if it's not testing ## should only be there for the testing installation my $dir='/tmp/ernad'; if(not -d $dir) { mkpath $dir; } $e->{'dir'}->{'test'}=$dir; } sub echo { my $e=shift; my $line_number=shift; my $text=shift or confess "I need something to echo"; my $verbosity=shift // 0; if(not $verbosity=~m|^\d+$|) { confess "I need an integer for my verbosity, not '$verbosity'."; } ## by default, a maximum verbosity of 100 my $critical = $e->{'verbose'} // 100; if($verbosity < $critical) { my ($package, $filename, $line) = caller; print "$package $line | $text", "\n"; } } ## it's a doc, but logically belongs to the erimp ## build the implementation collection sub read_impdoc { my $e=shift; ## should be done, but we try to be generous if(not $e->{'t'}) { $e->{'t'}=Ernad::Xslt->new({'e'=> $e}); } my $impdoc=$e->{'t'}->t('','impcol'); $e->{'impdoc'}=$impdoc; my $impcol=$impdoc->documentElement->cloneNode(); $e->{'impcol'}=$impcol; return $impdoc; } ############################################################################### ## updates the last update for all reports sub update_last_updates { my $e=shift; ## fixme: this makes for a circular dependence with ## subroutines redefined warnings. use Ernad::ReportState; foreach my $repcode ($e->list_repcodes()) { my $rs=new Ernad::ReportState($repcode, $e); $rs->load(); my $rerc=$e->{'report'}->{$repcode}; $rerc->{'update_tist'}=$rs->{'update_tist'} or confess; #$e->{'report'}->{$repcode}->{'update_tist'}=$rs->{'update_tist'}; } } #*# ## this is the opposite of is_futile, it clears the ## work done by the editor ## used in unfutile and unleash sub clear_work { my $e=shift; my $repcode=shift // confess 'I need a report here.'; my $issuedate=shift // confess 'I need an issuedate here.'; ## deal with a crooked issuedate $issuedate=~s|^(\d{4})\D+(\d{2})\D+(\d{2})$|$1-$2-$3|; my $with_source=shift // ''; my $report_dir=$e->{'report'}->{$repcode}->{'path'} // confess "I don't have a path for the report $repcode."; my $backup_dir=$e->{'dir'}->{'backup'}; my $ernad_dir=$e->{'dir'}->{'ernad'} or confess "I need a backup directory here."; if($with_source) { confess "fixme: This is no longer implemented."; # my $s="find $report_dir -name '$issuedate*.amf.xml*' -type f -exec rm {} \\;"; # $e->echo(__LINE__,"running $s"); # system($s); # return; } foreach my $stage (@{$e->{'const'}->{'stages'}}) { my $stage_dir=$report_dir.'/'.$stage; # print "$issuedate\n"; foreach my $ext ('.amf.xml','.amf.xml.gz') { my $glob=$stage_dir.'/'.$issuedate.'_*'.$ext; my @files=glob($glob); # print "glob is $glob\n"; foreach my $file (@files) { print "file is $file\n"; my $backup_file=$file; $backup_file=~s|^\Q$ernad_dir\E|$backup_dir| or confess "I can't find the backup for the file '$file'."; $e->echo(__LINE__,"I move $file to $backup_file",1); &Krichel::File::prepare($backup_file); move($file,$backup_file); } } } ## remove vadoc } sub call_on_all_reports { my $e=shift; my $fun_ref=shift; #my $only_do_repcode=shift // ''; ## curpu first, then curse my @repcodes=shuffle $e->list_repcodes(); #push(@repcodes,shuffle $e->get_curse_repcodes()); foreach my $repcode (@repcodes) { #if($only_do_repcode and ($repcode ne $only_do_repcode)) { # $e->echo(__LINE__,"I skip $repcode because I only to $only_do_repcode."); # next; #} &{$fun_ref}($repcode,@_); } } 1;