package Main; # rebuilds patients, patient_case, requests, request_specimen # & request_initial_screen tables: # anonymises patients if source is LTH server =begin # how it works: passes hilis3 data to Roles::Patient::get_patient_id to see if nhs_number match exists, if so uses patients.id. Otherwise, if hilis3 data has nhs_no, creates new patient record & uses new patients.id. Next, tries to match on last_name, first_name, dob & unit_number. =cut use Moose; with qw( Role::User Role::Main Role::Report Role::Specimen Role::Patient Role::Referrer Role::RebuildTables ); use namespace::clean -except => 'meta'; has $_ => (is => 'ro', isa => 'HashRef', required => 1) foreach qw( db sql ); has $_ => ( is => 'ro', required => 1 ) foreach qw(log_file path_to_app ); has cfg => ( is => 'ro', isa => 'Config::Tiny', required => 1 ); # need to force delay until after db passed in from new(): has $_ => ( is => 'ro', isa => 'HashRef', lazy_build => 1 ) foreach qw( gp_practice referral_source request_status_options_map resolved_nhsno_duplicates_map screen_map signature_userid_map specimen_map substitute_user unknown_source username_userid_map substitute_screening_terms unknown_referrer_id_referrer_department_id_map ); has $_ => ( is => 'rw', isa => 'HashRef', default => sub { {} } ) foreach qw( no_referral_source nhs_number_map ); has path_to_app => ( is => 'ro', isa => 'Str', required => 1 ); __PACKAGE__->meta->make_immutable; use Data::Dumper; use Anonymise::LIMS; my @tables = qw( patients requests patient_case request_trial request_option request_report request_specimen request_external_ref request_initial_screen request_gross_description request_diagnosis_history ); sub convert { my $self = shift; #=begin # open my $file, '>', './new.txt' or die $!; $self->rebuild_tables(\@tables); my $dbix4 = $self->db->{dbix4}; my $dbix3 = $self->db->{dbix3}; my $log_file = $self->log_file; $dbix4->keep_statements = 50; # might speed up process with a lot of queries my $REQ = q! SELECT m.`DBID`, m.`HMDS`, m.`Date`, YEAR(m.`Date`) as 'year', m.`Specimen`, m.`Source`, m.`Clinician`, m.`BlockRef`, m.`Study`, m.`Urgent`, m.`DoI`, m.`Private`, m.`CopyTo`, m.`Teaching`, m.`TimeStamp` as 'main_time', p.`LName`, p.`FName`, p.`DoB`, p.`Sex`, p.`PatNo`, p.`NHSNo`, p.`TimeStamp` as 'pid_time', s.`OrgCode`, c.`cons_code`, c.`last_name`, r.`Rpt_ID`, r.`InitialDiag`, r.`Screener`, r.`ScreenDate`, r.`Diagnosis`, r.`ReportBy`, r.`ReportDate`, r.`AuthorisedBy`, r.`AuthDate`, r.`ReviseDiag`, r.`RevisedBy`, r.`ReviseDate`, r.`FinalDiag`, r.`FinalBy`, r.`FinalDate`, r.`Comment`, r.`ClinDetails`, r.`NewDiagnosis`, r.`SpecQuality`, r.`GrossDesc`, r.`TimeStamp` as 'ReportTime' FROM Main m LEFT JOIN Report r on m.DBID = r.Rpt_ID LEFT JOIN Source s on m.Source = s.Src_ID LEFT JOIN PID p on m.Pat_ID = p.P_ID LEFT JOIN Clinician c on m.Clinician = c.c_id ORDER BY m.DBID!; my $t0 = DateTime->now; my $hilis3 = $dbix3->query($REQ); my $duration = DateTime->now - $t0; printf "hilis3 query took %sm %ss\n", $duration->minutes, $duration->seconds; REQUEST: while ( my $vals = $hilis3->hash ) { $vals->{dbid} % 1000 || print $vals->{dbid}, "\n"; # print $SELECT_PATIENT_ID . "\n"; my @first_names = split ' ', $vals->{fname}; if ($first_names[1]) { if (length $first_names[1] > 1) { @{$vals}{qw/fname mname/} = @first_names; } else { # don't want single letter middle name ! $vals->{fname} = $first_names[0]; } } #=cut #=begin # retrieve/create patient.id: my $patient_id = $self->get_patient_id($vals) or die 'No patient_id value set'; my $referral_source_id = 0; { # changed Notts. City Hospital org_code to match current etrust file: no warnings 'uninitialized'; $vals->{orgcode} =~ s/^(RCS)/RHA/; } my $org_code = $vals->{orgcode} || ''; my $clinician = $vals->{clinician} || ''; my $source = $vals->{source} || ''; # cons_code only exists for Clinician > 643: my $referrer_code = $vals->{cons_code} || $self->get_referrer_code($vals); my $referrer_id = $self->convert_referrer_code_to_id($referrer_code); if (! $referrer_id) { $referrer_id = $clinician =~ /\A(G\d+)/ ? $self->unknown_referrer_id->{practitioner} : $self->unknown_referrer_id->{clinician}; warn "Cannot find referrer.id for $referrer_code [$vals->{dbid}]"; } # if Main.Clinician = G\d+ : if ( my ($g_id) = $clinician =~ /\A(G\d+)/ ) { my $practice_code = $self->gp_practice->{$g_id}->{practice_code}; $referral_source_id = $self->referral_source->{$practice_code} || $self->unknown_source->{78}; # unknown practice } # unknown OrgCode due to m.Source = 57 (Other), 78 (Surgery/MC), # 54 (Mid Yorks Nuffield), PathLinks, Ghana, etc elsif ( my $ref_source = $self->unknown_source->{$source} ) { # warn ($vals->{dbid},'UNKNOWN SOURCE'); $referral_source_id = $ref_source; } # if hilis3.OrgCode found in hilis4.referral_sources: elsif ( $org_code && $self->referral_source->{$org_code} ) { $referral_source_id = $self->referral_source->{$org_code} # || warn Dumper ( $org_code, $self->referral_source->{$org_code}); } # can't set referral_source_id: else { $self->no_referral_source->{$source} += 1; die "No referral_source_id value set for $source on $vals->{dbid}\n"; # next REQUEST; # no point - lost the request !! } my %case_data = ( patient_id => $patient_id, referral_source_id => $referral_source_id, unit_number => $vals->{patno} || 'UNKNOWN', time => $vals->{pid_time}, ); # retrieve or create new case_id: my $case_id = $self->get_case_id(\%case_data); if (! $case_id ) { $case_id = $self->find_or_create_new_patient_case(\%case_data); } $case_id or die 'No case_id value set'; { # get referrer_department.id from referrer_id & referral_source_id: my $sql = q!select id from referrer_department where referrer_id = ? and parent_organisation_id = ( select parent_organisation_id from referral_sources rs where rs.id = ?)!; my $referrer_department_id = $dbix4->query( $sql, $referrer_id, $referral_source_id )->list; if (! $referrer_department_id) { my $referrer_department_id_map = $self->unknown_referrer_id_referrer_department_id_map; my $sql = 'select description from referral_types t join referral_sources s on s.referral_type_id = t.id where s.id = ?'; my $referral_type = $dbix4->query( $sql, $referral_source_id )->list; # warn unless unknown referrer id's: warn "Cannot find referrer_department_id for referrer_id " . "$referrer_id & referral_source_id $referral_source_id " . "[$vals->{dbid}]" unless map { $referrer_id == $_ } ($self->unknown_referrer_id->{clinician}, $self->unknown_referrer_id->{practitioner}); # set to referrer_department.id for unknown referrer: $referrer_department_id = $referrer_department_id_map->{$referral_type}; } my $status_option_id = $self->_get_request_status($vals); # create new request: my %request_data = ( year => $vals->{year}, request_number => $vals->{hmds}, patient_case_id => $case_id, referrer_department_id => $referrer_department_id, status_option_id => $status_option_id, created_at => '0000-00-00 00:00:00', # will be updated in History updated_at => $vals->{main_time}, ); $dbix4->insert('requests', \%request_data); } # add request.id to $vals for use in do_specimens() & do_report(): $vals->{_request_id} = $dbix4->last_insert_id(undef, undef, 'requests', 'id') || die 'no request_id found'; $self->do_specimens($vals); $self->do_clinical_trials($vals); $self->do_additional_options($vals); $self->do_report($vals); # block_ref: if ( my $block_ref = $vals->{blockref} ) { my ($ref, $status) = $block_ref =~ /(.*)\[(.*)\]\Z/; # foo [bar] if ($ref) { $ref =~ s/\s+$// }; # remove any trailing space(s) # set external ref to $ref if exists, or $block_ref: my $value = $status ? $ref : $block_ref; my %data = ( request_id => $vals->{_request_id}, external_reference => $value, ); if ($status) { $data{status} = $status } $dbix4->insert('request_external_ref', \%data); } } #=cut # need to anonymise patients if source is LTH && andLinux host: my $src = $self->cfg->{_}->{src_host}; # if ( $src =~ /\A163\.160/ && $ENV{PULSE_SERVER} ) { if ( $ENV{PULSE_SERVER} ) { # $self->_anonymise(); } # test converted diagnosis eq original diagnosis: print "Testing diagnoses.....\n"; $self->test_diagnoses; my $no_referral_source = $self->no_referral_source; print "$_: $no_referral_source->{$_}\n" foreach keys %$no_referral_source; # print $log_file Dumper $self->unknown_referrers; print 'Total unknown referrers: '.$self->unknown_referrers_total, "\n"; } sub test_diagnoses { my $self = shift; my $dbix3 = $self->db->{dbix3}; my $dbix4 = $self->db->{dbix4}; my $hilis3_sql = q!select HMDS, year(Date) as 'year', if(FinalDiag is not null, FinalDiag, if(ReviseDiag is not null, ReviseDiag, Diagnosis)) as 'diagnosis' from Main, Report where DBID = Rpt_ID!; my $hilis4_diagnosis_sql = q!select t3.name from requests t1 join request_report t2 on t2.request_id = t1.id join diagnoses t3 on t2.diagnosis_id = t3.id where t1.request_number = ? and t1.year = ?!; my $hilis3 = $dbix3->query($hilis3_sql); while ( my $vals = $hilis3->hash ) { # warn $diagnosis; my $hilis3_diagnosis = $vals->{diagnosis}; my $hilis4_diagnosis = $dbix4->query( $hilis4_diagnosis_sql, $vals->{hmds}, $vals->{year} )->list; { no warnings 'uninitialized'; # some records without diagnosis if ( $hilis3_diagnosis ne $hilis4_diagnosis ) { next if $vals->{year} <= 2003 && $hilis4_diagnosis eq 'Not required'; # Molecular warn "$hilis3_diagnosis ne $hilis4_diagnosis for $vals->{hmds} / $vals->{year}"; } } } } sub _anonymise { my $self = shift; print "anonymising patients .. "; my $dbh = $self->db->{dbix4}; # warn $self->path_to_app; # get all existing nhs_numbers so we don't attempt to duplicate one still unprocessed: my $sql = 'select nhs_number from patients where nhs_number is not null'; my $all_nhs_numbers = $dbh->query( $sql )->flat; my $anonymiser = Anonymise::LIMS->new( path_to_app => $self->path_to_app, existing_nhs_numbers => $all_nhs_numbers, ); # Anonymise::LIMS::anonymise_patient() needs hashref of PID rows: my $patient = $dbh->query('select id, last_name, first_name, dob, gender, nhs_number from patients'); while ( my $vals = $patient->hash ) { # anonymise $vals: $anonymiser->anonymise_patient($vals); my $id = $vals->{id}; delete $vals->{id}; $dbh->update('patients', $vals, { id => $id } ); } print "finished\n"; } # will be updated in RequestStatus if outstanding tests: sub _get_request_status { my $self = shift; my $vals = shift; my $status_options_map = $self->request_status_options_map; # set all requests before 2008 to complete: return $status_options_map->{complete} if $vals->{year} < 2008; return $status_options_map->{complete} if $vals->{authorisedby}; return $status_options_map->{reported} if $vals->{reportby}; return $status_options_map->{screened} if $vals->{screener}; return $status_options_map->{new}; } # Moose builder methods: sub _build_screen_map { my $self = shift; my $dbh = $self->db->{dbix4}; # create Report.InitialDiag -> screen.id map: my $map = $dbh->query( 'select description, id from screens' )->map; return $map; } sub _build_request_status_options_map { my $self = shift; my $dbh = $self->db->{dbix4}; my $map = $dbh->query( q!select description, id from status_options! )->map; return $map; } sub _build_resolved_nhsno_duplicates_map { my $self = shift; my $dbh = $self->db->{dbix3}; my $map = $dbh->query('select * FROM test.ResolvedNHSNo')->map_hashes('nhsno'); return $map; } sub _build_signature_userid_map { my $self = shift; my $dbh = $self->db->{dbix3}; # create Users.Signature -> user.id map: my $map = $dbh->query( q!select Signature, UserID from Users where Signature is not null! )->map; return $map; } sub _build_substitute_screening_terms { my $self = shift; return { 'Myeloma/P\'cytoma' => $self->screen_map->{'Myeloma/Plasmacytoma'}, 'Reactive Node' => $self->screen_map->{'Reactive node'}, '[NULL]' => $self->screen_map->{Other}, '---' => $self->screen_map->{ITP}, }; } sub _build_substitute_user { my $self = shift; return { 'S Richmond' => $self->username_userid_map->{bagguley}, Molecular => $self->username_userid_map->{evans}, Admin => $self->username_userid_map->{raj}, # for early hilis requests: admin => $self->username_userid_map->{raj}, fielding => $self->username_userid_map->{douglas}, westgate => $self->username_userid_map->{wilkinson}, }; } sub _build_username_userid_map { my $self = shift; my $dbh = $self->db->{dbix4}; # create users.username -> users.id map: my $map = $dbh->query( 'select username, id from users' )->map; return $map; } sub _build_specimen_map { my $self = shift; my $dbh = $self->db->{dbix4}; my $specimen_map = $dbh->query('SELECT sample_code, id FROM specimens')->map; # ->map_hashes('sample_code'); # don't need this if only 2 fields mapped # truncated/incorrect sample_codes: $specimen_map->{E} = 13; # EF $specimen_map->{L} = 25; # LU (checked) $specimen_map->{X} = 43; # XU (checked) $specimen_map->{SER} = 33; # SE return $specimen_map; } sub _build_gp_practice { my $self = shift; my $dbh = $self->db->{dbix3}; my $practices = $dbh->query('SELECT g_id, gp_code, practice_code FROM GP') ->map_hashes('g_id'); return $practices; } sub _build_referral_source { # warn 'HERE'; my $self = shift; # warn Dumper $self->db; my $dbh = $self->db->{dbix4}; my $referral_source = $dbh->query('SELECT organisation_code, id FROM referral_sources')->map; # print $log_file Dumper $referral_source; exit; } sub _build_unknown_referrer_id_referrer_department_id_map { my $self = shift; my $dbh = $self->db->{dbix4}; # get referrer_department.id from referrer_department for default unknown referrers: my $sql = q!select id from referrer_department where referrer_id in ( select id from referrers where national_code in ( select default_unknown from referral_types where description = ? ) )!; my %referrer_department_id = ( hospital => $dbh->query( $sql, 'clinician' )->list, practice => $dbh->query( $sql, 'practitioner' )->list, ); return \%referrer_department_id; } sub _build_unknown_source { my $self = shift; my $dbh = $self->db->{dbix4}; my $sql = q!SELECT id FROM referral_sources WHERE organisation_code = ?!; my %unknown_source = ( 54 => $dbh->query($sql, 'NT225' )->list, # Mid-Yorks -> Leeds Nuffield 56 => $dbh->query($sql, 'RHALB' )->list, # Notts. City Hospital 57 => $dbh->query($sql, 'X99999')->list, # Other hospital 78 => $dbh->query($sql, 'V81999')->list, # Surgery/Med. Ctr 217 => $dbh->query($sql, 'RWD00' )->list, # PathLinks 220 => $dbh->query($sql, 'RTD00' )->list, # Newcastle Uni. 234 => $dbh->query($sql, 'GHANA' )->list, # Ghana ); # warn Dumper \%unknown_source; return \%unknown_source; } 1;