#! /usr/bin/perl use strict; no strict 'refs'; use warnings; use DBI; use Date::Calc qw(:all); use File::Temp qw/ tempfile tempdir /; my $version = '0.5'; ############################################################################# # CONTENTS ############################################################################# #Section 1. Description and copyright statement #Section 2. Setup and instructions #Section 3. User definable variables #Section 4. Code ############################################################################# # Section 1. DESCRIPTION AND COPYRIGHT STATEMENT ############################################################################# #this script is used to scan patient files in for use in OSCAR EMR # (c) Robbie Coull, 2009 # robbie@coull.net # # This code is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # see . ############################################################################# # Section 2. SETUP AND INSTRUCTIONS ############################################################################# # Copy this script to a suitable location (eg: /usr/local/scripts) # sudo mv oscarscan.pl /usr/local/scripts/oscarscan.pl # Make sure that it can be used by any user # sudo chmod 777 /usr/local/scripts/oscarscan.pl #note: must have mysql root password set to '' and the test database created #for this to work and make sure MySQL is running! # sudo apt-get install mysql-server # sudo /etc/init.d/mysql start #For this script to work you need to install perl modules # sudo perl -MCPAN -e shell # cpan> install Bundle::CPAN # cpan> install Bundle::DBI # cpan> install Date::Calc # cpan> install File::Temp # cpan> exit #once the modules are installed, make sure MySQL on the local server is NOT running # sudo /etc/init.d/mysql stop #turn MySQL off in the services section of admin to prevent it starting at boot #For this script to work #you need to have the following programs installed on Ubuntu: # pdftk # imagemagic # #sudo apt-get install pdftk #sudo apt-get install imagemagick #if OSCAR is installed on a remote server #then you will need to set up a keychain access to that server #to allow this script to access the upload folders # #eg: ssh -D 8080 -fN $USER@$remoteserver # ssh -nNL 3306:127.0.0.1:3306 $USER@$remoteserver &> /dev/null & # #which provides SSH tunnel access to the server and to the MySQL server #I recommend using three user logins for MySQL (read only, read write, admin) #This helps prevent disasters when the wrong command is issued by accident. #If you do most of your database work from a read only user, then you can't #accidentally delete anything. # #Set up a read-only user and a read-write user as follows: #log in to mysql #mysql -uroot -p # #GRANT all ON oscar_mcmaster.* TO readonly@localhost IDENTIFIED BY '[password]'; #GRANT select ON oscar_mcmaster.* TO readwrite@localhost IDENTIFIED BY '[password]'; # #exit # #You will need to enter the user names and passwords of these two users #in the section below - make sure this script can only be accessed by trusted users! ############################################################################# # Section 3. USER VARIABLES ############################################################################# #scanner settings my $device = ' '; # scan dimensions my $sizeX = '-x 215.9'; my $sizeY = '-y 279.4'; #scan settings my $resolution = '200'; my $mode = 'Lineart'; my $imageformat = 'tiff'; #upload settings my $uploadfolder = '/usr/local/share/scanned_for_upload'; my $serveraddress = 'oscarpei.net'; my $serverlogin = getlogin(); #set duplexmodeon to '1' to offer a duplex option when scanning my $duplexmodeon = 1; #set manual_labs to '1' to ask for input of lab results my $manual_labs = 1; #set the length in digits of the local HIN my $province = 'PE'; my $phn_length = '8'; my $phn_length_plus1 = $phn_length+1; my $phn_length_plus2 = '12'; #set diagnostics on or off my $diagnostic; #$diagnostic = 'on'; my %biochem; $biochem{'01.Glucose AC'} = 'GLUC'; $biochem{'02.Glucose random'} = 'GLUR'; $biochem{'03.Sodium'} = 'NA'; $biochem{'04.Potassium'} = 'KPOT'; $biochem{'05.Urea'} = 'UREA'; $biochem{'06.Creatinine'} = 'CRTN'; $biochem{'07.AST'} = 'AST'; $biochem{'08.ALT'} = 'ALT'; $biochem{'09.Creatine Kinase'} = 'CK'; $biochem{'10.GGT'} = 'GGT'; $biochem{'11.eGFR'} = 'EGFR'; $biochem{'12.Cholesterol'} = 'TCHL'; $biochem{'13.HDL Cholesterol'} = 'HDL'; $biochem{'14.LDL Cholesterol'} = 'LDL'; $biochem{'15.Triglycerides'} = 'TG'; $biochem{'16.TSH'} = 'TSH'; my %hematology; $hematology{'1.Hgb'} = 'HB'; $hematology{'2.Hgb A1c'} = 'A1C'; $hematology{'3.Sedementation Rate (ESR)'} = 'ESR'; my %coagulation; $coagulation{'1.INR'} = 'INR'; my %pref; $pref{mysql_database} = 'oscar_mcmaster'; $pref{mysql_read} = 'read_user_name'; $pref{mysql_read_pass} = 'read user password'; $pref{mysql_write} = 'write user name'; $pref{mysql_write_pass} = 'write user password'; $pref{mysql_host} = '127.0.0.1'; $pref{mysql_port} = '3306'; $pref{mysql_log} = "/home/$serverlogin/oscar_scan_sql.log"; my @document_type = qw( lab consult insurance legal oldchart radiology pathology others ); my @test = qw( biochem hematology coagulation ); my %preventions = ( 'MAM' => 'Mammogram', 'PAP' => 'Pap Smear Test' ); #month variables my %month_names = ( 'JAN' => '1', 'FEB' => '2', 'MAR' => '3', 'APR' => '4', 'MAY' => '5', 'JUN' => '6', 'JUL' => '7', 'AUG' => '8', 'SEP' => '9', 'SEPT' => '9', 'OCT' => '10', 'NOV' => '11', 'DEC' => '12', 'JANUARY' => '1', 'FEBUARY' => '2', 'MARCH' => '3', 'APRIL' => '4', 'MAY' => '5', 'JUNE' => '6', 'JULY' => '7', 'AUGUST' => '8', 'SEPTEMBER' => '9', 'OCTOBER' => '10', 'NOVEMBER' => '11', 'DECEMBER' => '12', ); my $this_month_name; my @month_names = qw( blank Jan Feb Mar Apr May Jun July Aug Sept Oct Nov Dec ); #declare variables my $scanimage; my $scanningtodo; my $thisbatch; my $correct; my $correct2; my $last_name; my $first_name; my $demographic_no; my $phn; my $hc_type; my $description; my $document_type; my %bloods; my $source; my $patient_details; my $lab; my $test; my %test; my $this_test; my $this_result; my $test_name; my %test_result; my $document_date; my $duplex; my $duplex1; my $add_pages; my $document; my $tempdir; my $scan_document; my $destination; my $scan_ok; my $add_more_pages; my $no_more_pages; my $quit_requested; my $new_patient_requested; my $document_year; my $document_month; my $document_day; my $lab_hash_name; my %this_lab_hash; my $this_measurement; my $measurement_type; my %measurement_instructions; my $this_test_result; my $provider_no; my $login_id=''; my $random; my $outputfile; my $document_number; my $results_stored_message; my $temp; my $image_file; my $image_file2; my $prevention; my $patient_provider; #declare subroutine variables my $string_to_be_padded; my $desired_string_length; my $padding_character; my $right_or_left_justify; my $decimal_places; my $decimals; my $decimal_places_digit; my $missing_digits; my $mysql_user; my $mysql_login; my $mysql_pass; my $dbh_name; my $mysql_preferred_user; my $mysql_original_user; my $dbh_inuse; my $sql; my @mysql_row; my $mysql_query; my $mysql_record; my @mysql_result; my @mysql_insert; my $mysql_update_table; my $mysql_update_table_array; my $mysql_disconnect; my $error; my $this_sql_insert; my $mysql_insert_table; my $sql_data; my $tempath; my $path; my $mysql_result; my $mysql_set_insert; my %mysql_set_insert; ############################################################################# # Section 4. CODE ############################################################################# #get the date time (my $sec,my $min,my $hr,my $mday,my $mon,my $year,my $wday,my $yday,my $isdst) = localtime(time); my $longyr = $year + 1900; my $fixmo = $mon + 1; if ($isdst == 1) { my $tz = "CDT"; } else { my $tz = "CST"; } if ($hr<10) { my $hr="0".$hr; } if ($min<10) { my $min="0".$min; } my $sqlmo=$fixmo; if ($sqlmo<10) { $sqlmo="0".$sqlmo; } my $sqlmday=$mday; if ($sqlmday<10) { $sqlmday="0".$sqlmday; } my $sql_date="$longyr-$fixmo-$mday"; my $sql_time="$hr:$min:$sec"; my $sql_datetime="$sql_date $sql_time"; $device = find_scanner(); #set up the scanner functions by scanner my $adf_source = '--source ADF'; #use this line for the HP my $duplex_source = '--source Duplex'; #use this line for the HP if ($device=~m/fujitsu/i) { $adf_source = ' '; #use this line for the fujitsu $duplex_source = "--source 'ADF Duplex'"; #use this line for the fujitsu } #connect to the mysql database mysql_connect(); #download the measurement instructions list mysql_hash_query("SELECT * FROM measurementType"); for $this_measurement (@mysql_result) { $measurement_type=${$this_measurement}{'type'}; $measurement_instructions{$measurement_type}=${$this_measurement}{'measuringInstruction'}; } header(); print "Scanner.................$device\n"; print "ADF command:............$adf_source\n"; print "ADF duplex command:.....$duplex_source\n"; print "Scan dimension width.... $sizeX\n"; print " height... $sizeY\n"; print " resolution......... $resolution\n"; print " mode............... $mode\n"; print " image format....... $imageformat\n"; print "Duplex mode (1=on)...... $duplexmodeon\n"; print "Manual lab entry (1=on). $manual_labs\n"; print "Login ID................ $login_id\n"; print "Server address.......... $serveraddress\n\n"; print "Upload folder location:\n$uploadfolder\n"; print "\n"; #get the provider_no $provider_no = mysql_array_query("SELECT provider_no FROM security WHERE user_name='$serverlogin'"); until ($provider_no) { print "USER NAME: [$serverlogin] "; $login_id=; $login_id=~s/[^A-Z0-9\.\-\_]//gi; unless ($login_id) { $login_id = $serverlogin; } $provider_no = mysql_array_query("SELECT provider_no FROM security WHERE user_name='$login_id'"); unless ($provider_no) { print "\n"; print "Sorry, user name $login_id is not recognised.\n"; print "\n"; } } ##variables the script uses my $newpatient='1'; #set up blank references to avoid errors my $empty_original_patient_details = qw{}; #now keep going until $newpatient is null while ($newpatient eq '1') { # variables to increment file names to store scanned pages $scanningtodo='3'; $thisbatch='0'; header(); if ($diagnostic) { print "DIAGNOSTICS20: \$phn='$phn', looking for new patient detaisl\n"; } #we need to find a new patient $correct = '2'; #keep looking until the user selects '1' for $correct while ($correct eq '2') { if ($diagnostic) { print "DIAGNOSTICS30: \$correct='$correct', keep looking until \$correct=1\n"; } #reset the patient details ${$patient_details}{'last_name'}= q{}; $patient_details = 'empty_original_patient_details'; $phn ='1'; #keep asking until the user gets a last_name until (${$patient_details}{'last_name'}) { $hc_type = $province; if ($diagnostic) { print "DIAGNOSTICS40: \${\$patient_details}{'last_name'}='${$patient_details}{'last_name'}', keep asking for user name\n"; } #keep getting input until you get a $phn_length digit number while ($phn!~m/^\d{4,30}$/) { if ($diagnostic) { print "DIAGNOSTICS50: \$phn='$phn', keep looking for $phn_length long input\n"; } print "Enter 2 letter province code and health # (eg: PE 12345678)\n"; print "[return empty to quit]\n"; print "\n"; print "Health#: "; $phn = ; chomp $phn; $phn = uc $phn; if ($phn=~m/^[A-Z]{2}[\ \:\-\,\.]?/) { ($hc_type, $phn) = split (/[\ \:\-\,\.]/, $phn); }; $phn=~s/\D//g; unless ($phn) { print "\n\nGoodbye.\n\n"; exit; } if ($hc_type eq $province) { #pad out with zeros $phn = pad_for_tabulation($phn, $phn_length_plus2, '0' ,'R' ,''); #some labs add extra zeros to the start of the PHN, remove it while (($phn=~m/^\d{$phn_length_plus1}/) && ($phn=~m/^0/)) { $phn=~s/^0//; } unless ($phn=~m/^\d{$phn_length}$/) { header(); print "Health Numbers must be 8 digits long - $phn is not an 8 digit number.\n\n\n"; } } } #now connect to the mysql server and pull this patient's details $patient_details = mysql_hash_query("SELECT last_name, first_name, demographic_no, provider_no FROM demographic WHERE hin='$phn' && hc_type='$hc_type'"); #check a patient was found unless (defined $patient_details) { #undefined strings cause errors, so define the empty string if nothing found $patient_details = 'empty_original_patient_details'; } unless (${$patient_details}{'last_name'}) { header(); print "No record found for Health # $phn.\n\n\n"; #the user will need to enter a new number, so reset $phn $phn = '1'; } } $last_name = ${$patient_details}{'last_name'}; $first_name = ${$patient_details}{'first_name'}; $demographic_no = ${$patient_details}{'demographic_no'}; $patient_provider = ${$patient_details}{'provider_no'}; header(); patient_details(); print "\n"; print "Is this the correct patient?\n"; print "\n"; print "1) Yes.\n"; print "\n"; print "2) No. I want to enter the Health# again.\n"; print "\n"; print "Q) QUIT. I'm finished.\n"; print "\n"; $correct = ; $correct = lc $correct; $correct=~s/[^12q]//g; if ($correct eq 'q') { print "\n\nGoodbye.\n\n"; exit; } if ($correct eq '2') { header(); #the user has chosen to enter a new number, so reset $phn $phn = '1'; } } #we have a PHN that the user is happy with $newpatient=2; while ($newpatient eq '2') { $description = q{}; if ($diagnostic) { print "DIAGNOSTICS60: \$newpatient='$newpatient', keep going while \$newpatient eq '2'\n"; } until ($description) { if ($diagnostic) { print "DIAGNOSTICS70: \$document='$document', keep going until \$document is not ''\n"; } $document = qw{}; while ($document!~m/^\d$/) { $prevention = qw{}; $lab = qw{}; if ($diagnostic) { print "DIAGNOSTICS80: \$document='$document', keep going while \$document is not a single digit\n"; } header(); patient_details(); print "\n" ; print "A. Patient Access Form\n" ; print "C. Clinical Chemistry\n" ; print "H. Hematology\n" ; print "I. INR / Coagulation\n" ; print "M. $preventions{'MAM'}\n" ; print "P. $preventions{'PAP'}\n" ; print "1. Lab, other\n" ; print "2. Consult\n" ; print "3. Insurance\n" ; print "4. Legal\n" ; print "5. Old Chart\n" ; print "6. Radiology\n" ; print "7. Pathology\n" ; print "8. Others\n" ; print "\n" ; print "Type of document: "; $document = ; $document = lc $document; $document=~s/[^12345678achimp]//g; if ($document eq 'a') { $document = '8', $description = 'Patient Access Form', } if ($document eq 'c') { $document = '6'; $description = 'Clinical chemistry'; $lab = 1; } if ($document eq 'i') { $document = '6'; $description = 'Coagulation'; $lab = 3; } elsif ($document eq 'h') { $document = '6'; $description = 'Hematology'; $lab = 2; } elsif ($document eq 'm') { $document = '6'; $description = $preventions{'MAM'}; $prevention = 'MAM'; } elsif ($document eq 'p') { $document = '7'; $description = $preventions{'PAP'}; $prevention = 'PAP'; } else { --$document; } unless ($document_type[$document]) { $document = q{}; } } while ($description eq '') { if ($diagnostic) { print "DIAGNOSTICS90: \$description='$description', keep going while \$desription eq ''\n"; } header(); patient_details(); print "Document type: $document_type[$document]\n"; print "\n"; print "Description: "; $description = ; $description=~s/[^A-Z0-9\ \-\_]//gi; print "\n"; } $document_date=''; $document_year=$longyr; while (($document_date!~m/^\d{4}-\d{1,2}-\d{1,2}$/) || ($document_year>$longyr)) { if ($diagnostic) { print "DIAGNOSTICS100: \$document_date='$document_date', keep going while \$document_date is not in the correct format\n"; } print "Document date: ($mday-$month_names[$fixmo]-$longyr) "; $document_date = ; $document_date = uc $document_date; $document_date=~s/[.: ]/\-/g; $document_date=~s/[^A-Z0-9\-]//g; if ($document_date) { #look for word versions of months for $this_month_name (keys %month_names) { $document_date=~s/\-$this_month_name\-/\-$month_names{$this_month_name}\-/; } #correct if DD-MM-YYYY format entered if ($document_date=~m/^(\d{1,2})\-(\d{1,2})\-(\d{2,4})$/) { $document_date="$3-$2-$1"; } #add year if only MM-DD format entered if ($document_date=~m/^(\d{1,2})\-(\d{1,2})$/) { $document_date="$longyr-$1-$2"; } #correct year if only 2 digits entered if ($document_date=~m/^\d{2}-\d{1,2}-\d{1,2}$/) { $document_date="20$document_date"; } $document_date=~m/^(\d{4})-(\d{1,2})-(\d{1,2})$/; $document_year = $1; $document_month = $2; $document_day = $3; } else { $document_date="$longyr-$fixmo-$mday"; $document_year = $longyr; $document_month = $fixmo; $document_day = $mday; } print "\n"; } header(); patient_details(); print "Document type: $document_type[$document] \n"; print "\n"; print "Description: $description\n" ; print "\n"; print "Document date: $document_day-$month_names[$document_month]-$document_year\n"; print "\n"; print "\n"; print "Are these the correct datails?\n"; print "\n"; print "1) Yes.\n"; print "\n"; print "2) No. I want to enter the document details again.\n"; print "\n"; print " Q) QUIT. I'm finished.\n"; print "\n"; $correct = ; $correct = lc $correct; $correct=~s/[^12q]//g; if ($correct eq 'q') { print "\n"; print "\n"; print "ARE YOU SURE YOU WANT TO QUIT?"; print "THIS DOCUMENT WILL NOT BE UPLOADED TO THE SERVER.\n"; print "\n"; print "Enter 'quit' to quit: "; $correct=; $correct=lc $correct; chomp $correct; if ($correct eq 'quit') { print "\n\nGoodbye.\n\n"; exit; } } if ($correct eq '2') { header(); #the user has chosen to enter new details, so reset $document if ($diagnostic) { print "DIAGNOSTIC: \$document_date='$document_date', \$description='$description' the user has chosen to re-enter the document details\n"; } $document_date = qw{}; $description = qw{}; $document = qw{}; } } #check to see if the user should manually input lab results if (($manual_labs) && ($lab)) { #reset the test results and lab scalar %test_result=q{}; $correct2 = '2'; --$lab; if ($diagnostic) { print "DIAGNOSTICS130: \$lab='$lab', \$correct='$correct'\n"; } while ($correct2 eq '2') { header(); patient_details(); print "Document type: $document_type[$document] \n"; print "\n"; print "Description: $description\n" ; print "\n"; print "Document date: $document_day-$month_names[$document_month]-$document_year\n"; print "\n"; print "\n"; print "\n"; #get the name of the hash for this group of tests (ie: biochem or hematology) $lab_hash_name = $test[$lab]; %this_lab_hash=%{$lab_hash_name}; if ($test[$lab] eq 'biochem') { #go through each test item for this group of tests for $this_test (sort keys %biochem) { #pad out the display $test_name = pad_for_tabulation($this_test, 20, ' ', 'L',''); print "$test_name : "; $this_result = ; $this_result=~s/[^0-9\.]//g; #store this result with the test CODE from the hash of tests $test_result{$biochem{$this_test}} = $this_result; } } #go through each test item for this group of tests if ($test[$lab] eq 'hematology') { #go through each test item for this group of tests for $this_test (sort keys %hematology) { #pad out the display $test_name = pad_for_tabulation($this_test, 20, ' ', 'L',''); print "$test_name : "; $this_result = ; $this_result=~s/[^0-9\.]//g; #store this result with the test CODE from the hash of tests $test_result{$hematology{$this_test}} = $this_result; } } if ($test[$lab] eq 'coagulation') { #go through each test item for this group of tests for $this_test (sort keys %coagulation) { #pad out the display $test_name = pad_for_tabulation($this_test, 20, ' ', 'L',''); print "$test_name : "; $this_result = ; $this_result=~s/[^0-9\.]//g; #store this result with the test CODE from the hash of tests $test_result{$coagulation{$this_test}} = $this_result; } } print "\n"; print "\n"; print "Are these entries correct?\n"; print "\n"; print "1) Yes.\n"; print "\n"; print "2) No. I want to enter the results again.\n"; print "\n"; print " Q) QUIT. I don't want to enter this document.\n"; print "\n"; $correct2 = 'A'; while ($correct2!~/^[12q]$/) { $correct2 = ; $correct2 = lc $correct2; $correct2=~s/[^12q]//g; if ($correct2 eq 'q') { print "\n"; print "\n"; print "ARE YOU SURE YOU WANT TO QUIT?"; print "THIS DOCUMENT WILL NOT BE UPLOADED TO THE SERVER.\n"; print "\n"; print "Enter 'quit' to quit: "; $correct2=; $correct2=lc $correct2; chomp $correct2; if ($correct2 eq 'quit') { print "\n\nGoodbye.\n\n"; exit; } } } if ($correct2 eq '1') { ####################### ######################### ####################### upload the results to the database ######################### ####################### ######################### if ($diagnostic) { print "DIAGNOSTICS140: \$correct='$correct', upload the results to the database\n"; } for $this_test_result (sort keys %test_result) { if ($test_result{$this_test_result}) { %mysql_set_insert=( 'type' => $this_test_result, 'demographicNo' => $demographic_no, 'providerNo' => $provider_no, 'dataField' => $test_result{$this_test_result}, 'measuringInstruction' => $measurement_instructions{$this_test_result}, 'dateObserved' => $document_date, 'dateEntered' => $sql_datetime ); mysql_set_insert('measurements'); } } $results_stored_message = "\n\nResults stored in the database.\n\n"; } } } #check to see if the user should manually input mammogram/pap result if ( ($manual_labs) && ($prevention) ) { header(); patient_details(); print "Document type: $document_type[$document] \n"; print "\n"; print "Description: $description\n" ; print "\n"; print "Document date: $document_day-$month_names[$document_month]-$document_year\n"; print "\n"; print "\n"; print "\n"; my $prevention_result = ' '; until ($prevention_result=~m/^a?b?normal$/) { print pad_for_tabulation("$preventions{$prevention} Result (normal or abnormal) ", 45, ' ', 'L',''); $prevention_result = ; print "\n"; chomp $prevention_result; $prevention_result = lc $prevention_result; $prevention_result=~s/[^a-z]//g; } my $prevention_retest = '-1'; until (($prevention_retest=~m/^\d{1,2}$/) && ($prevention_retest < 25)) { print pad_for_tabulation("Next $preventions{$prevention} recommended in (months): ", 45, ' ', 'L',''); $prevention_retest = ; chomp $prevention_retest; $prevention_retest=~s/\D//g; } ####################### ######################### ####################### upload the results to the database ######################### ####################### ######################### #work out the follow up date my $next_date = mysql_array_query("SELECT DATE_ADD('$document_date', INTERVAL $prevention_retest MONTH)"); #store the item first %mysql_set_insert=( 'demographic_no' => $demographic_no, 'provider_no' => $patient_provider, 'prevention_type' => $prevention, 'prevention_date' => $document_date, 'creation_date' => $sql_datetime, 'deleted' => '0', 'refused' => '0', 'next_date' => $next_date, 'never' => '0', 'creator' => $provider_no, ); mysql_set_insert('preventions'); #now work out the prevention_no for this prevention entry my $prevention_number = mysql_array_query(" SELECT id FROM preventions WHERE demographic_no='$demographic_no' && provider_no='$patient_provider' && prevention_type='$prevention' && prevention_date='$document_date' && creation_date='$sql_datetime' && creator='$provider_no' "); #now store the result %mysql_set_insert=( 'prevention_id' => $prevention_number, 'keyval' => 'result', 'val' => $prevention_result, ); mysql_set_insert('preventionsExt'); %mysql_set_insert=( 'prevention_id' => $prevention_number, 'keyval' => 'comments', ); mysql_set_insert('preventionsExt'); %mysql_set_insert=( 'prevention_id' => $prevention_number, 'keyval' => 'reason', ); mysql_set_insert('preventionsExt'); %mysql_set_insert=( 'prevention_id' => $prevention_number, 'keyval' => 'neverReason', ); mysql_set_insert('preventionsExt'); #if the result is abnormal, set up a tickler for the patient's GP if ($prevention_result eq 'abnormal') { %mysql_set_insert=( 'demographic_no' => $demographic_no, 'message' => "Abnormal $preventions{$prevention} result.", 'status' => 'A', 'update_date' => $sql_datetime, 'service_date' => $sql_datetime, 'creator' => $provider_no, 'priority' => 'High', 'task_assigned_to' => $patient_provider, ); mysql_set_insert('tickler'); } #print out that the results have been uploaded $results_stored_message = "\n\nResults stored in the database.\n\n"; } #scan the document $scanningtodo=1; #create a temporary directory to store the files in $tempdir = tempdir(); while ($scanningtodo ne '4') { $scanningtodo=1; header(); patient_details(); print "Document date: $document_day-$month_names[$document_month]-$document_year\n"; print "\n"; print "Type: $document_type[$document] \n"; print "\n"; print "Description: $description\n" ; print "\n"; if ($diagnostic) { print "DIAGNOSTICS150: \$scanningtodo='$scanningtodo', loop where \$scanningtodo ne '4'\n"; } if ($results_stored_message) { print "$results_stored_message\n\n"; $results_stored_message=qw{}; } if ($scanningtodo eq '9') { #delete the most recent scanned pages if ($diagnostic) { print "DIAGNOSTICS160: \$scanningtodo='$scanningtodo', delete most recent scanned pages because \$scanningtodo eq '9'\n"; } print "**LAST BATCH OF FILING DELETED**\n\n"; print " rescan batch\n\n"; } #ask if these pages are simplex or duplex $duplex=1; if ($duplexmodeon eq "1") { print "\n"; print "1) Simple / single / 1 sided scan\n"; print "\n"; print "2) Duplex / double / 2 sided scan\n"; print "\n"; $duplex=; $duplex=~s/\D//g; } #increment the batch number to prevent earlier files being overwritten ++$thisbatch; # format of filenames to store $destination="$tempdir/batch$thisbatch.out%04d.$imageformat"; $duplex1='duplex mode off'; $source=$adf_source; if ($duplex eq "2") { $source=$duplex_source; $duplex1='DUPLEX MODE ON'; } print "$duplex1\n"; print "\n"; print "Ready to scan - make sure documents are in the feeder and press ENTER\n"; print "\n"; ; print "\n"; print "\n"; # scan pages $device = find_scanner(); print "Scanning pages...\n\n"; $scanimage="scanimage $device --batch=$tempdir/batch$thisbatch.out%04d.$imageformat --format=$imageformat --mode $mode --resolution $resolution $source $sizeX $sizeY"; #print "$scanimage"; system($scanimage); #ask if the scan was ok, or if the user wants to redo this scan or if there are more pages for THIS document while ($scanningtodo!~m/^[349q]$/i) { header(); if ($diagnostic) { print "DIAGNOSTICS170: \$scanningtodo='$scanningtodo', loop where \$scanningtodo not matching '[349q]'\n"; } print "$first_name $last_name, $phn, $description\n"; print "\n"; print "Pages scanned in but NOT YET UPLOADED TO SERVER.\n"; print "\n"; print "\n"; print "\n"; print "3) ADD MORE scanned pages with the SAME DOCUMENT type and description.\n"; print "\n"; print "4) UPLOAD THIS DOCUMENT TO THE SERVER - the document is complete.\n"; print "\n"; print "9) DELETE the pages I just scanned and scan in the pages again.\n"; print "\n"; print "Q) QUIT - cancel scanning in documents and exit program.\n"; print "\n"; $scanningtodo=; $scanningtodo=lc $scanningtodo; $scanningtodo=~s/[^349q]//g; if ($scanningtodo eq 'q') { print "\n"; print "\n"; print "ARE YOU SURE YOU WANT TO QUIT?"; print "THIS DOCUMENTS WILL NOT BE UPLOADED TO THE SERVER.\n"; print "\n"; print "Enter 'quit' to quit: "; $scanningtodo=; $scanningtodo=lc $scanningtodo; chomp $scanningtodo; if ($scanningtodo eq 'quit') { print "\n\nGoodbye.\n\n"; exit; } } if ($scanningtodo eq '9') { print "\n"; print "\n"; print "ARE YOU SURE YOU WANT TO DELETE THE MOST RECENT BATCH OF SCANNED PAGES?"; print "\n"; print "Enter 'delete' to DELETE BATCH: "; $scanningtodo=; $scanningtodo=lc $scanningtodo; chomp $scanningtodo; if ($scanningtodo eq 'delete') { #delete the most recent scanned pages system("rm $tempdir/batch$thisbatch.*.$imageformat") || die ("Can't delete $tempdir/batch$thisbatch.*.$imageformat"); $scanningtodo = '9'; } $scanningtodo = qw{}; } } } #uplead the scanned pages # assemble into a single output file #convert the image files to pdf files opendir(TEMPDIR, $tempdir) or die $!; while ($image_file = readdir(TEMPDIR)) { #we only want files next unless (-f "$tempdir/$image_file"); #we only want the image files next unless ($image_file =~ m/\.$imageformat$/); #convert the file via ps to pdf $image_file2=$image_file; $image_file2=~s/\.$imageformat$//; system("convert $tempdir/$image_file2.$imageformat $tempdir/$image_file2.ps"); system("convert $tempdir/$image_file2.ps $tempdir/$image_file2.pdf"); } closedir(TEMPDIR); #generate a random number for the end of the file to avoid overwriting files $random=rand_id(); $outputfile="$last_name.$first_name.$description.$document_type[$document].$random.pdf"; $outputfile=~s/[^A-Z0-9\.\_\-]//gi; system("pdftk $tempdir/*.pdf cat output $tempdir/$outputfile"); #copy the resulting pdf over to the upload scanning folder on the server #make sure the file permissions are right first chmod(0666, "$tempdir/$outputfile") || die ("Can't change permissions on file $tempdir/$outputfile"); system("scp $tempdir/$outputfile $login_id\@$serveraddress:$uploadfolder/$outputfile"); # remove temporary directory and all of its contents system("rm -rf $tempdir"); #enter the document data into the MySQL database %mysql_set_insert=( doctype => $document_type[$document], docdesc => $description, docfilename => $outputfile, doccreator => $provider_no, program_id => '-1', updatedatetime => $sql_datetime, status => 'A', contenttype => 'application/pdf', public1 => '0', observationdate => $document_date ); mysql_set_insert('document'); #get the document_no to link to the patient record $document_number = mysql_array_query("SELECT document_no FROM document WHERE docfilename='$outputfile'"); #link the document to the patient record %mysql_set_insert=( module => 'demographic', module_id => $demographic_no, document_no => $document_number, status => 'A', ); mysql_set_insert('ctl_document'); #zero $newpatient while we work out what to do next $newpatient = 'What next?'; while ($newpatient!~m/^[12q]$/i) { if ($diagnostic) { print "DIAGNOSTICS180: \$scanningtodo='$scanningtodo', \$newpatient='$newpatient'; loop where \$scanningtodo not matching [12q]\n"; } print "\n"; print "Scan job completed.\n"; print "\n"; print "Document(s) uploaded to server and entered into patient's chart.\n"; print "\n"; print "1) Start a NEW SCAN JOB for a different patient\n"; print "\n"; print "2) Scan another document for $last_name, $first_name\n"; print "\n"; print "Q) Quit\n"; print "\n"; $newpatient=; $newpatient= lc $newpatient; $newpatient=~s/[^0-9q]//g; if ($diagnostic) { print "DIAGNOSTICS190: \$newpatient='$newpatient', exit if \$newpatient eq 'q'\n"; } if ($newpatient eq 'q') { print "\n\nGoodbye.\n\n"; exit; } } } } #disconnect from the database mysql_disconnect(); print "\n\nGoodbye.\n\n"; exit; ########################### #subroutines ########################### sub header { system ("clear"); print "-------------------------------\n"; print "OSCAR EMR SCANNING UTILITY v$version $login_id\n"; print "-------------------------------\n"; print "\n" ; } sub patient_details { print "Health#: $phn \n"; print "\n"; print "Name: $last_name, $first_name\n"; print "\n"; } sub find_scanner { #find out which scanner to use until ($device=~m/^--device-name/) { my @devices = `scanimage -L`; for my $this_device (@devices) { if ($this_device=~m/scan/i) { $this_device=~m/(\`.*\')/; $device = "--device-name $1"; $device=~s/\`/\'/g; } } unless ($device=~m/^--device-name/) { header(); print "No scanner detected.\n\n"; print "Please ensure the scanner is connected and turned on, then press enter\n\n"; $temp=; print "\n\nSearching for scanner....."; } } return $device; } sub mysql_log { if ($_[0]) { $sql_data=$_[0]; } if ($pref{mysql_log}) { unless ($sql_data) { $sql_data=$sql; } open (MYSQL,">>$pref{mysql_log}"); flock (MYSQL, 2); print MYSQL "$longyr-$fixmo-$mday $hr:$min:$sec - $sql_data\n"; close (MYSQL); } return; } sub mysql_connect { #set up login and password for this user unless ($mysql_user) { $mysql_user="read"; } $mysql_login="mysql_".$mysql_user; $mysql_pass="mysql_".$mysql_user."_pass"; $dbh_name="dbh_$mysql_user"; # Connect to the database mysql_log("mysql_connect : mysql_user=$mysql_user, dbh_name=$dbh_name, mysql_login=$mysql_login, mysql_pass=$mysql_pass"); ${$dbh_name} = DBI->connect("DBI:mysql:$pref{mysql_database}:$pref{mysql_host}:$pref{mysql_port}","$pref{$mysql_login}","$pref{$mysql_pass}") || ErrorMessage('Could not connect to database'); #record that this database is in use $dbh_inuse="dbh_inuse_$mysql_user"; ${$dbh_inuse}=1; return; } sub mysql_disconnect { # Disconnect the current user ($mysql_user) from the database # but don't disconnect the read-only user #get the passed user name if passed directly if ($_[0]) { $mysql_user=$_[0]; } if (($mysql_user) && ($mysql_user ne "read")) { #check that the user is connected first $dbh_inuse="dbh_inuse_$mysql_user"; if (${$dbh_inuse}) { #this user is connected $dbh_name="dbh_$mysql_user"; mysql_log("mysql_disconnect : mysql_user=$mysql_user, dbh_name=$dbh_name, mysql_login=$mysql_login, mysql_pass=$mysql_pass"); ${$dbh_name}->disconnect || mysql_log('ERROR could not disconnect database'); #record that this database is no longer in use ${$dbh_inuse}=0; } } return; } sub mysql_check_connection { #check that the preferred user is the one currently selected if ($mysql_user eq $mysql_preferred_user) { $mysql_original_user=$mysql_user; } else { $mysql_original_user=$mysql_user; $mysql_user=$mysql_preferred_user; } $dbh_name="dbh_$mysql_user"; $dbh_inuse="dbh_inuse_$mysql_user"; $mysql_disconnect=0; #check that the preferred user is connected unless (${$dbh_inuse}) { #this user is not connected $mysql_disconnect=1; mysql_connect(); } return; } sub mysql_check_connection_finish { #check if this user was only connected for this event if ($mysql_disconnect) { mysql_disconnect(); } #reset the current user to the original user $mysql_user=$mysql_original_user; return $mysql_user; } sub mysql_simple { #this subroutine is passed a full statement as $sql and sends it to the database as a 'read' user #get passed sql string if present if ($_[0]) { $sql=$_[0]; } $mysql_preferred_user="read"; mysql_submit_simple($sql); return 1; } sub mysql_write_simple { #this subroutine is passed a full statement as $sql and sends it to the database as a 'write' user #get passed sql string if present if ($_[0]) { $sql=$_[0]; } #print "\n\n**test mode** sub mysql_write_simple has had mysql_submit_simple($sql) commented out - your data has NOT been saved!\n\n$sql\n\n"; $mysql_preferred_user="write"; mysql_submit_simple($sql); return 1; } sub mysql_submit_simple { #this subroutine is passed a full statement as $sql and sends it to the database as the user $preferred_user #get passed sql string if present if ($_[0]) { $sql=$_[0]; } @mysql_row=(); mysql_check_connection(); mysql_log("mysql_submit_simple : $sql"); $mysql_query = ${$dbh_name}->do ($sql) || ErrorMessage('Could not submit data to the database'); mysql_check_connection_finish(); return 1; } sub mysql_array_query { #get passed sql string if present if ($_[0]) { $sql=$_[0]; } $mysql_preferred_user="read"; mysql_check_connection(); $mysql_query = ${$dbh_name}->prepare ($sql); @mysql_result=(); if (defined($mysql_query)) { mysql_log("mysql_array_query : $sql"); $mysql_query->execute() || ErrorMessage('Could not execute array query on database'); while (@mysql_row = $mysql_query->fetchrow_array()) { @mysql_result=@mysql_row; } } else { mysql_log("mysql_array_query (not found) : $sql"); ErrorMessage('Could not find query to submit to database'); } $mysql_query->finish(); mysql_check_connection_finish(); #pass the first result back as a return value (or undefined, if no result) if ($mysql_result[0]) { return $mysql_result[0]; } return; } sub mysql_hash_query { #get passed sql string if present if ($_[0]) { $sql=$_[0]; } $mysql_preferred_user="read"; mysql_check_connection(); $mysql_query = ${$dbh_name}->prepare ($sql); @mysql_result=(); if (defined($mysql_query)) { mysql_log("mysql_hash_query : $sql"); $mysql_query->execute() || ErrorMessage('Could not execute hash query on database'); @mysql_result=(); while ($mysql_record = $mysql_query->fetchrow_hashref()) { push (@mysql_result, $mysql_record); } } else { mysql_log("mysql_hash_query (not found) : $sql"); ErrorMessage('Could not find hash query to submit to database'); } $mysql_query->finish(); mysql_check_connection_finish(); #return the mysql_result array (containing hash references), or undefined if no result if ($mysql_result[0]) { return $mysql_result[0]; } return; } sub mysql_set_insert { #this subroutine is passed: # 1. the table name ($mysql_update_table) # 2. a hash of elements to include (%mysql_update) #and it then creates a mysql statement ($sql) and sends it to the database as a 'write' user #get the passed table name if passed directly if ($_[0]) { $mysql_insert_table=$_[0]; } my $mysql_set_something_to_insert = 0; $mysql_preferred_user="write"; mysql_check_connection(); #insert this line in the database $error="mysql_insert"; $sql="INSERT INTO $mysql_insert_table SET "; for $this_sql_insert (keys %mysql_set_insert) { if ($mysql_set_insert{$this_sql_insert}) { ++$mysql_set_something_to_insert; $mysql_set_insert{$this_sql_insert}=~s/\'/\`/g; $sql.="$this_sql_insert='$mysql_set_insert{$this_sql_insert}', "; } } $sql=~s/, $//; #check that at least one insert row was valid if ($mysql_set_something_to_insert > 0) { mysql_write_simple($sql); } mysql_check_connection_finish(); return; } sub pad_for_tabulation { #pads out the string #pass the following variables: #1. the string to be padded - including decimals, decimal places, and thousands separators #2. the desired length #3. the character to use for padding (usually ' ' or '0', defaults to '0') #4. right of left justify ('R' or 'L') #5. the number of decimal places to use, preceeded by T if thousands commas to be used (eg: '2', 'T2' or just'T' to just include thousands) #eg $bar = pad_for_tabulation($foo,'12',' ','R',''); ($string_to_be_padded, $desired_string_length, $padding_character, $right_or_left_justify, $decimal_places) = @_; unless ($padding_character) { $padding_character='0'; } #add decimal places if ($decimal_places) { $decimals=q{}; if ($decimal_places =~m/\d/) { $decimal_places_digit=$decimal_places; $decimal_places_digit=~s/^T//; ($string_to_be_padded, $decimals) = split (/\./, $string_to_be_padded); $decimals.="0"x$decimal_places_digit; $decimals=substr ($decimals, 0, $decimal_places_digit); $decimals=".$decimals"; } #add thousands separators if required if ($decimal_places =~m/T/) { $string_to_be_padded=~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g; } $string_to_be_padded.=$decimals; } #next check if the string is too long, and truncate if required if (length $string_to_be_padded > $desired_string_length) { $string_to_be_padded=substr ($string_to_be_padded, 0, $desired_string_length); } $missing_digits = $desired_string_length - (length $string_to_be_padded); $missing_digits="$padding_character"x$missing_digits; if ($right_or_left_justify eq 'L') { $string_to_be_padded=$string_to_be_padded.$missing_digits; } else { $string_to_be_padded=$missing_digits.$string_to_be_padded; } return $string_to_be_padded; } sub rand_id { my $rand_id_digits = $_[0]; unless ($rand_id_digits) { $rand_id_digits = 5; } my $rand_id=''; while (length $rand_id < $rand_id_digits) { my $ftemp=int(rand 9)+1; $rand_id.=$ftemp; } return $rand_id; } sub ErrorMessage { print "\n"; print "\n"; print "Error: $_\n"; exit; }