3 * Patient reminders functions.
5 * These functions should not ever attempt to write to
6 * session variables, because the session_write_close() function
7 * is typically called before utilizing these functions.
9 * Functions for collection/displaying/sending patient reminders. This is
10 * part of the CDR engine, which can be found at library/clinical_rules.php.
12 * Copyright (C) 2010-2012 Brady Miller <brady.g.miller@gmail.com>
14 * LICENSE: This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
26 * @author Brady Miller <brady.g.miller@gmail.com>
27 * @link http://www.open-emr.org
31 * Include the main CDR engine library, email class and maviq class
33 require_once(dirname(__FILE__
) . "/clinical_rules.php");
34 require_once(dirname(__FILE__
) . "/maviq_phone_api.php");
37 * Display the patient reminder widget.
39 * @param integer $patient_id pid of selected patient
40 * @param string $dateTarget target date (format Y-m-d H:i:s). If blank then will test with current date as target.
42 function patient_reminder_widget($patient_id,$dateTarget='') {
44 // Set date to current if not set
45 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
47 // Update reminders for patient
48 update_reminders($dateTarget, $patient_id);
50 // Fetch the active reminders
51 $listReminders = fetch_reminders($patient_id);
53 if (empty($listReminders)) {
54 // No reminders to show.
55 echo htmlspecialchars( xl('No active patient reminders.'), ENT_NOQUOTES
);
59 echo "<table cellpadding='0' cellspacing='0'>";
60 foreach ($listReminders as $reminder) {
61 echo "<tr><td style='padding:0 1em 0 1em;'><span class='small'>";
62 // show reminder label
63 echo generate_display_field(array('data_type'=>'1','list_id'=>'rule_action_category'),$reminder['category']) .
64 ": " . generate_display_field(array('data_type'=>'1','list_id'=>'rule_action'),$reminder['item']);
65 echo "</span></td><td style='padding:0 1em 0 1em;'><span class='small'>";
66 // show reminder due status
67 echo generate_display_field(array('data_type'=>'1','list_id'=>'rule_reminder_due_opt'),$reminder['due_status']);
68 echo "</span></td><td style='padding:0 1em 0 1em;'><span class='small'>";
69 // show reminder sent date
70 if (empty($reminder['date_sent'])) {
71 echo htmlspecialchars( xl('Reminder Not Sent Yet'), ENT_NOQUOTES
);
74 echo htmlspecialchars( xl('Reminder Sent On').": ".$reminder['date_sent'], ENT_NOQUOTES
);
76 echo "</span></td></tr>";
82 * Function to update reminders via a batching method to improve performance and decrease memory overhead.
84 * Function that updates reminders and returns an array with a specific data structure.
85 * <pre>The data structure of the return array includes the following elements
86 * 'total_active_actions' - Number of active actions.
87 * 'total_pre_active_reminders' - Number of active reminders before processing.
88 * 'total_pre_unsent_reminders' - Number of unsent reminders before processing.
89 * 'total_post_active_reminders' - Number of active reminders after processing.
90 * 'total_post_unsent_reminders' - Number of unsent reminders after processing.
91 * 'number_new_reminders' - Number of new reminders
92 * 'number_updated_reminders' - Number of updated reminders (due_status change)
93 * 'number_inactivated_reminders' - Number of inactivated reminders.
94 * 'number_unchanged_reminders' - Number of unchanged reminders.
97 * @param string $dateTarget target date (format Y-m-d H:i:s). If blank then will test with current date as target.
98 * @param integer $batchSize number of patients to batch (default is 25; plan to optimize this default setting in the future)
99 * @param integer $report_id id of report in database (if already bookmarked)
100 * @param boolean $also_send if TRUE, then will also call send_reminder when done
101 * @return array see above for data structure of returned array
103 function update_reminders_batch_method($dateTarget='', $batchSize=25, $report_id=NULL, $also_send=FALSE) {
105 // Default to a batchsize, if empty
106 if (empty($batchSize)) {
110 // Collect total number of pertinent patients (to calculate batching parameters)
111 $totalNumPatients = buildPatientArray('','','',NULL,NULL,TRUE);
113 // Cycle through the batches and collect/combine results
114 if (($totalNumPatients%
$batchSize) > 0) {
115 $totalNumberBatches = floor($totalNumPatients/$batchSize) +
1;
118 $totalNumberBatches = floor($totalNumPatients/$batchSize);
121 // Prepare the database to track/store results
123 $report_id = beginReportDatabase("process_send_reminders",'',$report_id);
126 $report_id = beginReportDatabase("process_reminders",'',$report_id);
128 setTotalItemsReportDatabase($report_id,$totalNumPatients);
131 for ($i=0;$i<$totalNumberBatches;$i++
) {
132 $patient_counter = $batchSize*($i+
1);
133 if ($patient_counter > $totalNumPatients) $patient_counter = $totalNumPatients;
134 $update_rem_log_batch = update_reminders($dateTarget,'',(($batchSize*$i)+
1),$batchSize);
136 // For first cycle, simply copy it to update_rem_log
137 $update_rem_log = $update_rem_log_batch;
141 //error_log("CDR: ".print_r($update_rem_log,TRUE),0);
142 //error_log("CDR: ".($batchSize*$i). " records",0);
144 // Integrate batch results into main update_rem_log
145 $update_rem_log['total_active_actions'] = $update_rem_log['total_active_actions'] +
$update_rem_log_batch['total_active_actions'];
146 $update_rem_log['total_pre_active_reminders'] = $update_rem_log['total_pre_active_reminders'] +
$update_rem_log_batch['total_pre_active_reminders'];
147 $update_rem_log['total_pre_unsent_reminders'] = $update_rem_log['total_pre_unsent_reminders'] +
$update_rem_log_batch['total_pre_unsent_reminders'];
148 $update_rem_log['number_new_reminders'] = $update_rem_log['number_new_reminders'] +
$update_rem_log_batch['number_new_reminders'];
149 $update_rem_log['number_updated_reminders'] = $update_rem_log['number_updated_reminders'] +
$update_rem_log_batch['number_updated_reminders'];
150 $update_rem_log['number_unchanged_reminders'] = $update_rem_log['number_unchanged_reminders'] +
$update_rem_log_batch['number_unchanged_reminders'];
151 $update_rem_log['number_inactivated_reminders'] = $update_rem_log['number_inactivated_reminders'] +
$update_rem_log_batch['number_inactivated_reminders'];
152 $update_rem_log['total_post_active_reminders'] = $update_rem_log['total_post_active_reminders'] +
$update_rem_log_batch['total_post_active_reminders'];
153 $update_rem_log['total_post_unsent_reminders'] = $update_rem_log['total_post_unsent_reminders'] +
$update_rem_log_batch['total_post_unsent_reminders'];
155 //Update database to track results
156 updateReportDatabase($report_id,$patient_counter);
159 // Create an array for saving to database (allows combining with the send log)
161 $save_log[] = $update_rem_log;
163 // Send reminders, if this was selected
165 $log_send = send_reminders();
166 $save_log[] = $log_send;
169 // Record combo results in database
170 finishReportDatabase($report_id,json_encode($save_log));
172 // Just return the process reminders array
173 return $update_rem_log;
177 * Function to update reminders.
179 * Function that updates reminders and returns an array with a specific data structure.
180 * <pre>The data structure of the return array includes the following elements
181 * 'total_active_actions' - Number of active actions.
182 * 'total_pre_active_reminders' - Number of active reminders before processing.
183 * 'total_pre_unsent_reminders' - Number of unsent reminders before processing.
184 * 'total_post_active_reminders' - Number of active reminders after processing.
185 * 'total_post_unsent_reminders' - Number of unsent reminders after processing.
186 * 'number_new_reminders' - Number of new reminders
187 * 'number_updated_reminders' - Number of updated reminders (due_status change)
188 * 'number_inactivated_reminders' - Number of inactivated reminders.
189 * 'number_unchanged_reminders' - Number of unchanged reminders.
192 * @param string $dateTarget target date (format Y-m-d H:i:s). If blank then will test with current date as target.
193 * @param integer $patient_id pid of patient. If blank then will check all patients.
194 * @param integer $start applicable patient to start at (when batching process)
195 * @param integer $batchSize number of patients to batch (when batching process)
196 * @return array see above for data structure of returned array
198 function update_reminders($dateTarget='', $patient_id='', $start=NULL, $batchSize=NULL) {
202 // Set date to current if not set
203 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
205 // Collect reminders (note that this function removes redundant and keeps the most distant
206 // reminder (ie. prefers 'past_due' over 'due' over 'soon_due')
207 // Note that due to a limitation in the test_rules_clinic function, the patient_id is explicitly
208 // needed to work correctly. So rather than pass in a '' patient_id to do the entire clinic,
209 // we instead need to pass in each patient_id separately.
210 $collectedReminders = array();
211 $patient_id_complete = "";
212 if (!(empty($patient_id))) {
213 // only one patient id, so run the function
214 $collectedReminders = test_rules_clinic('','patient_reminder',$dateTarget,'reminders-due',$patient_id);
215 $patient_id_complete = $patient_id;
218 // as described above, need to pass in each patient_id
219 // Collect all patient ids
220 $patientData = buildPatientArray('','','',$start,$batchSize);
221 for($iter=0; $row=sqlFetchArray($rez); $iter++
) {
222 $patientData[$iter]=$row;
225 foreach ($patientData as $patient) {
227 $tempCollectReminders = test_rules_clinic('','patient_reminder',$dateTarget,'reminders-due',$patient['pid']);
228 $collectedReminders = array_merge($collectedReminders,$tempCollectReminders);
229 // build the $patient_id_complete variable
231 $patient_id_complete .= $patient['pid'];
235 $patient_id_complete .= ",".$patient['pid'];
239 $logging['total_active_actions'] = count($collectedReminders);
241 // For logging purposes only:
242 // Collect number active of active and unsent reminders
243 $logging['total_pre_active_reminders'] = count(fetch_reminders($patient_id_complete));
244 $logging['total_pre_unsent_reminders'] = count(fetch_reminders($patient_id_complete, 'unsent'));
246 // Migrate reminders into the patient_reminders table
247 $logging['number_new_reminders'] = 0;
248 $logging['number_updated_reminders'] = 0;
249 $logging['number_unchanged_reminders'] = 0;
250 foreach ($collectedReminders as $reminder) {
252 // See if a reminder already exist
253 $sql = "SELECT `id`, `pid`, `due_status`, `category`, `item` FROM `patient_reminders` WHERE " .
254 "`active`='1' AND `pid`=? AND `category`=? AND `item`=?";
255 $result = sqlQueryCdrEngine($sql, array($reminder['pid'], $reminder['category'], $reminder['item']) );
257 if (empty($result)) {
258 // It does not yet exist, so add a new reminder
259 $sql = "INSERT INTO `patient_reminders` (`pid`, `due_status`, `category`, `item`, `date_created`) " .
260 "VALUES (?, ?, ?, ?, NOW())";
261 sqlStatementCdrEngine($sql, array($reminder['pid'], $reminder['due_status'], $reminder['category'], $reminder['item']) );
262 $logging['number_new_reminders']++
;
265 // It already exist (see if if needs to be updated via adding a new reminder)
266 if ($reminder['due_status'] == $result['due_status']) {
267 // No change in due status, so no need to update
268 $logging['number_unchanged_reminders']++
;
272 // Change in due status, so inactivate current reminder and create a new one
273 // First, inactivate the previous reminder
274 $sql = "UPDATE `patient_reminders` SET `active` = '0', `reason_inactivated` = 'due_status_update', " .
275 "`date_inactivated` = NOW() WHERE `id`=?";
276 sqlStatementCdrEngine($sql, array($result['id']) );
277 // Then, add the new reminder
278 $sql = "INSERT INTO `patient_reminders` (`pid`, `due_status`, `category`, `item`, `date_created`) " .
279 "VALUES (?, ?, ?, ?, NOW())";
280 sqlStatementCdrEngine($sql, array($reminder['pid'], $reminder['due_status'], $reminder['category'], $reminder['item']) );
285 // Inactivate reminders that no longer exist
286 // Go through each active reminder and ensure it is in the current list
287 $sqlReminders = fetch_reminders($patient_id_complete);
288 $logging['number_inactivated_reminders'] = 0;
289 foreach ( $sqlReminders as $row ) {
290 $inactivateFlag = true;
291 foreach ($collectedReminders as $reminder) {
292 if ( ($row['pid'] == $reminder['pid']) &&
293 ($row['category'] == $reminder['category']) &&
294 ($row['item'] == $reminder['item']) &&
295 ($row['due_status'] == $reminder['due_status']) ) {
296 // The sql reminder has been confirmed, so do not inactivate it
297 $inactivateFlag = false;
301 if ($inactivateFlag) {
302 // The sql reminder was not confirmed, so inactivate it
303 $sql = "UPDATE `patient_reminders` SET `active` = '0', `reason_inactivated` = 'auto', " .
304 "`date_inactivated` = NOW() WHERE `id`=?";
305 sqlStatementCdrEngine($sql, array($row['id']) );
306 $logging['number_inactivated_reminders']++
;
310 // For logging purposes only:
311 // Collect number of active and unsent reminders
312 $logging['total_post_active_reminders'] = count(fetch_reminders($patient_id_complete));
313 $logging['total_post_unsent_reminders'] = count(fetch_reminders($patient_id_complete, 'unsent'));
320 * Function to send reminders.
322 * Function that sends reminders and returns an array with a specific data structure.
323 * <pre>The data structure of the return array includes the following elements
324 * 'total_pre_unsent_reminders' - Number of reminders before processing.
325 * 'total_post_unsent_reminders' - Number of reminders after processing.
326 * 'number_success_emails' - Number of successfully sent email reminders.
327 * 'number_failed_emails' - Number of failed sent email reminders.
328 * 'number_success_calls' - Number of successfully call reminders.
329 * 'number_failed_calls' - Number of failed call reminders.
332 * @return array see above for data structure of returned array
334 function send_reminders() {
338 // Collect active reminders that have not yet been sent.
339 $active_unsent_reminders = fetch_reminders('', 'unsent');
340 $logging['total_pre_unsent_reminders'] = count($active_unsent_reminders);
342 // Send the unsent reminders
343 $logging['number_success_emails'] = 0;
344 $logging['number_failed_emails'] = 0;
345 $logging['number_success_calls'] = 0;
346 $logging['number_failed_calls'] = 0;
347 foreach ( $active_unsent_reminders as $reminder ) {
349 // Collect patient information that reminder is going to.
350 $sql = "SELECT `fname`, `lname`, `email`, `phone_home`, `hipaa_voice`, `hipaa_allowemail` from `patient_data` where `pid`=?";
351 $result = sqlQueryCdrEngine($sql, array($reminder['pid']) );
352 $patientfname = $result['fname'];
353 $patientlname = $result['lname'];
354 $patientemail = $result['email'];
355 $patientphone = $result['phone_home'];
356 $hipaa_voice = $result['hipaa_voice'];
357 $hipaa_allowemail = $result['hipaa_allowemail'];
359 // Email to patient if Allow Email and set reminder sent flag.
360 if ($hipaa_allowemail == "YES") {
361 $mail = new MyMailer();
362 $sender_name = $GLOBALS['patient_reminder_sender_name'];
363 $email_address = $GLOBALS['patient_reminder_sender_email'];
364 $mail->FromName
= $sender_name; // required
365 $mail->Sender
= $email_address; // required
366 $mail->From
= $email_address; // required
367 $mail->AddAddress($patientemail, $patientfname.", ".$patientlname); // required
368 $mail->AddReplyTo($email_address,$sender_name); // required
369 $category_title = generate_display_field(array('data_type'=>'1','list_id'=>'rule_action_category'),$reminder['category']);
370 $item_title = generate_display_field(array('data_type'=>'1','list_id'=>'rule_action'),$reminder['item']);
371 $mail->Body
= "Dear ".$patientfname.", This is a message from your clinic to remind you of your ".$category_title.": ".$item_title;
372 $mail->Subject
= "Clinic Reminder";
374 // deal with and keep track of this successful email
375 sqlStatementCdrEngine("UPDATE `patient_reminders` SET `email_status`='1', `date_sent`=NOW() WHERE id=?", array($reminder['id']) );
376 $logging['number_success_emails']++
;
379 // deal with and keep track of this unsuccesful email
380 $logging['number_failed_emails']++
;
384 // Call to patient if Allow Voice Message and set reminder sent flag.
385 if ($hipaa_voice == "YES") {
387 /******************************************************************************
388 * // Maviq does not work, is not currently supported, and seems to break on windows servers, so this
389 * // feature has been commented out for now.
390 * // Automated VOIP service provided by Maviq. Please visit http://signup.maviq.com for more information.
391 * $siteId = $GLOBALS['phone_gateway_username'];
392 * $token = $GLOBALS['phone_gateway_password'];
393 * $endpoint = $GLOBALS['phone_gateway_url'];
394 * $client = new MaviqClient($siteId, $token, $endpoint);
397 * "firstName" => $patientfname,
398 * "lastName" => $patientlname,
399 * "phone" => $patientphone,
400 * //"apptDate" => "$scheduled_date[1]/$scheduled_date[2]/$scheduled_date[0]",
401 * "timeRange" => "10-18",
402 * "type" => "reminder",
403 * "timeZone" => date('P'),
404 * "greeting" => str_replace("[[sender]]", $sender_name, str_replace("[[patient_name]]", $patientfname, $myrow['reminder_content']))
408 * $response = $client->sendRequest("appointment", "POST", $data);
410 * if ($response->IsError) {
411 * // deal with and keep track of this unsuccessful call
412 * $logging['number_failed_calls']++;
415 * // deal with and keep track of this succesful call
416 * sqlStatementCdrEngine("UPDATE `patient_reminders` SET `voice_status`='1', `date_sent`=NOW() WHERE id=?", array($reminder['id']) );
417 * $logging['number_success_calls']++;
419 *******************************************************************************/
424 // For logging purposes only:
425 // Collect active reminders that have not yet been sent.
426 $logging['total_post_unsent_reminders'] = count(fetch_reminders('', 'unsent'));
432 * Function to fetch reminders.
434 * @param integer/array $patient_id pid(s) of patient(s).
435 * @param string $type Can choose unsent ('unsent') vs all active (BLANK) reminders
436 * @param string $due_status due status of reminders (soon_due,due,past_due). If blank, then will return all.
437 * @param string $select Select component of select statement. If blank, then will return all columns.
438 * @return array Returns an array of reminders.
440 function fetch_reminders($patient_id='',$type='',$due_status='',$select='*') {
442 $arraySqlBind = array();
444 if (!empty($patient_id)) {
445 // check the specified pid(s)
446 $where = "`pid` IN (?) AND ";
447 array_push($arraySqlBind,$patient_id);
450 if (!empty($due_status)) {
451 $where .= "`due_status`=? AND ";
452 array_push($arraySqlBind,$due_status);
456 $where .= "`active`='1'";
458 else { // $type == 'unsent'
459 $where .= "`active`='1' AND `date_sent` IS NULL";
462 $order = "`due_status`, `date_created`";
464 $sql = "SELECT " . $select . " FROM `patient_reminders` WHERE " .
465 $where . " ORDER BY " . $order;
466 $rez = sqlStatementCdrEngine($sql, $arraySqlBind);
469 for($iter=0; $row=sqlFetchArray($rez); $iter++
)
470 $returnval[$iter]=$row;