package LIMS::Controller::Patient;
use Moose;
BEGIN { extends 'LIMS::Base'; }
with (
'LIMS::Controller::Roles::Misc',
'LIMS::Controller::Roles::SessionStore',
'LIMS::Controller::Roles::PatientDemographics', # Mini-Spine service
);
__PACKAGE__->meta->make_immutable(inline_constructor => 0);
use LIMS::Local::PAS;
use LIMS::Local::Utils;
use Data::Dumper;
#-------------------------------------------------------------------------------
sub default : Startrunmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $errs = shift; $self->stash( errs => $errs ); # for debugging in tmpl
return $self->forbidden() unless $self->user_can('register');
$self->js_validation_profile('js_new_patient');
# if redirected from empty registration search, retrieve saved form
# params from session & load into query:
$self->_load_saved_form_params;
return $self->tt_process($errs);
}
#-------------------------------------------------------------------------------
sub add_new : Runmode {
my $self = shift; my $rm = $self->get_current_runmode; $self->_debug_path($rm);
return $self->forbidden() unless $self->user_can('register');
my $dfv = $self->check_rm( 'default', $self->validate('new_patient') )
|| return $self->dfv_error_page;
my $patient = $dfv->valid; # $self->debug( $patient );
# create dt object from patient year, month & day vals:
$patient->{dob} = LIMS::Local::Utils::to_datetime($patient); # $self->debug( $patient );
# check new patient details for potential duplicates and/or PDS mismatches
# returns true if OK, otherwise sets appropriate tt_params for default.tt:
my $check_ok = $self->_check_new_patient($patient); # warn Dumper $check_ok;
unless ($check_ok) {
my $pds_return_codes = $self->get_yaml_file('pds_return_codes');
$self->tt_params( pds_codes => $pds_return_codes );
my $html = $self->render_view('patient/default.tt'); # for is_code_ref()
return $self->fill_form($html);
}
# insert data to patients/patient_cases and return patient_cases.id (last_insert_id):
my $case_id = $self->model('Patient')->create_new_patient($patient)
|| return $self->error('no patient_case id value returned to ' . $rm);
return $self->redirect( $self->query->url.'/request/add_new/'.$case_id );
}
#-------------------------------------------------------------------------------
sub add_new_location : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $patient_id = $self->param('id')
|| return $self->error('no patient id passed to '.$self->get_current_runmode);
my $dfv = $self->check_rm( 'select_patient', $self->validate('new_location') )
|| return $self->dfv_error_page;
my $data = $dfv->valid; # $self->debug($data);
my %patient_case_data = (
patient_id => $patient_id,
referral_source_id => $data->{referral_source_id},
unit_number => $data->{unit_number},
);
my $patient_case = $self->model('PatientCase')->new_patient_case(\%patient_case_data);
return $self->redirect( $self->query->url . '/request/add_new/' . $patient_case->id );
}
#-------------------------------------------------------------------------------
sub select_patient : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $errs = shift;
return $self->forbidden() unless $self->user_can('register');
my $patient_id = $self->param('id')
|| return $self->error('no patient id passed to '.$self->get_current_runmode);
my $patient = $self->model('Patient')->get_patient_demographics($patient_id);
$self->js_validation_profile('new_location');
my $patient_cases
= $self->model('PatientCase')->get_cases_by_patient_id($patient_id);
if ($self->cfg('settings')->{pas_address}) { # do PAS lookup (if local patient):
my $pas_query = $self->_pas_query($patient_id); # $self->debug($pas_query);
$self->tt_params( pas_query => $pas_query ); # switched off, replaced by PDS
}
{ # do PDS lookup:
my $pds_query = $self->_personal_demographic_service($patient);
$self->tt_params( pds_query => $pds_query );
}
{ # previous clinical trials:
my %h = ( patient_id => $patient_id );
my $trials = $self->model('ClinicalTrial')->get_patient_trials(\%h);
$self->tt_params( clinical_trials => $trials );
}
my $pds_return_codes = $self->get_yaml_file('pds_return_codes');
$self->tt_params(
patient_cases => $patient_cases,
pds_codes => $pds_return_codes,
patient => $patient,
);
return $self->render_view($self->tt_template_name, $errs);
}
#-------------------------------------------------------------------------------
sub edit_patient : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $errs = shift; # $self->stash(errs => $errs);
return $self->forbidden() unless $self->user_can('edit_pid');
my $patient_id = $self->param('id')
|| return $self->error('no id passed to '.$self->get_current_runmode);
# request_id (optionally) passed as 2nd token (or in form if resubmitted after dfv failure)
my $request_id = $self->param('Id') || $self->query->param('request_id') || '';
if ($request_id) {
my $request = $self->model('Request')->get_request($request_id);
$self->tt_params( request => $request );
}
$self->js_validation_profile('js_edit_patient');
my $patient = $self->model('Patient')->get_patient($patient_id);
$self->tt_params( patient => $patient );
if ($self->cfg('settings')->{pas_address}) { # PAS query (replaced by PDS):
# vars only supplied by PAS search if insufficient details supplied:
my $vars = $self->query->Vars(); # $self->debug($vars);
my $pas_query = (%$vars)
? [ $vars ] # template expects array of hashrefs
: $self->_pas_query($patient_id);
$self->tt_params( pas_query => $pas_query ); # $self->debug($pas_query);
}
{ # do PDS lookup:
my $pds_query = $self->_personal_demographic_service($patient);
my $pds_return_codes = $self->get_yaml_file('pds_return_codes');
$self->tt_params(
pds_query => $pds_query,
pds_codes => $pds_return_codes,
);
}
{ # error codes:
my $error_codes
= $self->model('ErrorCode')->get_error_code_assignment('patient');
$self->tt_params(
error_codes => $error_codes,
);
}
{ # get any similar patient entries:
my $cases
= $self->model('Patient')->get_similar_patients($patient);
my $similar_entries = $self->extract_patients_and_locations($cases);
$self->tt_params( similar_entries => $similar_entries );
}
{ # has patient id been used already (for delete function):
my $count = $self->model('Patient')->patient_request_count($patient_id);
$self->tt_params( count => $count );
}
return $self->render_view($self->tt_template_name, $errs);
}
#-------------------------------------------------------------------------------
sub pds_query : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $patient_id =
$self->param('id') || $self->query->param('_patient_id');
if ($patient_id) { # warn $patient_id;
my $data = $self->model('Patient')->get_patient($patient_id);
$self->tt_params( patient => $data ); # warn Dumper $data;
}
else { # no match in patients table - re-use submitted form params:
my $vars = $self->query->Vars; # warn Dumper $vars;
# freeze vars in session for transfer to registration form if no PAS match:
unless ($vars->{_is_pds_search}) { # ie return from pas search - don't want these vals
$self->session->param( patient_search_params => { %$vars } ); # needs to be in assoc. array format 1st
}
# get yr, month, day from dob if supplied (otherwise expect separate vars):
if ( my $dob = $vars->{dob} ) { # convert to ymd format for Utils::to_datetime():
@{$vars}{qw(year month day)} = split '-', $dob;
}
my %patient = map +($_ => $vars->{$_}),
qw(last_name first_name nhs_number unit_number);
$patient{dob} = LIMS::Local::Utils::to_datetime($vars); # .tt requires DT
$self->tt_params( patient => \%patient ); # warn Dumper \%patient;
=begin # PDS doesn't use unit numbers:
my @unit_numbers;
if ( my $unit_number = $self->query->param('unit_number') ) {
push @unit_numbers, $unit_number; # only if exists, or get [ '' ] in tt
}
$self->tt_params( unit_numbers => \@unit_numbers );
=cut
# clear all query params to prevent fill_form() loading into form fields:
# $self->query->delete_all(); # not required if not using fill_form()
}
return $self->render_view($self->tt_template_name);
}
#-------------------------------------------------------------------------------
sub do_pds_search : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my %data = $self->query->Vars; # warn Dumper \%data; # in hash context, or can't modify dob key
if ( grep { ! $data{$_} } qw(last_name gender dob) ) { # warn 'insufficient vars';
$self->flash( error => $self->messages('demographics')->{pds_insufficient_vars} );
return $self->forward('pds_query');
}
# dob -> datetime:
if ( my $dob = $data{dob} ) {
my $dt = DateTime::Format::MySQL->parse_date($dob); # warn Dumper $dt;
$data{dob} = $dt;
}
# format post-code (PDS error unless WWd(d) dWW format):
if ( my $post_code = $data{post_code} ) {
$data{post_code} = LIMS::Local::Utils::format_postcode($post_code);
} # warn \%data;
my $pds_query = $self->_personal_demographic_service(\%data); # warn Dumper $pds_query;
my $pds_return_codes = $self->get_yaml_file('pds_return_codes');
$self->tt_params(
pds_query => $pds_query,
pds_codes => $pds_return_codes,
);
return $self->forward('pds_query');
}
#-------------------------------------------------------------------------------
# request to edit patient during registration process; sets hidden flag to alter
# redirect destination:
sub register_edit_patient : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
$self->tt_params( registration_edit => 1 ); # flag for tmpl
return $self->forward('edit_patient'); # use forward() so tt_template_name() works
}
#-------------------------------------------------------------------------------
sub edit_case : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
return $self->forbidden() unless $self->user_can('edit_pid');
my $case_id = $self->param('id')
|| return $self->error('no id passed to '.$self->get_current_runmode);
$self->js_validation_profile('patient_case');
my $patient_case
= $self->model('PatientCase')->retrieve_patient_data($case_id);
# has entry been used already (for delete function):
my $requests_count
= $self->model('Request')->get_patient_case_requests_count($case_id);
# how many patient_case entries for this patient?
my $patient_id = $patient_case->patient_id;
my $patient_case_count = do {
my $o = $self->model('PatientCase')
->get_cases_by_patient_id($patient_id);
scalar @$o;
};
$self->tt_params(
data => $patient_case,
requests_count => $requests_count,
patient_case_count => $patient_case_count,
);
return $self->tt_process;
}
#-------------------------------------------------------------------------------
sub update_case : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
return $self->forbidden() unless $self->user_can('edit_pid');
my $case_id = $self->param('id')
|| return $self->error('no id passed to '.$self->get_current_runmode);
my $dfv = $self->check_rm( 'default', $self->validate('patient_case') )
|| return $self->dfv_error_page;
# get form params as hashref:
my $data = $dfv->valid; # warn Dumper $data;
my %args = (
data => $data,
case_id => $case_id,
);
my $rtn = $self->model('PatientCase')->update_patient_case(\%args);
return $rtn ?
$self->error($rtn) :
$self->redirect( $self->query->url . '/request/add_new/' . $case_id );
}
#-------------------------------------------------------------------------------
sub delete_case : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
return $self->forbidden() unless $self->user_can('delete_record');
my $q = $self->query;
my $case_id = $self->param('id')
|| return $self->error('no id passed to '.$self->get_current_runmode);
my $case = $self->model('PatientCase')->get_patient_case($case_id)
|| return $self->error(sprintf q!case id '%s' not found in %s!,
$case_id, $self->get_current_runmode);
# check not already been used (initial check done in edit_case):
if ( $self->model('Request')->get_patient_case_requests_count($case_id) ) {
$self->flash( error => $self->messages('registration')->{cannot_delete_case} );
return $self->redirect( $q->url . '/patient/edit_case/' . $case_id);
}
# need confirmation to delete record:
if ( $q->param('confirm_delete') ) {
my $redirect_url
= $q->url.'/patient/select_patient/'.$case->patient_id;
# successful delete (or no such record) returns true:
my $successful_delete = $self->model('PatientCase')->delete_patient_case($case_id);
return $successful_delete ?
$self->redirect( $redirect_url ) :
$self->error('Sorry - delete failed, I\'ve no idea why.');
}
# just return template with form:
else {
$self->tt_params( case => $case );
return $self->tt_process;
}
}
#-------------------------------------------------------------------------------
sub delete_patient : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
return $self->forbidden() unless $self->user_can('delete_record');
my $q = $self->query;
my $patient_id = $self->param('id')
|| return $self->error('no id passed to '.$self->get_current_runmode);
my $patient = $self->model('Patient')->get_patient($patient_id)
|| return $self->error(sprintf q!patient id '%s' not found in %s!,
$patient_id, $self->get_current_runmode);
# check patient not attached to other record(s) (initial check done in edit_patient):
if ( $self->model('Patient')->patient_request_count($patient_id) ) {
$self->flash( error => $self->messages('registration')->{cannot_delete_patient} );
return $self->redirect( $q->url . '/patient/edit_patient/' . $patient_id);
}
# need confirmation to delete record:
if ( $q->param('confirm_delete') ) {
# successful delete (or no such record) returns true:
my $successful_delete = $self->model('Patient')->delete_patient($patient_id);
return $successful_delete ?
$self->redirect( $q->url.'/register' ) :
$self->error('Sorry - delete failed, I\'ve no idea why.');
}
# just return template with form:
else {
$self->tt_params( patient => $patient );
return $self->tt_process;
}
}
#-------------------------------------------------------------------------------
sub update_patient : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
return $self->forbidden() unless $self->user_can('edit_pid');
my $q = $self->query;
my $patient_id = $self->param('id')
|| return $self->error('no id passed to '.$self->get_current_runmode);
# hidden field if edited from a request page:
my $request_id = $q->param('request_id');
# put $patient_id into params() for $self->validate('edit_patient') profile:
$self->query->param(_record_id => $patient_id);
my $dfv = $self->check_rm( 'edit_patient', $self->validate('edit_patient') );
# need to (re)set template flag(s) first if validation failed:
if (! $dfv) {
map { # warn $q->param($_);
$self->tt_params( $_ => $q->param($_) );
} grep $q->param($_), qw(registration_edit request_id);
return $self->dfv_error_page;
}
# get form params as hashref:
my $data = $dfv->valid; # $self->debug( $data );
# create DoB datetime object if day, month & year passed:
$data->{dob} = LIMS::Local::Utils::to_datetime($data);
# add patient.id to data so record updated if it's an edit:
$data->{id} = $patient_id;
# now we have the ability to change only 1 record, need to check NHS no if
# submitted doesn't already exist (can't change single record with an NHS no):
unless ( $self->_check_nhs_number_usage($data) ) { # returns 1 if OK
my $str = '/patient/edit_patient/' . join '/', $patient_id, $request_id;
return $self->redirect( $self->query->url . $str ); # flash msg set in sub
}
{ # do PDS lookup - returns 0 if PDS verification failed; 1 if it passes or not required:
my $result = $self->_demographics_verification($data);
return $self->forward('edit_patient') if not $result; # only necessary on failure
}
# validations, etc ok - update record:
my $rtn = $self->model('Patient')->update_patient($data); # warn Dumper $rtn;
# $rtn = { error => $db->error (if any); success => number of rows updated }
if ($rtn->{error}) {
return $self->error($rtn->{error});
}
else { # update succeeded:
my $messages = $self->messages('request_edit');
my $i = $rtn->{success}; # number of rows updated
my @flash = $i # success can be 0, or an INT
? ( info => sprintf $messages->{edit_success}, $i )
: ( warning => $messages->{edit_failed} );
$self->flash( @flash );
my $redirect = $q->param('registration_edit') ?
# redirect to registration page for $patient_id:
"/register/patient_search?patient_id=$patient_id" :
# redirect to specific request, or to general search page:
$request_id ? "/search/=/$request_id" : '/search';
return $self->redirect( $q->url . $redirect );
}
}
# ------------------------------------------------------------------------------
sub patient_notes : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
return $self->forbidden() unless $self->user_can(['report','modify_results']);
my $request_id = $self->param('id')
|| return $self->error('no request_id passed to '.$self->get_current_runmode);
my $patient_id = $self->param('Id')
|| return $self->error('no patient_id passed to '.$self->get_current_runmode);
# only one param passed:
my $str = $self->query->param('patient_notes');
my %args = (
patient_id => $patient_id,
detail => $str,
);
my $rtn = $self->model('Patient')->update_patient_notes(\%args);
return $self->error($rtn) if $rtn;
$self->flash( info => $self->messages('action')->{edit_success} );
return $self->redirect( $self->query->param('fwd_to') );
}
#-------------------------------------------------------------------------------
# check nhs number if submitted AND editing 1 patient only, returns 0 if NHS no
# already exists, otherwise returns 1:
sub _check_nhs_number_usage {
my ($self, $data) = @_;
# only applies to single record change with an NHS number:
return 1 unless ( $data->{this_record_only} && $data->{nhs_number} );
my $nhs_number = $data->{nhs_number};
if ( $self->model('Patient')->get_patient_from_nhs_number($nhs_number) ) {
$self->flash(error => $self->messages('request_edit')->{nhs_conflict});
return 0; # blocks update
}
return 1;
}
#-------------------------------------------------------------------------------
# checks against PDS (unless already done it, or PDS isn't config'd eg *.t, or
# anon patient)- only returns 0 if verification _fails_ (not if it isn't done):
sub _demographics_verification {
my ($self, $data) = @_;
my $can_skip_pds = (
$data->{use_patient_id} || # selected existing patient entry
! $self->cfg('settings')->{pds_proxy} ) || # PDS switched off
grep $self->query->param($_), qw(_pid_confirmed _skip_pds); # already done it
unless ( $can_skip_pds ) {
my $pds_query = $self->_personal_demographic_service($data);
# need to check pds return is hashref (not error str) before deref'ing:
my $is_verified = ( $pds_query && ref $pds_query eq 'HASH'
&& $pds_query->{demographics_verified} );
# return 0 if verification attempted but fails:
unless ( $is_verified ) {
# flag to tt to allow override:
$self->tt_params( is_patient_edit => 1 ); # to allow '_pid_confirmed'
return 0; # will trigger forward to patient_edit
};
}
return 1; # caller only cares if verification fails in previous block
}
#-------------------------------------------------------------------------------
# if redirected from empty registration search, retrieve saved form params from
# session & load into query:
sub _load_saved_form_params {
my $self = shift; $self->_debug_path;
my $patient_search_params = # will be empty if not arrived via empty registration search
$self->session->param('patient_search_params') || return;
foreach (keys %$patient_search_params) { # $self->debug($patient_search_params->{$_});
$self->query->param( $_ => $patient_search_params->{$_} );
}
# now clear patient_search_params hahref
$self->session->clear('patient_search_params');
}
# checks new patient data for potential duplicate or PAS mismatch,
# returns false if potential problem, otherwise true:
#-------------------------------------------------------------------------------
sub _check_new_patient {
my $self = shift; $self->_debug_path;
my $patient = shift; # hashref (with dob = DT)
# form flags to confirm & override checks:
my $local_confirmed = $self->query->param('_local_confirmed');
my $pid_confirmed = $self->query->param('_pid_confirmed');
# do PDS query first - if OK then do a local db check for duplicates - can
# only do local check *after* PDS, so can skip PDS if local_confirmed:
if ( not $pid_confirmed and not $local_confirmed ) {
if ( my $pds_query = $self->_personal_demographic_service($patient) ) { # warn Dumper $pds_query;
$self->tt_params(
is_new_patient => 1, # flag for tt to enforce confirmation in case of pds diffs
pds_query => $pds_query,
patient => $patient,
);
return 0;
}
}
# check for potential duplicate unit_number and/or nhs_number:
if ( not $local_confirmed ) {
my $maybe_duplicate =
$self->model('PatientCase')->validate_patient_case($patient);
if (@$maybe_duplicate) { # warn 'maybe_duplicate';
$self->tt_params( maybe_duplicate => $maybe_duplicate );
return 0;
}
}
# OK, patient validated against PDS and HILIS4 patient table:
return 1;
}
#-------------------------------------------------------------------------------
# returns true if 1 or more locations = local in patient_cases object:
sub _is_local_patient {
my $self = shift; $self->_debug_path;
my $patient_data = shift; # arrayref if PatientCase obj, or hashref of patient data
# get local prefix from settings or return if empty:
my $local_prefix = $self->cfg('settings')->{local_prefix}
|| return 0; # warn $local_prefix;
if (ref $patient_data eq 'HASH') {
my $ref_src_id = $patient_data->{referral_source_id};
my $location =
$self->model('ReferralSource')->get_referral_source($ref_src_id);
# returns true if organisation_code matches local prefix:
return $location->organisation_code =~ /\A($local_prefix)/;
}
else { # returns true if at least 1 organisation_code matches local prefix:
return grep {
$_->referral_source->organisation_code =~ /\A($local_prefix)/
} @$patient_data;
}
}
=begin nd
Function: _pas_query()
Called by <select_patient()> to check patient demographic data submitted either via
selection of previous patient match or directly through Validate button.
Uses <LIMS::Local::PAS> to query the PAS interface. Returns a ref to array of hashrefs
(if matching patient data found on PAS) or string containing error status
($c->config->{msg}) if not.
=cut
#-------------------------------------------------------------------------------
sub _pas_query {
my ($self, $patient_id) = @_; $self->_debug_path;
my @pas_config_settings = qw(pas_address pas_username pas_pwd);
my $cfg = $self->cfg('settings');
# need all required PAS config settings, or return:
return 0 if grep ! $cfg->{$_}, @pas_config_settings;
my $patient_cases # arrayref
= $self->model('PatientCase')->get_cases_by_patient_id($patient_id);
# needs to be local patient for PAS lookup:
return 0 unless $self->_is_local_patient($patient_cases);
# variable: $pas_excluded_names
# list of last_names excluded from PAS search (eg HIV's)
# my $pas_excluded_names = join '|', @{ $self->cfg('pas_excluded_names') };
# return if # skip PAS lookup if:
# last_name matches proscribed entry(s) in config->pas_excluded_names,
# $patient->{last_name} =~ /\A($pas_excluded_names)/ || # eg HIV's
# form carries 'pas_checked' flag:
# $params_ref->{pas_checked}; # already been there
my %patient;
if (ref $patient_cases eq 'ARRAY') {
# extract 1st record (all same patient) from $patient_data:
my $patient_case = $patient_cases->[0];
# create patient hashref for PAS query:
%patient = map {
$_ => $patient_case->patient->$_;
} $patient_case->patient->meta->column_names;
# get local unit_number(s) as arrayref from referral(s):
my $local_unit_numbers = $self->_get_local_unit_numbers($patient_cases);
# include unit_numbers arrayref:
$patient{unit_number} = $local_unit_numbers; # $self->debug($local_unit_numbers);
# dob needs to be in yyyy-mm-dd format:
if ( my $dob = $patient_case->patient->dob) {
$patient{dob} = $dob->ymd; # $self->debug(\%patient);
}
# PAS considers FORENAME as first_name + middle_name:
$patient{first_name} = join ' ', map $patient_case->patient->$_,
grep $patient_case->patient->$_, qw(first_name middle_name);
}
elsif (ref $patient_cases eq 'HASH') { # does this ever happen ??
%patient = %$patient_cases; # $self->debug(%patient);
# TODO: dob needs to be ymd:
$patient{dob} = $patient{dob}->ymd if $patient{dob};
}
else {
return $self->error('_pas_query() called with unexpected $patient_data format');
}
my %pas_config = map +($_ => $cfg->{$_}), @pas_config_settings; # $self->debug(\%pas_config);
my %args = (
patient => \%patient,
config => \%pas_config,
messages => $self->messages('demographics'),
); # warn Dumper \%args;
my $pas = LIMS::Local::PAS->new(\%args);
my $q = $pas->query; # warn Dumper $q; # ref to array of hashrefs or string
return $q;
}
#-------------------------------------------------------------------------------
sub _personal_demographic_service {
my ($self, $patient) = @_; $self->_debug_path; # hashref (with DoB = DT)
return 0 if $self->query->param('_skip_pds'); # _skip_pds is tt flag
my $config = $self->cfg('settings');
# pds_proxy only used by dev server, but also used as on/off switch;
# env flag PDS_LOOKUPS allows pds queries even if not in production mode:
return 0 unless $config->{pds_proxy} &&
( $config->{is_in_production_mode} || $ENV{PDS_LOOKUPS} );
my $data = ref $patient eq 'LIMS::DB::Patient' # get_pds_data() expects hashref with DT object:
? $patient->as_tree(deflate => 0) # $patient = select_patient() object
: $patient; # $patient = add_new() hashref
# skip anon patients, certain trials, etc:
if ( my $cfg = $self->get_yaml_file('pds_exempt_names') ) { # warn Dumper $cfg;
my $first_names = $cfg->{first_name}; # warn Dumper $first_names;
my $last_names = $cfg->{last_name}; # warn Dumper $last_names;
# set flag for tt to skip pds on subsequent submissions & return 0:
if (
( grep $data->{first_name} =~ qr(\A$_\Z)i, @$first_names ) ||
( grep $data->{last_name} =~ qr(\A$_\Z)i, @$last_names )
) {
$self->tt_params( is_pds_exempt => 1 );
return 0;
}
}
my $result = $self->get_pds_data($data); # warn Dumper $result;
return $result;
}
#-------------------------------------------------------------------------------
# parses $patient_cases array(ref) for local unit_numbers, as patient referral
# from multiple locations may include remote location unit_number(s) which match
# a (different) local patient but are not valid for local PAS lookup:
sub _get_local_unit_numbers {
my $self = shift; $self->_debug_path;
my $patient_cases = shift;
my $local_prefix = $self->cfg('settings')->{local_prefix};
# create hash of local unit_numbers (excluding default val):
my %local_unit_numbers =
map { $_->unit_number, 1 }
grep { # skip null (or default) unit_numbers:
$_->unit_number ne $_->meta->column('unit_number')->default,
}
grep {
$_->referral_source->organisation_code =~ /\A($local_prefix)/
} @$patient_cases; # $self->debug(\%local_unit_numbers);
# return arrayref to list of unique unit_numbers:
return [ sort keys %local_unit_numbers ];
}
1;
__END__
=begin # done in LIMS::Valiadate now
_validate_patient_data {
my $self = shift;
my $nhsno = $self->query->param('nhs_number');
# return profile as mixture of new_patient &
# patient_search_data validation profiles:
my $new_patient = $self->validate('new_patient');
my $patient_search = $self->validate('patient_search_data');
# add nhs_number constraint_method to patient_search_data:
$patient_search->{constraint_methods}->{nhs_number} = sub {
return LIMS::Local::Utils::check_nhsno( $nhsno );
};
my %profile = (
required => $new_patient->{required},
optional => $new_patient->{optional},
dependency_groups => $patient_search->{dependency_groups},
constraint_methods => $patient_search->{constraint_methods},
);
$profile{require_some} = {
patient_id => [ 1, qw(nhs_number unit_number) ],
}; # $self->debug(\%profile);
return \%profile;
}
=cut
#-------------------------------------------------------------------------------
=begin # direct entry from patient/select_patient.tt now
new_location : Runmode {
my $self = shift;
my $errs = shift;
my $patient_id = $self->param('id')
|| return $self->error('no id passed to '.$self->get_current_runmode);
$self->js_validation_profile('new_location');
my $patient = $self->model('Patient')->get_patient($patient_id);
$self->tt_params(
patient => $patient,
);
return $self->tt_process($errs);
}
=cut