Subs::WrapUp (version $)


package Subs::WrapUp;
##############################################################################
#
# DESCRIPTION: This subroutine class handles a number of cleanup tasks which 
# DESCRIPTION: need to be done at the end of each processing run,
# DESCRIPTION: including creating output catalog files and verifying
# DESCRIPTION: the product files. Each of these functions is handled
# DESCRIPTION: by a separate method to make it easier to write a sub-class
# DESCRIPTION: which overriseds the default body method.
# DESCRIPTION: 
# DESCRIPTION: Each processing script should run this subroutine class
# DESCRIPTION: (or a decendant of it) last. 
#
# HISTORY
# HISTORY: $Log: WrapUp.pm,v $
# HISTORY: Revision 1.3  2014/02/27 06:38:17  apsop
# HISTORY: VERSION header now shows CVS Revision
# HISTORY:
# HISTORY: Revision 1.2  2006/08/01 20:35:34  apsop
# HISTORY: Add in CVS history indicator.
# HISTORY:
# HISTORY: 1.0 -> 1.1 2002-04-01
# HISTORY: Now make a hard link insteead of copying the parfiles. This way
# HISTORY: the data product version will stay up to date. Previously 
# HISTORY: Things like missing file errors would not get counted, since
# HISTORY: they were logged after the parfiles were copied.
#
# VERSION: $Revision: 1.3 $
#
##############################################################################

use Subs::Sub;
@ISA = ("Subs::Sub");
use strict;

use Util::CoreTags;


#########################################
# constructor
#########################################
sub new {
    my $proto=shift;
    my $self=$proto->SUPER::new();

    $self->{DESCRIPTION}="Doing final wrapup of all files";

    $self->{FILES}=();
    $self->{FITS_FILES}=();

    $self->{CHECKSUM_TYPE}="checksum";
    $self->{HTML_CAT_TYPE}="fileinfo";

    return $self;
}


#########################
# METHODS:
#########################


##############################################################################
# Do a number of generic wrapup functions. Specifically:
# - save copies of the job.par and processing parfile.
# - create all output file catalogs (product and trend by default)
# - calculate site-independant checksums
# - update the checksums in all FITS file headers
# - run the fverify FTOOL on all FITS files.
# - check for leftover files, which are not included in any of the output 
#   catalogs
#############################################################################
sub body {
    my $self=shift;

    my $filename = $self->filename();
    my $log      = $self->log();
    my $jobpar   = $self->jobpar();
    my $procpar  = $self->procpar();
    
    ##############################################
    # Save the parameter files
    ##############################################
    $self->save_parfiles();

    #####################################
    # create the file catalogs
    #####################################
    $self->make_catalogs();

    #######################################
    # Write keywords to all the FITS files
    #######################################
    $self->write_standard_keywords();

    ###################################
    # make the checksum file
    ###################################
    $self->make_checksum_file();

    ##################################
    # update FITS checksum files
    ##################################
    $self->fits_checksums();
    $self->verify();

    ######################################
    # check for leftover files
    ######################################
    $self->leftovers();


} # end of body method

#######################################################
# save a copy of the parfiles
#######################################################
sub save_parfiles {

    my $self=shift;

    my $log=$self->log();
    my $filename=$self->filename();

    my %pars=(jobpar  => $self->jobpar(),
              procpar => $self->procpar() );

    my $type;
    foreach $type (keys %pars) {
        my $par=$pars{$type};

        my $infile=$par->name();
        my $outfile=$filename->get($type);

        ###############################################################
        # copy the file if the outfile file does not exist
        # or if the infile was modified before the last modification
        # of the outfile.
        # In stead of copying, we make a hard link. That way both
        # copies of the file will stay in synch even if one is modified
        # after the copy. This is important for the nprocerrors
        # parameter.
        ################################################################
        if( ! -f $outfile || (stat($outfile))[9] > (stat($infile))[9] ) {

            $log->entry("Copying $infile to $outfile");

            #open IN, "<$infile";
            #open OUT, ">$outfile";
            #print OUT <IN>;
            #close IN;
            #close OUT;

            link $infile, $outfile
        }
    } # end of loop over parfiles

} # end of save_parfiles method


###########################################################################
###########################################################################
# make all FITS catalogs and the HTML file catalog
###########################################################################
sub make_catalogs {
    my $self=shift;

    my $filename=$self->filename();

    ######################################################
    # get names of catalogs, and assume the HTML catalog 
    # has the same files as the first FITS catalog
    ######################################################
    my @fits_catalogs=$filename->catalog_types();
    my $html_cat_is_like=$fits_catalogs[0];

    ##########################################################
    # register the files we have yet to make with the 
    # Catalog class
    ##########################################################
    Util::Catalog->future_files((map     {$_=>'FITS'} @fits_catalogs),
                                'checksum'  =>'ASCII',
                                'fileinfo' =>'HTML');

    ###############################################
    # create FITS catalogs
    ###############################################
    my $type;
    foreach $type (@fits_catalogs ) {

        Util::Catalog->new($type)
                     ->make()
                     ->register_in_parfile();
    }

    ########################################
    # create the HTML catalog
    ########################################
    Util::HTMLcatalog->new($html_cat_is_like,'fileinfo')
                     ->make();

} # end of make_catalogs method

#########################################################################
#########################################################################
# add standard keywords to the headers of all the FITS files.
#########################################################################
sub write_standard_keywords {

    my $self=shift;

    my $log      = $self->log();
    my $filename = $self->filename();
    my $jobpar   = $self->jobpar();
    my $procpar  = $self->procpar();

    $log->entry("Writing standard keywords to all FITS files");

    ####################################
    # loop over all FITS files
    ####################################
    my $file;
    foreach $file (Util::Catalog->fits_files() ) {

        my $fits = Util::FITSfile->new($file);

        ##################################
        # loop over HDUs in the FITS file
        ##################################
        my $nhdus = $fits->nhdus();
        my $hdu;
        for($hdu=0; $hdu<$nhdus; $hdu++) {
            $fits->ext($hdu);

            ################################
            # write keywords to the file
            ################################
            $fits->begin_many_keywords();

            $fits->keyword("SEQNUM", $jobpar->read("sequence"), 
                           "Unique ID for this dataset" );

            $fits->keyword("SEQPNUM", $jobpar->read("seqprocnum"), 
                           "Number of times this dataset processed" );

            $fits->keyword("PROCVER", $procpar->read("version"), 
                           "Processing script version" );

            $fits->end_many_keywords();

        }

    }

} # end of write_standard_keywords method


#########################################################################
#########################################################################
# calculate site independant checksums
#########################################################################
sub make_checksum_file {
    my $self=shift;

    my $log      = $self->log();
    my $filename = $self->filename();
    my $jobpar   = $self->jobpar();

    $log->entry("Calculating site-independant checksums");

    #####################################
    # open the checksum file
    #####################################
    my $checksum_file=$filename->get("checksum");
    open  CHECKSUM, ">$checksum_file";
    print CHECKSUM "This file lists all files plus site-independant\n";
    print CHECKSUM "checksums for all FITS files. It may be used to compare\n";
    print CHECKSUM "sequences processed by parallel pipelines.\n";


    ####################################
    # loop over all FITS files
    ####################################
    my $file;
    foreach $file (Util::Catalog->fits_files() ) {

        my $sum=Util::FITSfile->new($file)
                              ->site_independant_checksum();

        print CHECKSUM "$file $sum\n";
    }

    ###########################
    # close the checksum file
    ###########################
    close CHECKSUM;

    ###################################################
    # now calculate the checksum of the checksum file
    # and record that in the checksum file
    ###################################################
    my $checksum_tool=Util::FITSfile->checksum_tool();

    my $seqcheck = $checksum_tool->command_line($checksum_file)
                                 ->run()
                                 ->stdout();
    $seqcheck =~ s/^(\S*).*$/$1/s;

    $jobpar->set({seqcheck=>$seqcheck});
    $log->entry("Site independant checksum: $seqcheck");



} # end of make_checksums method

    

#############################################################################
#############################################################################
# run fverify on all the FITS files
#############################################################################
sub verify {
    my $self=shift;

    my $log=$self->log();

    $log->entry("Running fverify on all FITS files");

    ###################
    # set up fverify 
    ###################
    my $fverify=Util::Ftool->new("fverify")
                           ->params({outfile=>"STDOUT",
                                     prhead=>"no",
                                     testdata=>"yes"})
                           ->verbose(0);

    ######################################
    # loop over all FITS files
    ######################################
    my $file;
    foreach $file (Util::Catalog->fits_files() ) {

        Util::FITSfile->new($file)
                      ->verify();
    }

 
} # end of verify method

#############################################################################
#############################################################################
# set FITS checksum keywords
#############################################################################
sub fits_checksums {
    my $self=shift;

    my $log=$self->log();

    $log->entry("Updating FITS checksum keywords");

    #####################
    # set up fchecksum
    #####################
    my $fchecksum=Util::Ftool->new("fchecksum")
                             ->params({update =>"yes",
                                       datasum=>"yes"})
                             ->verbose(0);

    ######################################
    # loop over all FITS files
    ######################################
    my $file;
    foreach $file (Util::Catalog->fits_files() ) {

        $fchecksum->params({infile=>"$file"})
                  ->run();
    }

} # end of fits_checksums 

                             
#############################################################################
#############################################################################
# check for leftover files
#############################################################################
sub leftovers {
    my $self=shift;

    my $log    =$self->log();
    my $jobpar =$self->jobpar();
    my $procpar=$self->procpar();


    ##########################################################
    # Mash together a list of all the files in the current
    # directory, plus all the files in the catalog, plus
    # the names of the two unsaved parfiles. Then
    # count the occurances of each filename in the combined
    # list. Anything which does not appear twice is an error
    #########################################################
    my @files=glob("*");
    my %count=();
    my $file;
    foreach $file (@files,
                   Util::Catalog->all_files(),
                   $jobpar ->name(),
                   $procpar->name() ) {

        $count{$file}++;
    }

    ##########################################
    # look for files in this directory which 
    # are not in the catalog
    ##########################################
    foreach $file (@files) {
        if($count{$file} == 1 ) {
            $log->error([ 1, UNKNOWN_FILE ], "Leftover file $file");
        }
    }

} # end of leftovers method


         
1;