IVR Perl Program for collecting time and other information - MySQL interface

Greetings,

I appreciate the help that comes from finding code snippets and configuration examples, so I desire to provide a nifty program for your enjoyment. This is a time clock program for entering patient information as well as job and task information for subsequent billing. It certainly may be improved, but it has the redeeming social value in that it works.

Comments would be nice, especially if you find my approach useful. Use the ideas in good health.

jeff williams

===================================== ivr.pl ==================================

#!/usr/bin/perl -w
###############################################################################

ivr.pl

###############################################################################

Jeff Williams - Hospice of Saint John

Released under GPL 2 or later

##############################################################################

12/21/2009 - Initial cut at interactive time recording

02/21/2010 - Using synthesized voice from Cepstral

02/22/2010 - Revised accept / reject sequence

###############################################################################

somewhat from:

http://www.mail-archive.com/[email protected]/msg21137.html

and

VanMeggelen, J; Madsen, L; Smith, J. (2007).

Asterisk: The Future of Telephony.

Sebastopol, CA: O’Reilly Media, Inc.

###############################################################################

Tables in the BARCODE database

mysql> describe ivr_in;

±-----------±------------±-----±----±--------±------+

| Field | Type | Null | Key | Default | Extra |

±-----------±------------±-----±----±--------±------+

| pt_number | varchar(10) | YES | | NULL | |

| job_id | char(4) | YES | | NULL | |

| task_id | char(4) | YES | | NULL | |

| data_date | date | YES | | NULL | |

| data_time | time | YES | | NULL | |

| in_out | char(1) | YES | | NULL | |

| revis_date | date | YES | | NULL | |

±-----------±------------±-----±----±--------±------+

7 rows in set (0.01 sec)

mysql> describe job_name;

±-------±------------±-----±----±--------±------+

| Field | Type | Null | Key | Default | Extra |

±-------±------------±-----±----±--------±------+

| title | varchar(50) | YES | | NULL | |

| job_id | char(4) | YES | | NULL | |

±-------±------------±-----±----±--------±------+

2 rows in set (0.00 sec)

mysql> describe task_name;

±---------±------------±-----±----±--------±------+

| Field | Type | Null | Key | Default | Extra |

±---------±------------±-----±----±--------±------+

| task_id | int(4) | YES | | NULL | |

| emp_type | varchar(50) | YES | | NULL | |

| task | varchar(50) | YES | | NULL | |

| avg_time | float(8,3) | YES | | NULL | |

±---------±------------±-----±----±--------±------+

4 rows in set (0.00 sec)

Tables in the PATIENT_NUMBER database

mysql> describe patient;

±-------------±------------±-----±----±--------±------+

| Field | Type | Null | Key | Default | Extra |

±-------------±------------±-----±----±--------±------+

| patient_id | int(6) | NO | PRI | NULL | |

| f_name | varchar(50) | YES | | NULL | |

| m_name | varchar(50) | YES | | NULL | |

| l_name | varchar(50) | YES | | NULL | |

| status | char(1) | YES | | NULL | |

| location | char(1) | NO | | NULL | |

| employee | int(6) | YES | | NULL | |

| asgn_date | date | YES | | NULL | |

| revis_date | date | YES | | NULL | |

| misys_number | int(6) | YES | | NULL | |

| medicare | char(3) | YES | | NULL | |

±-------------±------------±-----±----±--------±------+

11 rows in set (0.00 sec)

#############################################################################

be anal retentive concerning Perl errors

use strict;

use unbuffered output – this is the key to making the program work!

$|=1;

##############################################################################

my %AGI_ENV;
my $AGI;
my $job1;
my $pt1;
my $task1;
my $in1;
my $conf1;
my $ret_code;
my $confirm = ‘N’;
my ($dbh,$dbh_pt,$dbs,$job_response,$task_response,$pt_response,$fn,$ln);
my ($in_response,$dbs_pt);

obtain parameters passed by Asterisk to the AGI script

while() {
chomp;
last unless length($);
if (/^agi
(\w+):\s+(.*)$/) {
$AGI_ENV{%1} = $2;
}
}

##############################################################################

obtain configuration data from external file

open(CONF_IN,"</etc/bar_config.txt");
my $line_in;
my @conf_in;
my %conf;
my $password;
my $userid;
my $host;
my $dbf;
my $pt_dbf;
my %voice;

while ($line_in = <CONF_IN>) {
chomp($line_in);
@conf_in = split(’;’,"$line_in");
$conf{"$conf_in[0]"} = “$conf_in[1]”;
}

$password = $conf{password};
$userid = $conf{userid};
$host = $conf{host};
$dbf = $conf{dbf};
$pt_dbf = $conf{pt_dbf};

$voice{welcome} = $conf{welcome};
$voice{job} = $conf{job};
$voice{jobbad} = $conf{jobbad};
$voice{joblist} = $conf{joblist};
$voice{pt} = $conf{pt};
$voice{ptbad} = $conf{ptbad};
$voice{task} = $conf{task};
$voice{taskbad} = $conf{taskbad};
$voice{tasklist} = $conf{tasklist};
$voice{in} = $conf{in};
$voice{accept} = $conf{accept};
$voice{thankyou} = $conf{thankyou};

##############################################################################

open(HC_LOG,">>/tmp/jobout.txt");

print HC_LOG “AGI Environment Dump:\n”;

foreach my $i (sort keys %AGI_ENV) {

print HC_LOG “-- $i = $AGI_ENV{$i}\n”;

}

use Asterisk::AGI;
use DBI;
$dbh = DBI->connect(“DBI:mysql:$dbf:$host”,"$userid","$password") ||
die “cannot open $dbf”;
$dbh_pt = DBI->connect(“DBI:mysql:$pt_dbf:$host”,"$userid","$password") ||
die “cannot open $pt_dbf”;

##############################################################################

my @time_date = localtime();
my $year = $time_date[5]+1900;
my $month = $time_date[4]+1;
my $day = $time_date[3];
my $today = “$year-$month-$day”;
my $time_now = “$time_date[2]:$time_date[1]”;

################################################################################

$AGI = new Asterisk::AGI;

################################################################################

Call the processing subroutines in order.

while ($confirm =~ /N/i) {
ivr_answer();
ivr_getjob();

ivr_sayjob();

    ivr_getpt();

ivr_saypt();

    ivr_gettask();

ivr_saytask();

    ivr_getinout();

ivr_sayinout();

    ivr_prtlog();

ivr_sqlin();

    ivr_getconfirm();
    ivr_thanks();

}

################################################################################

sub ivr_answer {
$AGI->answer();
$AGI->exec(‘swift’,"$voice{welcome}");
}

################################################################################

sub ivr_daytime {
$AGI->say_datetime($time_now);
}

################################################################################

sub ivr_getjob {
$AGI->exec(‘swift’,"$voice{job}");

grab four digits from the phone and translate them from ASCII decimal

representation back to integer equivalents

    my @job;
    my $goodjob = 'N';
    while ($goodjob =~ /N/i) {
            my $rep = 0;
            while ($rep < 4) {
                    my $job_in = $AGI->wait_for_digit(-1);
                    if ($job_in == 35) {
                            last;
                    }
                    $job[$rep] = $job_in;
                    $rep++;
            }
            if ($rep <= 1) {        # should happen if # is pressed
                    $AGI->exec('swift',"$voice{joblist}");
                    $AGI->exec('swift',"$voice{job}");
            } else {
                    $job1 = ivr_dtmf2int(@job);
                    $goodjob = ivr_checkjob();
                    if ($goodjob =~ /N/i) {
                            $AGI->exec('swift',"$voice{jobbad}");
                    }
            }
    }

}

################################################################################

sub ivr_getpt {
$AGI->exec(‘swift’,"$voice{pt}");

grab five digits from the phone and translate them from ASCII decimal

representation back to integer equivalents

    my @pt;
    my $goodpt = 'N';
    while ($goodpt =~ /N/i) {
            my $rep = 0;
            while ($rep < 5) {
                    my $pt_in = $AGI->wait_for_digit(-1);
                    $pt[$rep] = $pt_in;
                    $rep++;
            }
            $pt1 = ivr_dtmf2int(@pt);
            $goodpt = ivr_checkpt();
            if ($goodpt =~ /N/i) {
                    $AGI->exec('swift',"voice{ptbad}");
            }
    }

}

################################################################################

sub ivr_gettask {
$AGI->exec(‘swift’,"$voice{task}");

grab four digits from the phone and translate them from ASCII decimal

representation back to integer equivalents

    my @task;
    my $goodtask = 'N';
    while ($goodtask =~ /N/i) {
            my $rep = 0;
            while ($rep < 4) {
                    my $task_in = $AGI->wait_for_digit(-1);
                    if ($task_in == 35) {
                            last;
                    }
                    $task[$rep] = $task_in;
                    $rep++;
            }
            if ($rep <= 1) {        # should happen if # is pressed
                    $AGI->exec('swift',"$voice{tasklist}");
                    $AGI->exec('swift',"$voice{task}");
            } else {
                    $task1 = ivr_dtmf2int(@task);
                    $goodtask = ivr_checktask();
                    if ($goodtask =~ /N/i) {
                            $AGI->exec('swift',"$voice{taskbad}");
                    }
            }
    }

}

################################################################################

sub ivr_getconfirm {
my $goodconf = ‘N’;
my @conf;
while ($goodconf =~ /N/i) {
my $rep = 0;
ivr_confirm();

grab 1 digits from the phone and translate it from ASCII decimal

representation back to integer equivalents

            my $in_conf = $AGI->wait_for_digit(-1);
            $conf[$rep] = $in_conf;
            $conf1 = ivr_dtmf2int(@conf);
            $goodconf = ivr_checkin();
            if ($goodconf =~ /N/i) {
                    $AGI->exec('swift',"$voice{badconf}");
            }
            $confirm = $goodconf;
    }

}

################################################################################

sub ivr_getinout {
my $goodin = ‘N’;
my @in;
while ($goodin =~ /N/i) {
my $rep = 0;
$AGI->exec(‘swift’,"$voice{in}");

grab 1 digits from the phone and translate it from ASCII decimal

representation back to integer equivalents

            my $in_in = $AGI->wait_for_digit(-1);
            $in[$rep] = $in_in;
            $in1 = ivr_dtmf2int(@in);
            $goodin = ivr_checkin();
            if ($goodin =~ /N/i) {
                    $AGI->exec('swift',"$voice{badin}");
            }
    }

}

################################################################################

sub ivr_sayjob {
$AGI->say_digits($job1);
}

################################################################################

sub ivr_saypt {
$AGI->say_digits($pt1);
}

################################################################################

sub ivr_saytask {
$AGI->say_digits($task1);
}

################################################################################

sub ivr_prtlog {
my $prt_line = “$time_now: insert into ivr_in set job_id=’$job1’,”;
$prt_line = “$prt_line pt_number=’$pt1’,in_out=’$in1’,”;
$prt_line = “$prt_line data_date=’$today’,data_time=’$time_now’\n”;
print HC_LOG “$prt_line\n”;
}

################################################################################

sub ivr_sayinout {
$AGI->say_digits($in1);
}

################################################################################

sub ivr_thanks {
$AGI->exec(‘swift’,"$voice{thankyou}");
}

################################################################################

sub ivr_dtmf2int {
# this reads the values of the DTMF input string
# and converts from decimal ascii value to digit
my @dtmf_in = @_;
my $outstring = ‘’;
foreach my $dtmf (@dtmf_in) {
if ($dtmf == 48) {
$outstring = “$outstring” . ‘0’;
} elsif ($dtmf == 49) {
$outstring = “$outstring” . ‘1’;
} elsif ($dtmf == 50) {
$outstring = “$outstring” . ‘2’;
} elsif ($dtmf == 51) {
$outstring = “$outstring” . ‘3’;
} elsif ($dtmf == 52) {
$outstring = “$outstring” . ‘4’;
} elsif ($dtmf == 53) {
$outstring = “$outstring” . ‘5’;
} elsif ($dtmf == 54) {
$outstring = “$outstring” . ‘6’;
} elsif ($dtmf == 55) {
$outstring = “$outstring” . ‘7’;
} elsif ($dtmf == 56) {
$outstring = “$outstring” . ‘8’;
} elsif ($dtmf == 57) {
$outstring = “$outstring” . ‘9’;
}
}
return($outstring);
}

################################################################################

sub ivr_sqlin {
my $dbh;
my $dbs;

    $dbs = $dbh->prepare("insert into ivr_in set
            pt_number  = '$pt1',
            job_id     = '$job1',
            task_id    = '$task1',
            data_date  = '$today',
            data_time  = '$time_now',
            in_out     = '$in1',
            revis_date = '$today' ");

$dbs->execute();

}

################################################################################

sub ivr_checkjob {
$dbs = $dbh->prepare(“select title from job_name where
job_id = ‘$job1’”);
$dbs->execute();
if ($dbs->rows == 0) {
$job_response = “Invalid job number”;
$ret_code = ‘N’;
} else {
($job_response) = $dbs->fetchrow_array;
$ret_code = ‘Y’;
}
return($ret_code);
}

################################################################################

sub ivr_checktask {
$dbs = $dbh->prepare(“select task from task_name where
task_id = ‘$task1’”);
$dbs->execute();
if ($dbs->rows == 0) {
$task_response = “Invalid task number”;
$ret_code = ‘N’;
} else {
($task_response) = $dbs->fetchrow_array;
$ret_code = ‘Y’;
}
return($ret_code);
}

################################################################################

sub ivr_checkpt {
print HC_LOG “$today $time_now: patient = $pt1\n”;
$dbs_pt = $dbh_pt->prepare(“select f_name,l_name from patient where
patient_id = $pt1”);
$dbs_pt->execute();
my $rows = $dbs_pt->rows;
if ($rows == 0) {
$pt_response = “Invalid patient number”;
$ret_code = ‘N’;
} else {
($fn,$ln) = $dbs_pt->fetchrow_array;
$pt_response = “$fn $ln”;
$ret_code = ‘Y’;
}
return($ret_code);
}

################################################################################

sub ivr_checkin {
if ($in1 == 1) {
$in_response = ‘Clock In’;
$ret_code = ‘Y’;
} elsif ($in1 == 2) {
$in_response = ‘Clock Out’;
$ret_code = ‘Y’;
} else {
$ret_code = ‘N’;
}
}

################################################################################

sub ivr_confirm {
$AGI->exec(‘swift’,“Job: $job_response”);
$AGI->exec(‘swift’,“Task: $task_response”);
$AGI->exec(‘swift’,“Patient: $pt_response.”);
$AGI->exec(‘swift’,"$in_response");
$AGI->exec(‘swift’,"$voice{accept}");
}

================================= configuration file ============================

password;**********
userid;**********
host;SERVERXX
dbf;barcode
pt_dbf;patient_number
welcome;Hi. This is the home care time system.
job;Please enter your four digit job code
jobbad;Invalid job number. Please re-enter or press pound for the list of jobs.
joblist;R N: 9 0 0 1, L P N: 9 0 0 2, C N A: 9 0 0 3, Social Worker: 9 0 0 4, Chaplain: 9 0 0 5, Complementary Therapy: 9 0 0 6, Volunteer: 9 0 0 7
pt;Please enter your five digit patient number.
ptbad;Patient not found. Please re-enter.
task;Please enter your task number.
taskbad;Invalid task number. Please re-enter or press pound for the list of tasks.
tasklist;Routine Home Care: 7 0 2 0, Routine Nursing Home Care: 7 0 2 1, G I Nursing Home Care: 7 0 2 2, Respite Nursing Home Care: 7 0 2 3, G I Hospital Care: 7 0 2 4, Continuous Home Care: 7 0 2 5
in;Please enter one to clock inn, or two to clock out.
inbad;Invalid clocking entry.
accept;Please press the pound sign to re-enter or one to continue
thankyou;Thank you