# initialises all tables used by previous_cml_monitoring query (requests,
# patient_case, request_initial_screen, screens, request_result_summaries,
# lab_sections)
use WWW::Mechanize::TreeBuilder; # look_down()
use HTML::TreeBuilder::XPath; # findnodes()
use Test::WWW::Mechanize;
use LWP::Protocol::PSGI;
use Test::More;
use Test::Deep;
use Data::Printer alias => 'ddp';
use RequestForm::Test;
use RequestForm;
my $psgi_app = RequestForm::runner()->psgi_app;
LWP::Protocol::PSGI->register($psgi_app);
my $mech = Test::WWW::Mechanize->new;
WWW::Mechanize::TreeBuilder->meta->apply($mech);
my $args = form_tokens(); # warn $args;
my $addr = 'http://localhost/bcr-abl';
my $config = YAML::Tiny->read( 'config.yml' ) or die $!; # ddp $config;
my $required_fields = $config->[0]->{validation}->{required}; # ddp $required_fields;
my $src = patient_data(); # ddp $src;
my $cml_patient = $src->[2]; # ddp $cml_patient;
my $today = DateTime->today();
my $dbix = get_dbix();
run_schema(); # exit;
# initialise session with form tokens:
$mech->get_ok($addr . '?' . $args); # print_output($mech);
$mech->text_contains(
'request for RQ-PCR BCR-ABL estimation',
'OK: BCR-ABL request form loaded'
);
$mech->field(nhs_number => $cml_patient->{nhs_number});
$mech->submit_form(); # print_output($mech);
{
$mech->text_contains(
'Matching patient details found',
'OK: nhs number submitted, matching details found',
);
# check loaded fields:
my %formatted = (
nhs_number => $cml_patient->{nhs_number},
last_name => uc $cml_patient->{last_name},
first_name => $cml_patient->{first_name},
previous => $cml_patient->{previous},
gender => $cml_patient->{gender},
day => $cml_patient->{day},
month => $cml_patient->{month},
year => $cml_patient->{year},
);
while ( my ($key, $val) = each %formatted ) {
is( $mech->field($key), $val, "OK: $key field set" );
}
$mech->text_contains(
'Previous BCR-ABL results ',
'OK: previous BCR-ABL results retrieved',
);
$mech->text_contains(
'Last result < 90 days ago',
'OK: last result within minimum time required',
);
$mech->text_contains(
'Reason for request',
'OK: reason for request required',
);
}
{ # submit cml form (without reason):
my $reason = $cml_patient->{reason};
delete $cml_patient->{reason};
$mech->submit_form(fields => $cml_patient); # print_output($mech);
$mech->text_contains(
'Form validation failed',
'OK: form validation failed',
); # warn $mech->text;
$mech->text_contains( # can't localise 'Missing' div tag to reason - use text_contains
'Reason for request Missing',
'OK: missing field identified',
);
# recreate reason for request:
$cml_patient->{reason} = $reason;
$mech->submit_form(fields => $cml_patient); # print_output($mech);
$mech->has_tag_like(
p => qr/Form passed validation/,
'OK: validation passed',
);
# put _skip_pds back in (not retained in form):
$mech->field( _skip_pds => 1 );
# submit form to generate pdf (or HTML for non-deployment env):
$mech->submit_form(); # print_output($mech);
$mech->has_tag_like(
h3 => 'BCR-ABL Monitoring Request Form',
'OK: request form title field',
);
ok( # barcode class table:
$mech->look_down(_tag => 'table', class => 'hbc'), # table class="hbc"
'OK: has barcode table element',
);
{ # look for unique ref (eg 1428054818SILVERO):
my $ref = uc( $cml_patient->{last_name}
. substr($cml_patient->{first_name}, 0, 1) ); # ddp $ref;
$mech->has_tag_like(
td => qr/\d+$ref/,
'OK: unique ref id found',
);
}
# look for each required field (except special format fields):
for my $field (@$required_fields) { # warn Dumper [$field, $cml_patient->{$field}]; # next;
# defer testing fields with special formatting (tested below):
next if grep $field eq $_, # location_id not present at pdf generation stage:
qw(last_name nhs_number day month year location_id treatment);
$mech->has_tag( span => $cml_patient->{$field}, "OK: $field field found" );
}
{ # look for special format fields (dob, last_name, nhs_number) & cml_specific:
my $nhs_number = join ' ', # amazingly, this works:
( $cml_patient->{nhs_number} =~ /(\d{3})(\d{3})(\d{4})/ ); # ddp $nhs_number;
my $current_treament = sprintf '%s %s commenced %s',
@{ $cml_patient }{ qw(line_number treatment first_line_date) };
my $last_name = uc $cml_patient->{last_name};
my $dob = sprintf '%02d-%02d-%s', @{$cml_patient}{ qw(day month year) };
my %pre_formatted = (
diagnosis_date => $cml_patient->{diagnosis_date},
nhs_number => $nhs_number,
last_name => $last_name,
treatment => $current_treament,
reason => $cml_patient->{reason},
phase => $cml_patient->{phase},
dob => $dob,
);
while ( my ($key, $val) = each %pre_formatted ) {
$mech->has_tag( span => $val, "OK: $key field found" );
}
}
{ # compare content of request_form table with cml_patient data:
my $data = $dbix->select('request_form', '*')->hash; # ddp $data;
# delete keys not expected in cmp_patient data:
delete $data->{$_} for qw(id pds_code user_id created imported);
# get cols from request_form table:
my $cols = RequestForm::DB->new(dbname => 'test')
->get_cols('request_form'); # ddp $cols;
my %ref; # create new ref data stucture from cml_patient data:
$ref{$_} = $cml_patient->{$_} for grep $cml_patient->{$_}, @$cols;
my %dob = map +($_ => $cml_patient->{$_}), qw(year month day);
$ref{dob} = DateTime->new(%dob)->datetime;
$ref{last_name} = uc $ref{last_name}; # ddp %ref;
is_deeply( $data, \%ref, 'OK: request_form data matches patient data' );
}
{ # compare content of patient_bcr_abl table with cml_patient data:
my $tbl = $dbix->select('patient_bcr_abl', '*')->hash; # ddp $tbl;
my $src = clone($cml_patient); # ddp $src;
for ( qw/diagnosis_date first_line_date/ ) { # warn $src->{$date};
my $dt = _to_datetime_using_datecalc($src->{$_}); # warn $dt;
$src->{$_} = $dt->ymd('-');
} # ddp $src;
delete $tbl->{timestamp}; # not in $cml_patient
cmp_deeply( $src, superhashof($tbl), 'OK: db data is superhashof patient');
}
}
{ # change treatment details to 2nd line and phase to acute:
my $data = clone($cml_patient);
$data->{phase} = 'acute phase';
$data->{line_number} = '2nd line';
$data->{treatment_date} = '10/06/2012'; # for non-1st-line tx
$mech->get_ok($addr); # print_output($mech);
$mech->field(nhs_number => $data->{nhs_number});
$mech->submit_form(); # print_output($mech);
# check first_line_date & diagnosis_date fields set (picked up from db table):
is( $mech->field($_), $data->{$_}, "OK: $_ data retrieved" ) for
qw(first_line_date diagnosis_date);
# '** check this is correct **' - expect 4 instances of '*' for each date entry
# is( $mech->text =~ y/*/*/, 8, "OK: 'check this' flags set" ); # y/// same as tr///
{ # same thing using HTML::TreeBuilder::XPath::findnodes():
my @nodes = $mech->tree->findnodes( q!//div[@class='dfv-err']! ); # ddp @nodes;
my $phrase = '** check this is correct **';
# have 1 div with same classname (id=nhsno_result) but empty, so $_->content
# undef so use empty arrayref, in scalar context will get array count:
my $count = grep { ($_->content || [''])->[0] eq $phrase } @nodes; # ddp $count;
is( $count, 2, "OK: 'check this' flags set" );
}
$mech->submit_form(fields => $data); # print_output($mech);
$mech->has_tag_like(
p => qr/Form passed validation/,
'OK: validation passed',
);
# put _skip_pds back in (not retained in form):
$mech->field( _skip_pds => 1 );
# submit form to generate pdf (or HTML for non-deployment env):
$mech->submit_form(); # print_output($mech);
{ # just check for fields changed from original 1st line treatment submission:
my $current_treament = sprintf '%s %s commenced %s',
@{ $data }{ qw(line_number treatment treatment_date) };
my %fields = (
diagnosis_date => $data->{diagnosis_date},
treatment => $current_treament,
phase => $data->{phase},
);
while ( my ($key, $val) = each %fields ) {
$mech->has_tag( span => $val, "OK: $key field found" );
}
}
{ # compare content of patient_bcr_abl table with cml_patient data:
my $tbl = $dbix->select('patient_bcr_abl', '*')->hash; # ddp $tbl;
my $src = clone($data); # ddp $src;
for ( qw/diagnosis_date first_line_date/ ) { # warn $src->{$_};
my $dt = _to_datetime_using_datecalc($src->{$_}); # warn $dt;
$src->{$_} = $dt->ymd('-');
} # ddp $src;
delete $tbl->{timestamp}; # not in $cml_patient
cmp_deeply( $src, superhashof($tbl), 'OK: db data is superhashof patient');
}
}
done_testing(51);
#===============================================================================
sub run_schema {
my @schema = _schema();
do { $dbix->dbh->do($_) || die $dbix->error } foreach @schema; # $dbix->error doesn't work here
{ # initialise patients table with patient #3 from patients.conf:
my $info = $dbix->query("PRAGMA table_info(patients)")->hashes; # warn Dumper $info;
my @cols = grep { $_ ne 'id' } map $_->{name}, @$info; # warn Dumper \@cols;
my %h;
$h{$_} = $cml_patient->{$_} for @cols; # warn Dumper \%data;
# dob from components:
$h{dob} = join '-', @{$cml_patient}{qw(year month day)}; # warn Dumper \%h;
$dbix->insert('patients', \%h);
}
{ # requests, request_initial_screen, request_result_summaries:
my %h = (
2 => 0.004,
4 => 0.014,
6 => 0.018,
8 => 0.023,
);
for my $i ( reverse sort keys %h ) { # warn $i;
$dbix->dbh->do( qq!
INSERT INTO requests(patient_case_id, created_at)
SELECT 1, date('now', "-$i months")! ) || die $dbix->error;
my $ratio = $h{$i}; # eg 0.004%
$dbix->dbh->do(
qq!INSERT INTO request_result_summaries(lab_section_id,
results_summary, time)
SELECT 1, "BCR-ABL : ABL ratio = ${ratio}%",
date('now', "-$i months")! ) || die $dbix->error;
$dbix->dbh->do( q!INSERT INTO request_initial_screen(screen_id)
VALUES(1)! ) || die $dbix->error; # doesn't need auto-increment
}
# my $t1 = $dbix->select('requests','*')->hashes; ddp $t1;
# my $t2 = $dbix->select('request_initial_screen','*')->hashes; ddp $t2;
# my $t3 = $dbix->select('request_result_summaries','*')->hashes; ddp $t3;
# _previous_cml_monitoring() # complex query
}
sub _schema {
return (
# don't need full table schemas, just enough for previous_cml_monitoring query:
q{
CREATE TABLE requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
patient_case_id INTEGER,
created_at INTEGER
)
},
q{
CREATE TABLE patient_case (
id INTEGER PRIMARY KEY AUTOINCREMENT,
patient_id INTEGER
)
},
q{
INSERT INTO patient_case(patient_id) VALUES(1)
},
# request_initial_screen.request_id AUTOINCREMENT for sync with requests
q{
CREATE TABLE request_initial_screen (
request_id INTEGER PRIMARY KEY AUTOINCREMENT,
screen_id INTEGER
)
},
q{
CREATE TABLE screens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT
)
},
q{
INSERT INTO screens(description) VALUES('Follow-up CML (PB)')
},
# request_result_summaries.request_id AUTOINCREMENT for sync with requests
q{
CREATE TABLE request_result_summaries (
request_id INTEGER PRIMARY KEY AUTOINCREMENT,
lab_section_id INTEGER,
results_summary TEXT,
time TEXT
)
},
q{
CREATE TABLE lab_sections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
section_name TEXT
)
},
q{
INSERT INTO lab_sections(section_name) VALUES('Molecular')
},
q{
CREATE TABLE patient_bcr_abl (
nhs_number INTEGER PRIMARY KEY,
diagnosis_date TEXT,
first_line_date TEXT,
treatment TEXT,
line_number TEXT,
phase TEXT,
timestamp TEXT
)
},
);
}
sub _to_datetime_using_datecalc {
LIMS::Local::Utils::to_datetime_using_datecalc(@_);
}
sub _previous_cml_monitoring { # check data:
my $nhs_number = $cml_patient->{nhs_number};
my $sql = qq!
select date(r.created_at) as 'registered', rrs.results_summary as 'result'
from requests r
join ( patient_case pc join patients p on pc.patient_id = p.id )
on r.patient_case_id = pc.id
join ( request_initial_screen ris join screens s
on ris.screen_id = s.id ) on ris.request_id = r.id
join ( request_result_summaries rrs join lab_sections ls
on rrs.lab_section_id = ls.id ) on rrs.request_id = r.id
where s.description = 'Follow-up CML (PB)'
and ls.section_name = 'Molecular'
and p.nhs_number = ?
order by r.created_at desc!;
my $data = $dbix->query($sql, $nhs_number)->hashes; ddp $data;
}
}