RSS Git Download  Clone
Raw Blame History
#!/usr/bin/env perl

=begin -------------------------------------------------------------------------
function to email reports to:
1) location options:
* specific hospital department (eg Pinderfields/Haem)
* specific organisation department (eg Leeds/Haem)
* a hospital (all departments)
* an organisation (all departments)

2) request status options:
* new
* all

only using basic DBIx::Simple methods (hashes) so son't need Local::DB

how to deal with reports where a departmental contact exists, and a
hospital/organisation contact (do they also get the dept report?), and also a
CWT contact (usually needs all 'new' reports)

* can manually run script using --re-run flag to dispatch any unsent reports on
    same day: CENTRE=xxxx perl email_reports.pl --re-run >> ~/crons/cron.log 2>&1
--------------------------------------------------------------------------------
=cut

BEGIN {
	use lib '/home/raj/perl5/lib/perl5'; # *must* come before FindBin($Bin)
 	use FindBin qw($Bin); # warn $Bin; exit;
 	use lib ("$Bin/../../../lib", '/home/raj/perl-lib');
 	# override default db test:
 	$ENV{ROSEDB_DEVINIT} = "$Bin/../../../config/rosedb_devinit_prod.pl";
}

my($opt_d,$opt_t,$rerun);
use Getopt::Long;
GetOptions (
    "days|d=s"  => \$opt_d,  # string
    "testing|t" => \$opt_t,  # flag
    "re-run"    => \$rerun,  # flag
); # warn $opt_d; warn $opt_t; warn $rerun; exit;

# emails only 'service_email' addr in config, dumps history data to lims_test db:
my $JUST_TESTING = $opt_t || 0;

my $delta = $opt_d || 1; # warn $delta; # days ago (1 for production)

use IO::All;
use File::Slurp;
use Config::Auto;
use Modern::Perl;
use Data::Printer;
use SQL::Abstract::More;
use DateTime::Format::MySQL;

use LIMS::Local::Reports;
use LIMS::Local::ScriptHelpers;

my $cfg_file = "$Bin/../lib/settings.cfg";
my $settings = Config::Auto::parse($cfg_file); # p $settings;

# get tools from LIMS::Local::ScriptHelpers:
my $tools = LIMS::Local::ScriptHelpers->new();
$tools->test_only($JUST_TESTING);

my $sql_lib = $tools->sql_lib();
my $config  = $tools->config();
my $today   = $tools->time_now();
my $dbix    = $tools->dbix;
my $sqla    = SQL::Abstract::More->new;

my $date = $tools->date_subtract( days => $delta )->ymd; # warn $date;

# substitutes LIMS methods required by Role::RecordHandler:
my $lims = LIMS::Local::Reports->new({tools => $tools});
# localhost http address - for charts; is_print_request for tt formatting:
$lims->tt_params(
    app_url => 'http://localhost/hilis4',
	is_print_request => 1, # otherwise it loads guest view template by default
);

# load exemptions:
my $skip_reports = $tools->get_yaml_file('skip_email_reports') || {}; # hashref or 0

#===============================================================================
# get all requests authorised/modified yesterday as AoH:
# [request_id, status, referral_source_id, parent_organisation_id & department_id]
my @requests = do {
	my @cols = qw( r.id rr.status rs.id|referral_source_id|ref_src_id
		hd.id|department_code rs.parent_organisation_id|parent_id );
    my @rels = (
        'requests|r'                  => 'ris.request_id=r.id'               ,
        'request_initial_screen|ris'  => 'ris.screen_id=s.id'                ,
        'screens|s'                   => 'r.patient_case_id=pc.id'           ,
        'patient_case|pc'             => 'pc.referral_source_id=rs.id'       ,
        'referral_sources|rs'         => 'r.referrer_department_id=rd.id'    ,
		'referrer_department|rd'	  => 'rd.hospital_department_code=hd.id' ,
		'hospital_departments|hd'     => 'rr.request_id=r.id'				 ,
		'request_report_view|rr'	  => 'r.status_option_id=so.id'			 ,
		'status_options|so'           => '=>rt.request_id=r.id'              ,
		'request_trial|rt'			  => '=>rt.trial_id=ct.id'				 ,
		'clinical_trials|ct'
    );

    my @status = ( 'complete' );
    push @status, $tools->does_authorisation ? 'authorised' : 'reported';

	my %where = (
        'DATE(rr.updated_at)' => $date,
		'so.description'      => { -in => \@status },
	);
	# skip screened_as/presentation:
	if ( my $presentation = $skip_reports->{presentation} ) { # aref
		$where{'s.description'} = { -not_in => $presentation };
	}
	# skip clinical trials:
	if ( my $trials = $skip_reports->{clinical_trial} ) { # aref
		$where{'ct.trial_name'} = # needs an undef or lose all non-ct entries
			[ -or => { -not_in => $trials }, undef ];
	}

    my @args = (
		-columns => \@cols,
		-from    => [ -join => @rels ],
		-where   => \%where,
	);
	my ($sql, @bind) = $sqla->select(@args); # p $sql; # p \@bind;
	 # $dbix->dump_query($sql, @bind); exit;
    $dbix->query($sql, @bind)->hashes; # AoH
}; # p \@requests;

#===============================================================================
# get list of report email contact details as AoH:
my @contacts = do {
	my @cols = qw(
		ec.display_name
		ec.referral_source_id|source_id
		rs.parent_organisation_id|parent_id
		ec.scope
		ec.department_id
		ec.contact_address
		ec.status
	);
    my @rels = (
        'email_contacts|ec'    =>   'ec.referral_source_id=rs.id',
        'referral_sources|rs'
	);
    my @args = (
		-columns  => \@cols,
		-from     => [ -join => @rels ],
		-where    => { 'ec.type' => 'report', 'ec.is_active' => 'yes' },
	); # p @args;
	my ($sql, @bind) = $sqla->select(@args); # p $sql; # p \@bind;
	  # $dbix->dump_query($sql, @bind); exit;
    $dbix->query($sql, @bind)->hashes; # AoH
}; # p \@contacts;

my %reports; # hash of contact addresses => [ request.ids ]

my @request_cols = qw( id status ref_src_id department_code parent_id );
my @contact_cols
	= qw( scope contact_address parent_id department_id source_id status );

# for each contact address, assign any matching request ids from $requests
# involves looping through each request for each contact (but very fast):
for my $contact (@contacts) {
	my ($scope, $email, $parent_id, $department_id, $location_id, $want_status)
		= @{$contact}{@contact_cols};

	REQ:
	for my $req (@requests) {
		my ($req_id, $req_status, $req_refsrc_id, $req_department_code,
			$req_parent_id)	= @{$req}{@request_cols};

		# first deal with request status (requires new diagnoses or not):
		if ( $want_status eq 'new' ) {
			next REQ unless $req_status eq 'new';
		}
		# next deal with contacts for specific departments:
		if ( $department_id ) { # 823, 824, etc
			next REQ unless $req_department_code == $department_id;
		}
		# contact 'scope' is either hospital or organisation:
		if ( $scope eq 'hospital' ) {
			next REQ unless $req_refsrc_id == $location_id;
		}
		else { # scope = 'organisation'
			next REQ unless $req_parent_id == $parent_id;
		}
        if ($rerun) { # manual command using --re-run flag
            # has report already been sent today?
            my $dispatched = $dbix->select('request_dispatch_log', 1,
                {
                    'DATE(time)' => $today->ymd,
                    request_id   => $req_id,
                    recipient    => $email,
                })->list;
            next REQ if $dispatched;
        }
		# now have required combination of hospital/organisation + optional dept:
			# my $label = join '::', $email, $contact->{display_name}; # for debug use only
			# push @{ $reports{$label} }, $req_id;
		push @{ $reports{$email} }, $req_id;
	}
} # p \%reports; exit;

=begin #========================================================================
# for testing using known requests:
%reports = ( 'hmds.lth@nhs.net' => [
#   307257 # NEQAS request - anonymised patient
#   308979 # PenENRICH trial - unknown referrer
#   309256 # Imatinib chart
    308502 # Outreach
  ]);
#===============================================================================
=cut

# common vars for email message:
my %mail = ( config => $config );

# now get pdf's as in-memory object for each contact - may involve compiling
# some reports more than once but alternative is save to disk and clean up after
while ( my ($email, $ref) = each %reports ) { # aref of request_id's
	REQ:
	for my $req_id (@$ref) {
		my $pdf = $lims->format_report($req_id); # L::C::Roles::RecordHandler
        # $pdf > io("$Bin/$req_id.pdf"); warn 'for test only'; next REQ;
		$mail{attachment} = $pdf;

		# retrieve request data object stashed in Recordhandler::_format_report():
		my $data = $lims->stash->{request_data};

		{ # add subject line:
			my $referrer_department = $data->referrer_department;

			my $subject = sprintf 'HMDS Report for %s / %s',
				$referrer_department->hospital_department->display_name, # speciality
				$referrer_department->referrer->name; # referrer
            # doesn't work properly if unknown clinician - uses referrer_department.id 31116:
            if ( $subject =~ /Unknown clinician/ ) { # "Unknown/Other / Unknown clinician"
                $subject = 'HMDS Report [referrer unknown]'
            }
			$mail{subject} = $subject;
		}
		{ # add filename:
			my $filename = sprintf '%02d_%05d.pdf',
				$data->year - 2000,
				$data->request_number;
			$mail{filename} = $filename;
		} # p \%mail;
        # send report to recipient:
        $mail{recipient} = $email; # p \%mail;
        dispatch_report({ request_id => $req_id, message => \%mail });
	}
}

sub dispatch_report {
    my $args = shift;

    my $request_id = $args->{request_id};
    my $message    = $args->{message};

    # substitute recipient address here if $JUST_TESTING (L::L::Mail doesn't any more):
    if ($JUST_TESTING) {
        my $safe_address = $config->{service_email};
        die "unsafe recipient address $safe_address"
            unless $safe_address =~ /\@nhs.net\Z/;
        $message->{recipient} = $safe_address;
    }

    my $result = LIMS::Model::Email->send_message($message); # Return::Value
    my $centre = $ENV{CENTRE} || 'leeds';

	if ( $result->type eq 'success' ) {
        my $recipient = $message->{recipient};
        my $filename  = $message->{filename};

        my $str = $result->string; # rs = 'message sent'; can't substitute direct on rs, so:
        $str =~ s/message/$filename [$centre]/i; # str = eg '12_01234.pdf [uclh] sent'

        printf "%s %s to %s\n", $tools->script_filename, $str, $recipient;

        my %data = (
            request_id => $request_id,
            recipient  => $recipient,
        );

        # log details in request_dispatch_log & request_history tables:
        unless ($JUST_TESTING) {
            my $rtn = $lims->model('Request')->update_request_dispatch_log(\%data);
            warn "Error in $0: $rtn" if $rtn;
        }
        else { # manual input into lims_test db:
            $dbix->insert('lims_test.request_dispatch_log', \%data);

            my $service_user = $tools->get_server_user_details();
            $data{user_id} = $service_user->{id};

            $data{action} = 'dispatched report to ' . $recipient;
            delete $data{recipient};
            $dbix->insert('lims_test.request_history', \%data);
        }
    }
	else {
		warn $result->string;
        $tools->mail_admin({ script => $0, msg => $result->string });
	}
}