RSS Git Download  Clone
Raw Blame History
package LIMS::Controller::PrintRun;

use base 'LIMS::Base';
use LIMS::Local::Utils;
use File::Find::Rule; # find()
use Data::Dumper;

use Moose;
with (
    'LIMS::Controller::Roles::PDF',
    'LIMS::Controller::Roles::DataMap',
    'LIMS::Controller::Roles::DataFile', # get_destination_dir()
    'LIMS::Controller::Roles::RecordHandler',
	'LIMS::Controller::Roles::ResultHandler',
);

has reports => (
    is      => 'ro',
    isa     => 'ArrayRef[HashRef]',
    default => sub { [] },
    lazy    => 1,
	traits  => ['Array'],
	handles => {
		add_to_reports => 'push',
        clear_all_reports => 'clear',
	},
);
__PACKAGE__->meta->make_immutable(inline_constructor => 0);

#-------------------------------------------------------------------------------
sub default : StartRunmode {
    my $self = shift; $self->_debug_path($self->get_current_runmode);

	return $self->forbidden() unless $self->user_can('print_all');

    my $cfg = $self->cfg; # warn Dumper $cfg;
    { # load url if pre-formatted pdf (from print_run.pl cron) present:
        my $local_path = sprintf 'docs/%s/print_run/printrun_%s.pdf',
            $cfg->{settings}->{'_centre'},
            LIMS::Local::Utils::today->ymd; # warn $local_path;
        my $full_path = $cfg->{path_to_www_docs} . $local_path; # warn $full_path;
        $self->tt_params( printrun_pdf => $local_path ) if -e $full_path;
    }
    my $last_working_day = $self->_get_last_working_day();
    return $self->tt_process({ auth_date => $last_working_day });
}

#-------------------------------------------------------------------------------
sub do : Runmode {
    my $self = shift; $self->_debug_path($self->get_current_runmode);

    # get start & end datetime objects:
    my $start_date = $self->_get_start_date; # warn Dumper $start_date->datetime;
    my $end_date   = $self->_get_end_date;   # warn Dumper $end_date->datetime;

    # first find out how many have been requested:
    my $count = do {
        my %args = (
            start_date => $start_date,
            end_date   => $end_date,
            type       => 'non-trial', # only skips trial cases if configured in yml
        );
        $self->model('PrintRun')->get_print_run_size(\%args);
    }; # warn $count;
    if (! $count) {
        $self->flash( info => $self->messages('search')->{no_records_found} );
        return $self->redirect( $self->query->url . '/printrun' );
    }
    # if more than max print_run size (defined in settings), split load:
    elsif ($count > $self->cfg('settings')->{print_run_max}) {
        my %args = (
            count => $count,
            start_date => $start_date,
            # $end defaults to 00:00:00 on current_date (unless use_today selected)
            end_date => $end_date->subtract(seconds => 1), # so subtract 1 sec for .tt param
        );
        return $self->tt_process(\%args);
    }
    else {
		$self->stash( date_span => { start => $start_date, end => $end_date } );
        return $self->forward('_do_print'); # forward so tt picks up 'print' in runmode
    }
}

#-------------------------------------------------------------------------------
# converts form start & end date strings back to DT objects, forwards to _do_print():
sub do_offset : Runmode {
    my $self = shift; $self->_debug_path($self->get_current_runmode);

    my $p = $self->query->Vars();

    # to_datetime_using_parsedate() seems to work:
#    my $start = LIMS::Local::Utils::to_datetime_using_parsedate($p->{start});
#    my $end   = LIMS::Local::Utils::to_datetime_using_parsedate($p->{end});

    # to_datetime_using_strptime() config'd to use supplied pattern:
	my $dt = sub { # datetime string eg 2010-01-01T00:00:00
		LIMS::Local::Utils::to_datetime_using_strptime(@_, '%FT%T');
	};
	my $start = &$dt($p->{start}); # warn $start->datetime;
	my $end   = &$dt($p->{end});   # warn $end->datetime;
	$self->stash( date_span => { start => $start, end => $end } );

    my $content = $self->forward('_do_print'); # forward so tt picks up 'print' in runmode
    return $content;
}

#-------------------------------------------------------------------------------
sub _do_print : Runmode { # rm so template picks up 'print' in get_current_runmode
    my $self = shift; $self->_debug_path($self->get_current_runmode);

    my @sort_by = qw(referrers.name requests.year requests.request_number);
	my $dates   = $self->stash->{date_span};

    my %args = (
        start_date => $dates->{start},
        end_date   => $dates->{end},
        sort_by    => \@sort_by,
        type       => 'non-trial', # only skips trial requests if configured to do so in yml
    );
    #  offset supplied if total > max print_run size:
    $args{offset} = $self->query->param('offset') || 0; # default to beginning
    $args{limit}  = $self->cfg('settings')->{print_run_max} || 50; # sensible default

    # get request id's & cc's for records amended between start & end datetimes:
    my ( $request_ids, $ccs ) # = [298446, 298961]; # outreach & imatinib charts + 1 copy_to
        = $self->model('PrintRun')->get_print_run_request_ids(\%args);
    unless (@$request_ids) { # should never get here - count checked in do()
        $self->flash( info => $self->messages('search')->{no_records_found} );
        return $self->redirect( $self->query->url . '/printrun' );
    }
    # do we want to append supplimentary pdf's:
    my $supplimentary_filenames
        = $self->get_yaml_file('supplimentary_report_files');
    # warn Dumper $supplimentary_filenames;
    for my $request_id (@$request_ids) {
        # format request data for template:
        my $request_data = $self->get_single_request_data($request_id);
		# add processed lab test data to $request_data:
		$self->process_raw_lab_test_data($request_data);
        $self->add_to_reports($request_data); # warn Dumper $request_data;
        # add any supplimentary pdf's to args:
        if ( $supplimentary_filenames ) { # warn Dumper $filenames;
            my $request = $request_data->{data}->as_tree;
            my $pdf_dir = $self->_get_destination_dir($request); # warn $pdf_dir;
            my $regex = join '|', map quotemeta, @$supplimentary_filenames;
            # use File::Find::Rule procedural interface to find files matching pattern:
            my @files = find( 'file', name => qr{$regex}i, in => $pdf_dir );
            # check supplimentary files are pdf's:
            push @{ $args{supplimentary_files}{$request_id} },
                grep { lc io($_)->ext eq 'pdf' } @files;
        }
    } # warn Dumper $args{supplimentary_files};
    # copy-tos:
    for my $request_id (@$ccs) {
        my $request_data = $self->get_single_request_data($request_id);
        $self->process_raw_lab_test_data($request_data);
        my $copy = $self->_generate_copy($request_data);
        $self->add_to_reports($copy);
    }

    $ENV{REPORT_HTML} = undef if $ENV{PDF_REPORT}; # PDF_REPORT env overrides REPORT_HTML env
	if ($ENV{REPORT_HTML}) { # for lims_server and/or no PDF reader
        my $tmpl = 'record/default.tt';
        return $self->render_view($tmpl, { reports => $self->reports });
    }
    # combine pdfs into single:
    my $content = $self->combine_pdfs($self->reports, \%args);
    $self->clear_all_reports; # maybe free some memory ??
    # return single pdf:
    $self->header_add(-type => 'application/pdf', -expires => 'now');
    return $content;
}

=begin
sub _do_print_old {
    my ($self, $start, $end) = @_; $self->_debug_path();

    my @sort_by = qw(referrers.name requests.year requests.request_number);

    my %args = (
        start_date => $start,
        end_date   => $end,
        sort_by    => \@sort_by,
    );

    # exclude any locations which don't require paper copies:
    if ( my $cfg = $self->get_yaml_file('skip_paper_reports') ) {
        $args{skip_reports} = $cfg;
    } # warn Dumper \%args;

    #  offset supplied if total > max print_run size (maybe '0' so use defined):
    # if ( defined $self->query->param('offset') ) { # always submitted now
        $args{offset} = $self->query->param('offset') || 0; # default to beginning
        $args{limit}  = $self->cfg('settings')->{print_run_max} || 50; # sensible default
    # }

    # get request id's for records amended between start & end datetimes:
    my $request_ids
        = $self->model('PrintRun')->get_print_run_request_ids(\%args);

    unless (@$request_ids) { # should never get here - count checked in do()
        $self->flash( info => $self->messages('search')->{no_records_found} );
        return $self->redirect( $self->query->url . '/printrun' );
    }

	# allowed report error_codes:
	my $report_error_codes = $self->report_error_codes_map;

    # diagnosis_context_warnings:
	my $context_warning = $self->diagnosis_context_warning_map;

    # categories exempt from 'use NHS number':
    my $nhs_number_exempt = $self->nhs_number_exempt;

	# preserve 'app_url' & 'active_link' tt_params for use in loop:
	my %required_tt_params = map {
		$_ => $self->tt_params->{$_};
	} qw(app_url active_link);

	# my $i;
    foreach my $request_id (@$request_ids) { # last if $i++ > 1; # for testing
        # format request data for template:
        my $request_data = $self->format_print_record_request_data($request_id); # warn Dumper $request_id;
        # add data maps for template:
		$self->tt_params(
            context_warning_map => $context_warning,
            nhs_number_exempt   => $nhs_number_exempt,
            report_error_codes  => $report_error_codes,
            request             => $request_data,
        );
        # template depends on whether it's outreach data:
        my $tmpl = $request_data->{outreach}
            ? 'outreach/report_body.tt'
            : 'record/body.tt'; # warn $tmpl;

        # render template & add to 'reports' attr:
        my $html = $self->render_view($tmpl);
        $self->add_to_reports($$html); # dereference first

        { # has copy_to been requested?
            my $request_options = $request_data->{request_options};
            # warn Dumper $request_options;
            if ( $request_options->{copy_to}->{is_selected} ) {
                my $cc = $self->_generate_copy($html);
                $self->add_to_reports($cc); # already deref'ed
            }
        }
        $self->tt_clear_params; # clear all template params for next loop
		$self->tt_params(\%required_tt_params); # add required ones back
    }

    return $self->tt_process('printrun/wrapper.tt', { reports => $self->reports });
}
=cut
#-------------------------------------------------------------------------------
sub _generate_copy {
    my ($self, $ref) = @_; $self->_debug_path();

	# clone() of $ref generates thousands of warning msgs in Devel::Cycle
    # (ie dev server), caused by presence of a LIMS::DB object (OK if as_tree'd):
#	my $clone = LIMS::Local::Utils::clone($ref); $self->debug($clone);

    # take hash copy of $ref so original unaffected:
    my %h = %$ref;

    # take %h LIMS::DB::Request 'data' object into a hashref and clone it:
    my $data = $h{data}; # warn ref $data; # LIMS::DB::Request object
    # clone hashref NOT object - vital to prevent Devel::Cycle warnings:
    my $cloned = LIMS::Local::Utils::clone( $data->as_tree(deflate => 0) );

    # replace referrer name with 'cc' flag for template:
    $cloned->{referrer_department}->{referrer}->{name} = 'cc'; # warn Dumper $clone;
    # replace original $h{data} DB::Request object with cloned hashref (OK for tt):
    $h{data} = $cloned;
    # return %h as hashref, same as original $ref, but with referrer name changed:
    return \%h;
}

#-------------------------------------------------------------------------------
sub _get_start_date {
    my $self = shift; $self->_debug_path();

    my $q = $self->query;

    my $datetime;

    # expect params 'last_working_day' OR 'date', and optional 'today':
    if ( $q->param('last_working_day') ) {
        $datetime = $self->_get_last_working_day();
    }
    elsif ( my $date = $q->param('date') ) {
        $datetime = LIMS::Local::Utils::to_datetime_using_datecalc($date)
        || return $self->error("cannot decode date-string $date");
    } # warn Dumper $datetime;

    # in case start date not supplied (beginning of day 00:00:00):
    $datetime ||= LIMS::Local::Utils::time_now->truncate( to => 'day' );
    return $datetime;
}

#-------------------------------------------------------------------------------
sub _get_end_date {
    my $self = shift; $self->_debug_path();

    my $datetime = LIMS::Local::Utils::time_now(); # up to current time (BST-aware)
    # end date is today - 1 day, unless 'today' selected (query uses ymd so
    # includes full day to midnight):
    return $self->query->param('today')
        ? $datetime
        : $datetime->subtract( days => 1 );
}

#-------------------------------------------------------------------------------
sub _get_last_working_day { # considers public holidays
    my $self = shift; $self->_debug_path();

    # function moved to LLU (returns DT object):
    return LIMS::Local::Utils::last_working_date(); # considers public hols
=begin
    my $dt = DateTime->today->subtract( days => 1 ); # yesterday at 00:00:00

    # day 6 is Saturday, day 7 is Sunday
    while ( $dt->day_of_week >= 6 ) {
        $dt->subtract( days => 1 );
    } # warn Dumper $dt;
    return $dt;
=cut
}

1;