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;