2 // Copyright (C) 2011 by following authors:
3 // -Brady Miller <brady@sparmy.com>
5 // -Medical Information Integration, LLC
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // Functions are kept here that will support the clinical rules.
14 require_once(dirname(__FILE__
) . "/patient.inc");
15 require_once(dirname(__FILE__
) . "/forms.inc");
16 require_once(dirname(__FILE__
) . "/formdata.inc.php");
17 require_once(dirname(__FILE__
) . "/options.inc.php");
19 // Display the clinical summary widget.
21 // $patient_id - pid of selected patient
22 // $mode - choose either 'reminders-all' or 'reminders-due' (required)
23 // $dateTarget - target date. If blank then will test with current date as target.
24 // $organize_mode - Way to organize the results (default or plans)
25 function clinical_summary_widget($patient_id,$mode,$dateTarget='',$organize_mode='default') {
27 // Set date to current if not set
28 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
30 // Collect active actions
31 $actions = test_rules_clinic('','passive_alert',$dateTarget,$mode,$patient_id,'',$organize_mode);
33 // Display the actions
34 foreach ($actions as $action) {
36 // Deal with plan names first
37 if ($action['is_plan']) {
39 echo htmlspecialchars( xl("Plan"), ENT_NOQUOTES
) . ": ";
40 echo generate_display_field(array('data_type'=>'1','list_id'=>'clinical_plans'),$action['id']);
45 if ($action['custom_flag']) {
46 // Start link for reminders that use the custom rules input screen
47 echo "<a href='../rules/patient_data.php?category=" .
48 htmlspecialchars( $action['category'], ENT_QUOTES
) . "&item=" .
49 htmlspecialchars( $action['item'], ENT_QUOTES
) .
50 "' class='iframe medium_modal' onclick='top.restoreSession()'>";
52 else if ($action['clin_rem_link']) {
53 // Start link for reminders that use the custom rules input screen
54 echo "<a href='../../../" . $action['reminder_message'] .
55 "' class='iframe medium_modal' onclick='top.restoreSession()'>";
58 // continue, since no link will be created
61 // Display Reminder Details
62 echo generate_display_field(array('data_type'=>'1','list_id'=>'rule_action_category'),$action['category']) .
63 ": " . generate_display_field(array('data_type'=>'1','list_id'=>'rule_action'),$action['item']);
65 if ($action['custom_flag'] ||
$action['clin_rem_link']) {
66 // End link for reminders that use an html link
71 if ($action['due_status']) {
72 // Color code the status (red for past due, purple for due, green for not due and black for soon due)
73 if ($action['due_status'] == "past_due") {
74 echo " (<span style='color:red'>";
76 else if ($action['due_status'] == "due") {
77 echo " (<span style='color:purple'>";
79 else if ($action['due_status'] == "not_due") {
80 echo " (<span style='color:green'>";
83 echo " (<span>";
85 echo generate_display_field(array('data_type'=>'1','list_id'=>'rule_reminder_due_opt'),$action['due_status']) . "</span>)<br>";
94 // Test the clinic rules of entire clinic and create a report or patient reminders
95 // (can also test on one patient or patients of one provider)
97 // $provider - id of a selected provider. If blank, then will test entire clinic. If 'collate', then will test each
98 // provider separately in entire clinic.
99 // $type - rule filter (active_alert,passive_alert,cqm,amc,patient_reminder). If blank then will test all rules.
100 // $dateTarget - target date. If blank then will test with current date as target.
101 // $mode - choose either 'report' or 'reminders-all' or 'reminders-due' (required)
102 // $patient_id - pid of patient. If blank then will check all patients.
103 // $plan - test for specific plan only
104 // $organize_mode - Way to organize the results (default, plans)
106 // Returns a two-dimensional array of results organized by rules:
107 // reminders-due mode - returns an array of reminders (action array elements plus a 'pid' and 'due_status')
108 // reminders-all mode - returns an array of reminders (action array elements plus a 'pid' and 'due_status')
109 // report mode - returns an array of rows for the Clinical Quality Measures (CQM) report
111 // Returns similar to default, but organizes by the active plans
113 function test_rules_clinic($provider='',$type='',$dateTarget='',$mode='',$patient_id='',$plan='',$organize_mode='default') {
115 // Set date to current if not set
116 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
118 // Prepare the results array
121 // If set the $provider to collate, then run through this function recursively.
122 if ($provider == "collate") {
123 // First, collect an array of all providers
124 $query = "SELECT id, lname, fname, npi, federaltaxid FROM users WHERE authorized = 1 ORDER BY lname, fname";
125 $ures = sqlStatement($query);
126 // Second, run through each provider recursively
127 while ($urow = sqlFetchArray($ures)) {
128 $newResults = test_rules_clinic($urow['id'],$type,$dateTarget,$mode,$patient_id,$plan,$organize_mode);
129 if (!empty($newResults)) {
130 $provider_item['is_provider'] = TRUE;
131 $provider_item['prov_lname'] = $urow['lname'];
132 $provider_item['prov_fname'] = $urow['fname'];
133 $provider_item['npi'] = $urow['npi'];
134 $provider_item['federaltaxid'] = $urow['federaltaxid'];
135 array_push($results,$provider_item);
136 $results = array_merge($results,$newResults);
139 // done, so now can return results
143 // If set organize-mode to plans, then collects active plans and run through this function recursively
144 if ($organize_mode == "plans") {
145 // First, collect active plans
146 $plans_resolve = resolve_plans_sql($plan,$patient_id);
147 // Second, run through function recursively
148 foreach ($plans_resolve as $plan_item) {
149 $newResults = test_rules_clinic($provider,$type,$dateTarget,$mode,$patient_id,$plan_item['id']);
150 if (!empty($newResults)) {
151 $plan_item['is_plan'] = TRUE;
152 array_push($results,$plan_item);
153 $results = array_merge($results,$newResults);
156 // done, so now can return results
160 // Collect all patient ids
161 $patientData = array();
162 if (!empty($patient_id)) {
163 // only look at the selected patient
164 array_push($patientData,$patient_id);
167 if (empty($provider)) {
168 // Look at entire practice
169 $rez = sqlStatement("SELECT `pid` FROM `patient_data`");
170 for($iter=0; $row=sqlFetchArray($rez); $iter++
) {
171 $patientData[$iter]=$row;
175 // Look at one provider
176 $rez = sqlStatement("SELECT `pid` FROM `patient_data` " .
177 "WHERE providerID=?", array($provider) );
178 for($iter=0; $row=sqlFetchArray($rez); $iter++
) {
179 $patientData[$iter]=$row;
183 // Go through each patient(s)
185 // If in report mode, then tabulate for each rule:
187 // Patients that pass the filter
188 // Patients that pass the target
189 // If in reminders mode, then create reminders for each rule:
190 // Reminder that action is due soon
191 // Reminder that action is due
192 // Reminder that action is post-due
194 //Collect applicable rules
195 if ($mode != "report") {
196 // Use per patient custom rules (if exist)
197 $rules = resolve_rules_sql($type,$patient_id,FALSE,$plan);
199 else { // $mode = "report"
200 // Only use default rules (do not use patient custom rules)
201 $rules = resolve_rules_sql($type,$patient_id,FALSE,$plan);
204 foreach( $rules as $rowRule ) {
206 // If in reminder mode then need to collect the measurement dates
207 // from rule_reminder table
209 $target_dates = array();
210 if ($mode != "report") {
211 // Calculate the dates to check for
212 if ($type == "patient_reminder") {
213 $reminder_interval_type = "patient_reminder";
215 else { // $type == "passive_alert" or $type == "active_alert"
216 $reminder_interval_type = "clinical_reminder";
218 $target_dates = calculate_reminder_dates($rowRule['id'], $dateTarget, $reminder_interval_type);
220 else { // $mode == "report"
221 // Only use the target date in the report
222 $target_dates[0] = $dateTarget;
231 foreach( $patientData as $rowPatient ) {
233 // Count the total patients
236 $dateCounter = 1; // for reminder mode to keep track of which date checking
237 foreach ( $target_dates as $dateFocus ) {
239 //Skip if date is set to SKIP
240 if ($dateFocus == "SKIP") {
245 //Set date counter and reminder token (applicable for reminders only)
246 if ($dateCounter == 1) {
247 $reminder_due = "soon_due";
249 else if ($dateCounter == 2) {
250 $reminder_due = "due";
252 else { // $dateCounter == 3
253 $reminder_due = "past_due";
256 // Check if pass filter
257 $passFilter = test_filter($rowPatient['pid'],$rowRule['id'],$dateFocus);
258 if ($passFilter === "EXCLUDED") {
259 // increment EXCLUDED and pass_filter counters
260 // and set as FALSE for reminder functionality.
264 error_log("DEBUG: ".$rowPatient['pid'].$rowRule['id'].$dateFocus,0);
267 // increment pass filter counter
275 // Check if pass target
276 $passTarget = test_targets($rowPatient['pid'],$rowRule['id'],'',$dateFocus);
278 // increment pass target counter
280 // send to reminder results
281 if ($mode == "reminders-all") {
282 // place the completed actions into the reminder return array
283 $actionArray = resolve_action_sql($rowRule['id'],'1');
284 foreach ($actionArray as $action) {
285 $action_plus = $action;
286 $action_plus['due_status'] = "not_due";
287 $action_plus['pid'] = $rowPatient['pid'];
288 $results = reminder_results_integrate($results, $action_plus);
294 // send to reminder results
295 if ($mode != "report") {
296 // place the uncompleted actions into the reminder return array
297 $actionArray = resolve_action_sql($rowRule['id'],'1');
298 foreach ($actionArray as $action) {
299 $action_plus = $action;
300 $action_plus['due_status'] = $reminder_due;
301 $action_plus['pid'] = $rowPatient['pid'];
302 $results = reminder_results_integrate($results, $action_plus);
310 // Calculate and save the data for the rule
311 if ($pass_filter > 0) {
312 $percentage = number_format(($pass_target/($pass_filter-$exclude_filter))*100) . xl('%');
315 $percentage = "0". xl('%');
317 if ($mode == "report") {
318 $newRow=array('is_main'=>TRUE,'total_patients'=>$total_patients,'excluded'=>$exclude_filter,'pass_filter'=>$pass_filter,'pass_target'=>$pass_target,'percentage'=>$percentage);
319 $newRow=array_merge($newRow,$rowRule);
320 array_push($results, $newRow);
323 // Find the number of target groups, and go through each one if more than one
324 $targetGroups = numberTargetGroups($rowRule['id']);
325 if ($targetGroups > 1) {
326 if ($mode != "report") {
329 else { // $mode == "report"
332 for ($i = $start_id; $i <= $targetGroups; $i++
){
334 //Reset the target counter
337 foreach( $patientData as $rowPatient ) {
339 $dateCounter = 1; // for reminder mode to keep track of which date checking
340 foreach ( $target_dates as $dateFocus ) {
342 //Skip if date is set to SKIP
343 if ($dateFocus == "SKIP") {
348 //Set date counter and reminder token (applicable for reminders only)
349 if ($dateCounter == 1) {
350 $reminder_due = "soon_due";
352 else if ($dateCounter == 2) {
353 $reminder_due = "due";
355 else { // $dateCounter == 3
356 $reminder_due = "past_due";
359 //Check if pass target
360 $passTarget = test_targets($rowPatient['pid'],$rowRule['id'],$i,$dateFocus);
362 // increment pass target counter
364 // send to reminder results
365 if ($mode == "reminders-all") {
366 // place the completed actions into the reminder return array
367 $actionArray = resolve_action_sql($rowRule['id'],'1');
368 foreach ($actionArray as $action) {
369 $action_plus = $action;
370 $action_plus['due_status'] = "not_due";
371 $action_plus['pid'] = $rowPatient['pid'];
372 $results = reminder_results_integrate($results, $action_plus);
378 // send to reminder results
379 if ($mode != "report") {
380 // place the actions into the reminder return array
381 $actionArray = resolve_action_sql($rowRule['id'],$i);
382 foreach ($actionArray as $action) {
383 $action_plus = $action;
384 $action_plus['due_status'] = $reminder_due;
385 $action_plus['pid'] = $rowPatient['pid'];
386 $results = reminder_results_integrate($results, $action_plus);
394 // Calculate and save the data for the rule
395 if ($pass_filter > 0) {
396 $percentage = number_format(($pass_target/($pass_filter-$exclude_filter))*100) . xl('%');
399 $percentage = "0". xl('%');
402 // Collect action for title (just use the first one, if more than one)
403 $actionArray = resolve_action_sql($rowRule['id'],$i);
404 $action = $actionArray[0];
405 if ($mode == "report") {
406 $newRow=array('is_sub'=>TRUE,'action_category'=>$action['category'],'action_item'=>$action['item'],'total_patients'=>'','excluded'=>'','pass_filter'=>'','pass_target'=>$pass_target,'percentage'=>$percentage);
407 array_push($results, $newRow);
417 // Test filter of a selected rule on a selected patient
419 // $patient_id - pid of selected patient.
420 // $rule - id(string) of selected rule
421 // $dateTarget - target date.
423 // boolean (if pass filter then TRUE, if excluded then 'EXCLUDED', if not pass filter then FALSE)
424 function test_filter($patient_id,$rule,$dateTarget) {
426 // Set date to current if not set
427 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
429 // Collect patient information
430 $patientData = getPatientData($patient_id, "sex, DATE_FORMAT(DOB,'%Y %m %d') as DOB_TS");
432 // -------- Special Filters --------
433 // Check for special flag required by many of the CQM rules, that uses a standard
434 // measurement year (Jan1-Dec31). This adjusted date would then be used for
435 // date to calculate patient age and as the start range if filtering for clinic
436 // appointments. The value (usually will be 1) of this
437 // contains how many years to include.
440 $filter = resolve_filter_sql($rule,'filt_measure_period');
441 if (!empty($filter)) {
443 if ($row['method_detail'] == "year") {
444 $tempDateArray = explode("-",$dateTarget);
445 $tempYear = $tempDateArray[0];
446 // Set too one second before the measurement period
447 $adjustedDate1 = ($tempYear - $row['value']) . "-12-31 23:59:59";
448 // Set too the first second of the measurement period
449 $adjustedDate2 = ($tempYear - ($row['value']-1)) . "-01-01 00:00:00";
450 // Set target date to the last second of the measurement period
451 $dateTarget = ($tempYear - ($row['value']-1)) . "-12-31 23:59:59";
456 // ----------------- INCLUSIONS -----------------
459 // -------- Age Filter (inclusion) ------------
460 // Calculate patient age in years and months
461 if (!empty($adjustedDate1)) {
462 // See above Special Filters section in for details.
463 $patientAgeYears = convertDobtoAgeYearDecimal($patientData['DOB_TS'],$adjustedDate1);
464 $patientAgeMonths = convertDobtoAgeMonthDecimal($patientData['DOB_TS'],$adjustedDate1);
467 $patientAgeYears = convertDobtoAgeYearDecimal($patientData['DOB_TS'],$dateTarget);
468 $patientAgeMonths = convertDobtoAgeMonthDecimal($patientData['DOB_TS'],$dateTarget);
470 // Min age (year) Filter (assume that there in not more than one of each)
471 $filter = resolve_filter_sql($rule,'filt_age_min');
472 if (!empty($filter)) {
474 if ($row ['method_detail'] == "year") {
475 if ( $row['value'] && ($row['value'] > $patientAgeYears) ) {
479 if ($row ['method_detail'] == "month") {
480 if ( $row['value'] && ($row['value'] > $patientAgeMonths) ) {
485 // Max age (year) Filter (assume that there in not more than one of each)
486 $filter = resolve_filter_sql($rule,'filt_age_max');
487 if (!empty($filter)) {
489 if ($row ['method_detail'] == "year") {
490 if ( $row['value'] && ($row['value'] < $patientAgeYears) ) {
494 if ($row ['method_detail'] == "month") {
495 if ( $row['value'] && ($row['value'] < $patientAgeMonths) ) {
501 // -------- Gender Filter (inclusion) ---------
502 // Gender Filter (assume that there in not more than one of each)
503 $filter = resolve_filter_sql($rule,'filt_sex');
504 if (!empty($filter)) {
506 if ( $row['value'] && ($row['value'] != $patientData['sex']) ) {
511 // -------- Database Filter (inclusion) ------
513 $filter = resolve_filter_sql($rule,'filt_database');
514 if ((!empty($filter)) && !database_check($patient_id,$filter,'',$dateTarget)) return false;
516 // -------- Lists Filter (inclusion) ----
517 // Set up lists filter, which is fully customizable and currently includes diagnoses, meds,
518 // surgeries and allergies.
519 $filter = resolve_filter_sql($rule,'filt_lists');
520 if ((!empty($filter)) && !lists_check($patient_id,$filter,$dateTarget)) return false;
522 // -------- Clinic Visit(s) Filter --------
523 $filter = resolve_filter_sql($rule,'filt_encounter_min');
524 if (!empty($filter)) {
526 // For total number of appointments, simply get number of encounters
527 if (!empty($adjustedDate2)) {
528 // See above Special Filters section in for details.
529 $encounters = getEncounters($patient_id,$adjustedDate2,$dateTarget);
532 $encounters = getEncounters($patient_id,'',$dateTarget);
534 (empty($encounters)) ?
$totalNumberAppt = 0 : $totalNumberAppt = count($encounters);
535 if ($row['value'] && $totalNumberAppt < $row['value']) return false;
539 // ----------------- EXCLUSIONS -----------------
542 // -------- Lists Filter (EXCLUSION) ----
543 // Set up lists EXCLUSION filter, which is fully customizable and currently includes diagnoses, meds,
544 // surgeries and allergies.
545 $filter = resolve_filter_sql($rule,'filt_lists',0);
546 if ((!empty($filter)) && lists_check($patient_id,$filter,$dateTarget)) return "EXCLUDED";
548 // Passed all filters, so return true.
552 // Return the number of target groups of a selected rule
554 // $rule - id(string) of rule
556 // integer, number of target groups associated with rule
557 function numberTargetGroups($rule) {
560 $sql = sqlQuery("SELECT max(`group_id`) as numberGroups FROM `rule_target` " .
561 "WHERE `id`=?", array($rule) );
563 if ($sql['numberGroups']) $numberGroups = $sql['numberGroups'];
565 return $numberGroups;
568 // Test targets of a selected rule on a selected patient
570 // $patient_id - pid of selected patient.
571 // $rule - id(string) of selected rule (if blank, then will ignore grouping)
572 // $group_id - group id of target group
573 // $dateTarget - target date.
575 // boolean (if target passes then true, otherwise false)
576 function test_targets($patient_id,$rule,$group_id='',$dateTarget) {
578 // -------- Interval Target ----
579 $interval = resolve_target_sql($rule,$group_id,'target_interval');
581 // -------- Database Target ----
582 // Database Target (includes)
583 $target = resolve_target_sql($rule,$group_id,'target_database');
584 if ((!empty($target)) && !database_check($patient_id,$target,$interval,$dateTarget)) return false;
586 // -------- Procedure (labs,imaging,test,procedures,etc) Target ----
587 // Procedure Target (includes)
588 $target = resolve_target_sql($rule,$group_id,'target_proc');
589 if ((!empty($target)) && !procedure_check($patient_id,$target,$interval,$dateTarget)) return false;
591 // -------- Appointment Target ----
592 // Appointment Target (includes) (Specialized functionality for appointment reminders)
593 $target = resolve_target_sql($rule,$group_id,'target_appt');
594 if ((!empty($target)) && appointment_check($patient_id,$dateTarget)) return false;
596 // Passed all target tests, so return true.
600 // Function to return active plans
602 // $type - plan type filter (normal or cqm or blank)
603 // $patient_id - pid of selected patient. (if custom plan does not exist then
604 // will use the default plan)
605 // $configurableOnly - true if only want the configurable (per patient) plans
606 // (ie. ignore cqm plans)
607 // Return: array containing plans
608 function resolve_plans_sql($type='',$patient_id='0',$configurableOnly=FALSE) {
610 if ($configurableOnly) {
611 // Collect all default, configurable (per patient) plans into an array
612 // (ie. ignore the cqm rules)
613 $sql = sqlStatement("SELECT * FROM `clinical_plans` WHERE `pid`=0 AND `cqm` !=1 ORDER BY `id`");
616 // Collect all default plans into an array
617 $sql = sqlStatement("SELECT * FROM `clinical_plans` WHERE `pid`=0 ORDER BY `id`");
619 $returnArray= array();
620 for($iter=0; $row=sqlFetchArray($sql); $iter++
) {
621 array_push($returnArray,$row);
624 // Now collect the pertinent plans
625 $newReturnArray = array();
627 // Need to select rules (use custom if exist)
628 foreach ($returnArray as $plan) {
629 $customPlan = sqlQuery("SELECT * FROM `clinical_plans` WHERE `id`=? AND `pid`=?", array($plan['id'],$patient_id) );
631 // Decide if use default vs custom plan (preference given to custom plan)
632 if (!empty($customPlan)) {
633 if ($type == "cqm" ) {
634 // For CQM , do not use custom plans (these are to create standard clinic wide reports)
638 // merge the custom plan with the default plan
639 $mergedPlan = array();
640 foreach ($customPlan as $key => $value) {
641 if ($value == NULL && preg_match("/_flag$/",$key)) {
642 // use default setting
643 $mergedPlan[$key] = $plan[$key];
646 // use custom setting
647 $mergedPlan[$key] = $value;
650 $goPlan = $mergedPlan;
657 // Use the chosen plan if set
659 if ($goPlan["${type}_flag"] == 1) {
660 // active, so use the plan
661 array_push($newReturnArray,$goPlan);
665 if ($goPlan['normal_flag'] == 1 ||
666 $goPlan['cqm_flag'] == 1) {
667 // active, so use the plan
668 array_push($newReturnArray,$goPlan);
672 $returnArray = $newReturnArray;
677 // Function to return active rules
679 // $type - rule filter (active_alert,passive_alert,cqm,amc,patient_reminder)
680 // $patient_id - pid of selected patient. (if custom rule does not exist then
681 // will use the default rule)
682 // $configurableOnly - true if only want the configurable (per patient) rules
683 // (ie. ignore cqm and amc rules)
684 // $plan - collect rules for specific plan
685 // Return: array containing rules
686 function resolve_rules_sql($type='',$patient_id='0',$configurableOnly=FALSE,$plan='') {
688 if ($configurableOnly) {
689 // Collect all default, configurable (per patient) rules into an array
690 // (ie. ignore the cqm and amc rules)
691 $sql = sqlStatement("SELECT * FROM `clinical_rules` WHERE `pid`=0 AND `cqm_flag` !=1 AND `amc_flag` !=1 ORDER BY `id`");
694 // Collect all default rules into an array
695 $sql = sqlStatement("SELECT * FROM `clinical_rules` WHERE `pid`=0 ORDER BY `id`");
697 $returnArray= array();
698 for($iter=0; $row=sqlFetchArray($sql); $iter++
) {
699 array_push($returnArray,$row);
702 // Now filter rules for plan (if applicable)
704 $planReturnArray = array();
705 foreach ($returnArray as $rule) {
706 $standardRule = sqlQuery("SELECT * FROM `clinical_plans_rules` " .
707 "WHERE `plan_id`=? AND `rule_id`=?", array($plan,$rule['id']) );
708 if (!empty($standardRule)) {
709 array_push($planReturnArray,$rule);
712 $returnArray = $planReturnArray;
715 // Now collect the pertinent rules
716 $newReturnArray = array();
718 // Need to select rules (use custom if exist)
719 foreach ($returnArray as $rule) {
720 $customRule = sqlQuery("SELECT * FROM `clinical_rules` WHERE `id`=? AND `pid`=?", array($rule['id'],$patient_id) );
722 // Decide if use default vs custom rule (preference given to custom rule)
723 if (!empty($customRule)) {
724 if ($type == "cqm" ||
$type == "amc" ) {
725 // For CQM and AMC, do not use custom rules (these are to create standard clinic wide reports)
729 // merge the custom rule with the default rule
730 $mergedRule = array();
731 foreach ($customRule as $key => $value) {
732 if ($value == NULL && preg_match("/_flag$/",$key)) {
733 // use default setting
734 $mergedRule[$key] = $rule[$key];
737 // use custom setting
738 $mergedRule[$key] = $value;
741 $goRule = $mergedRule;
748 // Use the chosen rule if set
750 if ($goRule["${type}_flag"] == 1) {
751 // active, so use the rule
752 array_push($newReturnArray,$goRule);
756 if ($goRule['active_alert_flag'] == 1 ||
757 $goRule['passive_alert_flag'] == 1 ||
758 $goRule['cqm_flag'] == 1 ||
759 $goRule['amc_flag'] == 1 ||
760 $goRule['patient_reminder_flag'] == 1) {
761 // active, so use the rule
762 array_push($newReturnArray,$goRule);
766 $returnArray = $newReturnArray;
771 // Function to return a specific rule
773 // $rule - id(string) of rule
774 // $patient_id - pid of selected patient. (if set to 0, then will return
775 // the default rule).
776 // Return: array containing a rule
777 function collect_rule($rule,$patient_id='0') {
779 return sqlQuery("SELECT * FROM `clinical_rules` WHERE `id`=? AND `pid`=?", array($rule,$patient_id) );
783 // Function to set a specific rule activity for a specific patient
785 // $rule - id(string) of rule
786 // $type - rule filter (active_alert,passive_alert,cqm,amc,patient_reminder)
787 // $setting - activity of rule (yes,no,default)
788 // $patient_id - pid of selected patient.
790 function set_rule_activity_patient($rule,$type,$setting,$patient_id) {
792 // Don't allow messing with the default rules here
793 if ($patient_id == "0") {
798 if ($setting == "on") {
801 else if ($setting == "off") {
804 else { // $setting == "default"
808 // Collect patient specific rule, if already exists.
809 $query = "SELECT * FROM `clinical_rules` WHERE `id` = ? AND `pid` = ?";
810 $patient_rule = sqlQuery($query, array($rule,$patient_id) );
812 error_log("DEBUG :".$rule.$type.$setting.$patient_id,0);
814 if (empty($patient_rule)) {
815 // Create a new patient specific rule with flags all set to default
816 $query = "INSERT into `clinical_rules` (`id`, `pid`) VALUES (?,?)";
817 sqlStatement($query, array($rule, $patient_id) );
820 // Update patient specific row
821 $query = "UPDATE `clinical_rules` SET `" . add_escape_custom($type) . "_flag`= ? WHERE id = ? AND pid = ?";
822 sqlStatement($query, array($setting,$rule,$patient_id) );
826 // Function to return applicable reminder dates (relative)
828 // $rule - id(string) of selected rule
829 // $reminder_method - string label of filter type
830 // Return: array containing reminder features
831 function resolve_reminder_sql($rule,$reminder_method) {
832 $sql = sqlStatement("SELECT `method_detail`, `value` FROM `rule_reminder` " .
833 "WHERE `id`=? AND `method`=?", array($rule, $reminder_method) );
835 $returnArray= array();
836 for($iter=0; $row=sqlFetchArray($sql); $iter++
) {
837 array_push($returnArray,$row);
842 // Function to return applicable filters
844 // $rule - id(string) of selected rule
845 // $filter_method - string label of filter type
846 // $include_flag - to allow selection for included or excluded filters
847 // Return: array containing filters
848 function resolve_filter_sql($rule,$filter_method,$include_flag=1) {
849 $sql = sqlStatement("SELECT `method_detail`, `value`, `required_flag` FROM `rule_filter` " .
850 "WHERE `id`=? AND `method`=? AND `include_flag`=?", array($rule, $filter_method, $include_flag) );
852 $returnArray= array();
853 for($iter=0; $row=sqlFetchArray($sql); $iter++
) {
854 array_push($returnArray,$row);
859 // Function to return applicable targets
861 // $rule - id(string) of selected rule
862 // $group_id - group id of target group (if blank, then will ignore grouping)
863 // $target_method - string label of target type
864 // $include_flag - to allow selection for included or excluded targets
865 // Return: array containing targets
866 function resolve_target_sql($rule,$group_id='',$target_method,$include_flag=1) {
869 $sql = sqlStatement("SELECT `value`, `required_flag`, `interval` FROM `rule_target` " .
870 "WHERE `id`=? AND `group_id`=? AND `method`=? AND `include_flag`=?", array($rule, $group_id, $target_method, $include_flag) );
873 $sql = sqlStatement("SELECT `value`, `required_flag`, `interval` FROM `rule_target` " .
874 "WHERE `id`=? AND `method`=? AND `include_flag`=?", array($rule, $target_method, $include_flag) );
877 $returnArray= array();
878 for($iter=0; $row=sqlFetchArray($sql); $iter++
) {
879 array_push($returnArray,$row);
884 // Function to return applicable actions
886 // $rule - id(string) of selected rule
887 // $group_id - group id of target group (if blank, then will ignore grouping)
888 // Return: array containing actions
889 function resolve_action_sql($rule,$group_id='') {
892 $sql = sqlStatement("SELECT b.category, b.item, b.clin_rem_link, b.reminder_message, b.custom_flag " .
893 "FROM `rule_action` as a " .
894 "JOIN `rule_action_item` as b " .
895 "ON a.category = b.category AND a.item = b.item " .
896 "WHERE a.id=? AND a.group_id=?", array($rule,$group_id) );
899 $sql = sqlStatement("SELECT b.category, b.item, b.value, b.custom_flag " .
900 "FROM `rule_action` as a " .
901 "JOIN `rule_action_item` as b " .
902 "ON a.category = b.category AND a.item = b.item " .
903 "WHERE a.id=?", array($rule) );
906 $returnArray= array();
907 for($iter=0; $row=sqlFetchArray($sql); $iter++
) {
908 array_push($returnArray,$row);
913 // Function to check database filters and targets
915 // $patient_id - pid of selected patient.
916 // $filter - array containing filter/target elements
917 // $interval - used for the interval elements
918 // $dateTarget - target date. blank is current date.
919 // Return: boolean if check passed, otherwise false
920 function database_check($patient_id,$filter,$interval='',$dateTarget='') {
921 $isMatch = false; //matching flag
923 // Set date to current if not set
924 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
926 // Unpackage interval information
927 // (Assume only one for now and only pertinent for targets)
930 if (!empty($interval)) {
931 $intervalType = $interval[0]['value'];
932 $intervalValue = $interval[0]['interval'];
935 foreach( $filter as $row ) {
937 // [0]=>special modes
938 $temp_df = explode("::",$row['value']);
940 if ($temp_df[0] == "CUSTOM") {
942 // [0]=>special modes(CUSTOM) [1]=>category [2]=>item [3]=>complete? [4]=>number of hits comparison [5]=>number of hits
943 if (exist_custom_item($patient_id, $temp_df[1], $temp_df[2], $temp_df[3], $temp_df[4], $temp_df[5], $intervalType, $intervalValue, $dateTarget)) {
948 // If this is a required entry then return false
949 if ($row['required_flag']) return false;
952 else if ($temp_df[0] == "LIFESTYLE") {
954 // [0]=>special modes(LIFESTYLE) [1]=>column [2]=>status
955 if (exist_lifestyle_item($patient_id, $temp_df[1], $temp_df[2], $dateTarget)) {
960 // If this is a required entry then return false
961 if ($row['required_flag']) return false;
967 // [0]=>special modes(BLANK) [1]=>table [2]=>column [3]=>value comparison [4]=>value [5]=>number of hits comparison [6]=>number of hits
968 if (exist_database_item($patient_id, $temp_df[1], $temp_df[2], $temp_df[3], $temp_df[4], $temp_df[5], $temp_df[6], $intervalType, $intervalValue, $dateTarget)) {
973 // If this is a required entry then return false
974 if ($row['required_flag']) return false;
979 // return results of check
983 // Function to check procedure filters and targets
985 // $patient_id - pid of selected patient.
986 // $filter - array containing filter/target elements
987 // $interval - used for the interval elements
988 // $dateTarget - target date. blank is current date.
989 // Return: boolean if check passed, otherwise false
990 function procedure_check($patient_id,$filter,$interval='',$dateTarget='') {
991 $isMatch = false; //matching flag
993 // Set date to current if not set
994 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
996 // Unpackage interval information
997 // (Assume only one for now and only pertinent for targets)
1000 if (!empty($interval)) {
1001 $intervalType = $interval[0]['value'];
1002 $intervalValue = $interval[0]['interval'];
1005 foreach( $filter as $row ) {
1007 // [0]=>title [1]=>code [2]=>value comparison [3]=>value [4]=>number of hits comparison [5]=>number of hits
1009 // <type(ICD9,CPT)>:<identifier>; etc.
1010 $temp_df = explode("::",$row['value']);
1011 if (exist_procedure_item($patient_id, $temp_df[0], $temp_df[1], $temp_df[2], $temp_df[3], $temp_df[4], $temp_df[5], $intervalType, $intervalValue, $dateTarget)) {
1016 // If this is a required entry then return false
1017 if ($row['required_flag']) return false;
1021 // return results of check
1025 // Function to check for appointment
1027 // $patient_id - pid of selected patient.
1028 // $dateTarget - target date.
1029 // Return: boolean if appt exist, otherwise false
1030 function appointment_check($patient_id,$dateTarget='') {
1031 $isMatch = false; //matching flag
1033 // Set date to current if not set (although should always be set)
1034 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
1035 $dateTargetRound = date('Y-m-d',$dateTarget);
1038 $currentDate = date('Y-m-d H:i:s');
1039 $currentDateRound = date('Y-m-d',$dateCurrent);
1041 // Basically, if the appointment is within the current date to the target date,
1042 // then return true. (will not send reminders on same day as appointment)
1043 $sql = sqlStatement("SELECT openemr_postcalendar_events.pc_eid, " .
1044 "openemr_postcalendar_events.pc_title, " .
1045 "openemr_postcalendar_events.pc_eventDate, " .
1046 "openemr_postcalendar_events.pc_startTime, " .
1047 "openemr_postcalendar_events.pc_endTime " .
1048 "FROM openemr_postcalendar_events " .
1049 "WHERE openemr_postcalendar_events.pc_eventDate > ? " .
1050 "AND openemr_postcalendar_events.pc_eventDate <= ? " .
1051 "AND openemr_postcalendar_events.pc_pid = ?", array($currentDate,$dateTarget,$patient_id) );
1053 // return results of check
1055 // TODO: Figure out how to have multiple appointment and changing appointment reminders.
1056 // Plan to send back array of appt info (eid, time, date, etc.)
1058 if (sqlNumRows($sql) > 0) {
1065 // Function to check lists filters and targets
1066 // Customizable and currently includes diagnoses, medications,
1067 // allergies and surgeries.
1069 // $patient_id - pid of selected patient.
1070 // $filter - array containing lists filter/target elements
1071 // $dateTarget - target date. blank is current date.
1072 // Return: boolean if check passed, otherwise false
1073 function lists_check($patient_id,$filter,$dateTarget) {
1074 $isMatch = false; //matching flag
1076 // Set date to current if not set
1077 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
1079 foreach ( $filter as $row ) {
1080 if (exist_lists_item($patient_id, $row['method_detail'], $row['value'], $dateTarget)) {
1085 // If this is a required entry then return false
1086 if ($row['required_flag']) return false;
1090 // return results of check
1094 // Function to check for existance of data in database for a patient
1096 // $patient_id - pid of selected patient.
1097 // $table - selected mysql table
1098 // $column - selected mysql column
1099 // $data_comp - data comparison (eq,ne,gt,ge,lt,le)
1100 // $data - selected data in the mysql database
1101 // $num_items_comp - number items comparison (eq,ne,gt,ge,lt,le)
1102 // $num_items_thres - number of items threshold
1103 // $intervalType - type of interval (ie. year)
1104 // $intervalValue - searched for within this many times of the interval type
1105 // $dateTarget - target date.
1106 // Return: boolean if check passed, otherwise false
1107 function exist_database_item($patient_id,$table,$column='',$data_comp,$data='',$num_items_comp,$num_items_thres,$intervalType='',$intervalValue='',$dateTarget='') {
1109 // Set date to current if not set
1110 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
1112 // Collect the correct column label for patient id in the table
1113 $patient_id_label = collect_database_label('pid',$table);
1115 // Get the interval sql query string
1116 $dateSql = sql_interval_string($table,$intervalType,$intervalValue,$dateTarget);
1118 // If just checking for existence (ie. data is empty),
1119 // then simply set the comparison operator to ne.
1124 // get the appropriate sql comparison operator
1125 $compSql = convertCompSql($data_comp);
1128 if (empty($column)) {
1129 // simple search for any table entries
1130 $sql = sqlStatement("SELECT * " .
1131 "FROM `" . add_escape_custom($table) . "` " .
1132 "WHERE `" . add_escape_custom($patient_id_label) . "`=?", array($patient_id) );
1135 // search for number of specific items
1136 $sql = sqlStatement("SELECT `" . add_escape_custom($column) . "` " .
1137 "FROM `" . add_escape_custom($table) . "` " .
1138 "WHERE `" . add_escape_custom($column) ."`" . $compSql . "? " .
1139 "AND `" . add_escape_custom($patient_id_label) . "`=? " .
1140 $dateSql, array($data,$patient_id) );
1143 // See if number of returned items passes the comparison
1144 return itemsNumberCompare($num_items_comp, $num_items_thres, sqlNumRows($sql));
1147 // Function to check for existence of procedure(s) for a patient
1149 // $patient_id - pid of selected patient.
1150 // $proc_title - procedure title
1151 // $proc_code - procedure identifier code (array)
1152 // $result_comp - results comparison (eq,ne,gt,ge,lt,le)
1153 // $result_data - results data
1154 // $num_items_comp - number items comparison (eq,ne,gt,ge,lt,le)
1155 // $num_items_thres - number of items threshold
1156 // $intervalType - type of interval (ie. year)
1157 // $intervalValue - searched for within this many times of the interval type
1158 // $dateTarget - target date.
1159 // Return: boolean if check passed, otherwise false
1160 function exist_procedure_item($patient_id,$proc_title,$proc_code,$result_comp,$result_data='',$num_items_comp,$num_items_thres,$intervalType='',$intervalValue='',$dateTarget='') {
1162 // Set date to current if not set
1163 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
1165 // Set the table exception (for looking up pertinent date and pid sql columns)
1166 $table = "PROCEDURE-EXCEPTION";
1168 // Collect the correct column label for patient id in the table
1169 $patient_id_label = collect_database_label('pid',$table);
1171 // Get the interval sql query string
1172 $dateSql = sql_interval_string($table,$intervalType,$intervalValue,$dateTarget);
1176 // Figure out a way to use the identifiers codes
1180 // If just checking for existence (ie result_data is empty),
1181 // then simply set the comparison operator to ne.
1182 if (empty($result_data)) {
1183 $result_comp = "ne";
1186 // get the appropriate sql comparison operator
1187 $compSql = convertCompSql($result_comp);
1189 // collect specific items that fulfill request
1190 $sql = sqlStatement("SELECT procedure_result.result " .
1191 "FROM `procedure_type`, " .
1192 "`procedure_order`, " .
1193 "`procedure_report`, " .
1194 "`procedure_result` " .
1195 "WHERE procedure_type.procedure_type_id = procedure_order.procedure_type_id " .
1196 "AND procedure_order.procedure_order_id = procedure_report.procedure_order_id " .
1197 "AND procedure_report.procedure_report_id = procedure_result.procedure_report_id " .
1198 "AND procedure_type.name = ? " .
1199 "AND procedure_result.result " . $compSql . " ? " .
1200 "AND " . add_escape_custom($patient_id_label) . " = ? " .
1201 $dateSql, array($proc_title,$result_data,$patient_id) );
1203 // See if number of returned items passes the comparison
1204 return itemsNumberCompare($num_items_comp, $num_items_thres, sqlNumRows($sql));
1207 // Function to check for existance of data for a patient in the rule_patient_data table
1209 // $patient_id - pid of selected patient.
1210 // $category - label in category column
1211 // $item - label in item column
1212 // $complete - label in complete column (YES,NO, or blank)
1213 // $num_items_comp - number items comparison (eq,ne,gt,ge,lt,le)
1214 // $num_items_thres - number of items threshold
1215 // $intervalType - type of interval (ie. year)
1216 // $intervalValue - searched for within this many times of the interval type
1217 // $dateTarget - target date.
1218 // Return: boolean if check passed, otherwise false
1219 function exist_custom_item($patient_id,$category,$item,$complete,$num_items_comp,$num_items_thres,$intervalType='',$intervalValue='',$dateTarget) {
1222 $table = 'rule_patient_data';
1224 // Collect the correct column label for patient id in the table
1225 $patient_id_label = collect_database_label('pid',$table);
1227 // Get the interval sql query string
1228 $dateSql = sql_interval_string($table,$intervalType,$intervalValue,$dateTarget);
1230 // search for number of specific items
1231 $sql = sqlStatement("SELECT `result` " .
1232 "FROM `" . add_escape_custom($table) . "` " .
1233 "WHERE `category`=? " .
1235 "AND `complete`=? " .
1236 "AND `" . add_escape_custom($patient_id_label) . "`=? " .
1237 $dateSql, array($category,$item,$complete,$patient_id) );
1239 // See if number of returned items passes the comparison
1240 return itemsNumberCompare($num_items_comp, $num_items_thres, sqlNumRows($sql));
1243 // Function to check for existance of data for a patient in lifestyle section
1245 // $patient_id - pid of selected patient.
1246 // $lifestyle - selected label of mysql column of patient history
1247 // $status - specific status of selected lifestyle element
1248 // $dateTarget - target date. blank is current date.
1249 // Return: boolean if check passed, otherwise false
1250 function exist_lifestyle_item($patient_id,$lifestyle,$status,$dateTarget) {
1252 // Set date to current if not set
1253 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
1255 // Collect pertinent history data
1256 $history = getHistoryData($patient_id, $lifestyle,'',$dateTarget);
1259 $stringFlag = strstr($history[$lifestyle], "|".$status);
1260 if (empty($status)) {
1261 // Only ensuring any data has been entered into the field
1264 if ( $history[$lifestyle] &&
1265 $history[$lifestyle] != '|0|' &&
1274 // Function to check for lists item of a patient
1275 // Fully customizable and includes diagnoses, medications,
1276 // allergies, and surgeries.
1278 // $patient_id - pid of selected patient.
1279 // $type - type (medical_problem, allergy, medication, etc)
1280 // $value - value searching for
1281 // $dateTarget - target date. blank is current date.
1282 // Return: boolean if check passed, otherwise false
1283 function exist_lists_item($patient_id,$type,$value,$dateTarget) {
1285 // Set date to current if not set
1286 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
1288 if ($type == "medical_problem") {
1289 // Specific search for diagnoses
1290 // Explode the value into diagnosis code type and code
1291 $temp_diag_array = explode("::",$value);
1292 $code_type = $temp_diag_array[0];
1293 $diagnosis = $temp_diag_array[1];
1294 if ($code_type=='CUSTOM') {
1295 // Deal with custom code first (title column in lists table)
1296 $response = sqlQuery("SELECT * FROM `lists` " .
1300 "AND ( (`begdate` IS NULL AND `date`<=?) OR (`begdate` IS NOT NULL AND `begdate`<=?) ) " .
1301 "AND ( (`enddate` IS NULL) OR (`enddate` IS NOT NULL AND `enddate`>=?) )", array($type,$patient_id,$diagnosis,$dateTarget,$dateTarget,$dateTarget) );
1302 if (!empty($response)) return true;
1305 // Deal with the set code types (diagnosis column in lists table)
1306 $response = sqlQuery("SELECT * FROM `lists` " .
1309 "AND `diagnosis` LIKE ? " .
1310 "AND ( (`begdate` IS NULL AND `date`<=?) OR (`begdate` IS NOT NULL AND `begdate`<=?) ) " .
1311 "AND ( (`enddate` IS NULL) OR (`enddate` IS NOT NULL AND `enddate`>=?) )", array($type,$patient_id,"%".$code_type.":".$diagnosis."%",$dateTarget,$dateTarget,$dateTarget) );
1312 if (!empty($response)) return true;
1315 else { // generic lists item that requires no customization
1316 $response = sqlQuery("SELECT * FROM `lists` " .
1320 "AND ( (`begdate` IS NULL AND `date`<=?) OR (`begdate` IS NOT NULL AND `begdate`<=?) ) " .
1321 "AND ( (`enddate` IS NULL) OR (`enddate` IS NOT NULL AND `enddate`>=?) )", array($type,$patient_id,$value,$dateTarget,$dateTarget,$dateTarget) );
1322 if (!empty($response)) return true;
1328 // Function to return part of sql query to deal with interval
1330 // $table - selected mysql table (or EXCEPTION(s))
1331 // $intervalType - type of interval (ie. year)
1332 // $intervalValue - searched for within this many times of the interval type
1333 // $dateTarget - target date.
1334 // Return: string containing pertinent date interval filter for mysql query
1335 function sql_interval_string($table,$intervalType,$intervalValue,$dateTarget) {
1339 // Collect the correct column label for date in the table
1340 $date_label = collect_database_label('date',$table);
1342 // Deal with interval
1343 if (!empty($intervalType)) {
1344 switch($intervalType) {
1346 $dateSql = "AND (" . add_escape_custom($date_label) .
1347 " BETWEEN DATE_SUB('" . add_escape_custom($dateTarget) .
1348 "', INTERVAL " . add_escape_custom($intervalValue) .
1349 " YEAR) AND '" . add_escape_custom($dateTarget) . "') ";
1352 $dateSql = "AND (" . add_escape_custom($date_label) .
1353 " BETWEEN DATE_SUB('" . add_escape_custom($dateTarget) .
1354 "', INTERVAL " . add_escape_custom($intervalValue) .
1355 " MONTH) AND '" . add_escape_custom($dateTarget) . "') ";
1358 $dateSql = "AND (" . add_escape_custom($date_label) .
1359 " BETWEEN DATE_SUB('" . add_escape_custom($dateTarget) .
1360 "', INTERVAL " . add_escape_custom($intervalValue) .
1361 " WEEK) AND '" . add_escape_custom($dateTarget) . "') ";
1364 $dateSql = "AND (" . add_escape_custom($date_label) .
1365 " BETWEEN DATE_SUB('" . add_escape_custom($dateTarget) .
1366 "', INTERVAL " . add_escape_custom($intervalValue) .
1367 " DAY) AND '" . add_escape_custom($dateTarget) . "') ";
1370 $dateSql = "AND (" . add_escape_custom($date_label) .
1371 " BETWEEN DATE_SUB('" . add_escape_custom($dateTarget) .
1372 "', INTERVAL " . add_escape_custom($intervalValue) .
1373 " HOUR) AND '" . add_escape_custom($dateTarget) . "') ";
1376 $dateSql = "AND (" . add_escape_custom($date_label) .
1377 " BETWEEN DATE_SUB('" . add_escape_custom($dateTarget) .
1378 "', INTERVAL " . add_escape_custom($intervalValue) .
1379 " MINUTE) AND '" . add_escape_custom($dateTarget) . "') ";
1382 $dateSql = "AND (" . add_escape_custom($date_label) .
1383 " BETWEEN DATE_SUB('" . add_escape_custom($dateTarget) .
1384 "', INTERVAL " . add_escape_custom($intervalValue) .
1385 " SECOND) AND '" . add_escape_custom($dateTarget) . "') ";
1388 // Flu season to be hard-coded as September thru February
1389 // (Should make this modifiable in the future)
1390 // ($intervalValue is not used)
1391 $dateArray = explode("-",$dateTarget);
1392 $Year = $dateArray[0];
1393 $dateThisYear = $Year . "-09-01";
1394 $dateLastYear = ($Year-1) . "-09-01";
1397 "MONTH('" . add_escape_custom($dateTarget) . "') < 9 " .
1398 "AND " . add_escape_custom($date_label) . " >= '" . $dateLastYear . "' ) " .
1400 "MONTH('" . add_escape_custom($dateTarget) . "') >= 9 " .
1401 "AND " . add_escape_custom($date_label) . " >= '" . $dateThisYear . "' ))" .
1402 "AND " . add_escape_custom($date_label) . " <= '" . add_escape_custom($dateTarget) . "' ";
1407 $dateSql = "AND " . add_escape_custom($date_label) .
1408 " <= '" . add_escape_custom($dateTarget) . "' ";
1411 // return the sql interval string
1415 // Function to collect generic column labels from tables.
1416 // It currently works for date and pid.
1417 // Will need to expand this as algorithm grows.
1419 // $label - element (pid or date)
1420 // $table - selected mysql table (or EXCEPTION(s))
1421 // Return: string containing official label of selected element
1422 function collect_database_label($label,$table) {
1424 if ($table == 'PROCEDURE-EXCEPTION') {
1425 // return cell to get procedure collection
1426 // special case since reuqires joing of multiple
1427 // tables to get this value
1428 if ($label == "pid") {
1429 $returnedLabel = "procedure_order.patient_id";
1431 else if ($label == "date") {
1432 $returnedLabel = "procedure_result.date";
1435 // unknown label, so return the original label
1436 $returnedLabel = $label;
1439 else if ($table == 'immunizations') {
1440 // return requested label for immunization table
1441 if ($label == "pid") {
1442 $returnedLabel = "patient_id";
1444 else if ($label == "date") {
1445 $returnedLabel = "`administered_date`";
1448 // unknown label, so return the original label
1449 $returnedLabel = $label;
1453 // return requested label for default tables
1454 if ($label == "pid") {
1455 $returnedLabel = "pid";
1457 else if ($label == "date") {
1458 $returnedLabel = "`date`";
1461 // unknown label, so return the original label
1462 $returnedLabel = $label;
1466 return $returnedLabel;
1469 // Simple function to avoid processing of duplicate actions
1471 // $actions - 2-dimensional array with all current active targets
1472 // $action - array of selected target to test for duplicate
1473 // Return: boolean, true if duplicate, false if not duplicate
1474 function is_duplicate_action($actions,$action) {
1475 foreach ($actions as $row) {
1476 if ($row['category'] == $action['category'] &&
1477 $row['item'] == $action['item'] &&
1478 $row['value'] == $action['value']) {
1488 // Calculate the reminder dates.
1490 // $rule - id(string) of selected rule
1491 // $dateTarget - target date. If blank then will test with current date as target.
1492 // $type - either 'patient_reminder' or 'clinical_reminder'
1493 // For now, will always return an array of 3 dates:
1494 // first date is before the target date (past_due) (default of 1 month)
1495 // second date is the target date (due)
1496 // third date is after the target date (soon_due) (default of 2 weeks)
1497 function calculate_reminder_dates($rule, $dateTarget='',$type) {
1499 // Set date to current if not set
1500 $dateTarget = ($dateTarget) ?
$dateTarget : date('Y-m-d H:i:s');
1502 // Collect the current date settings (to ensure not skip)
1503 $res = resolve_reminder_sql($rule, $type.'_current');
1506 if ($row ['method_detail'] == "SKIP") {
1507 $dateTarget = "SKIP";
1511 // Collect the past_due date
1512 $past_due_date == "";
1513 $res = resolve_reminder_sql($rule, $type.'_post');
1516 if ($row ['method_detail'] == "week") {
1517 $past_due_date = date("Y-m-d H:i:s", strtotime($dateTarget . " -" . $row ['value'] . " week"));
1519 if ($row ['method_detail'] == "month") {
1520 $past_due_date = date("Y-m-d H:i:s", strtotime($dateTarget . " -" . $row ['value'] . " month"));
1522 if ($row ['method_detail'] == "hour") {
1523 $past_due_date = date("Y-m-d H:i:s", strtotime($dateTarget . " -" . $row ['value'] . " hour"));
1525 if ($row ['method_detail'] == "SKIP") {
1526 $past_due_date = "SKIP";
1530 // empty settings, so use default of one month
1531 $past_due_date = date("Y-m-d H:i:s", strtotime($dateTarget . " -1 month"));
1534 // Collect the soon_due date
1535 $soon_due_date == "";
1536 $res = resolve_reminder_sql($rule, $type.'_pre');
1539 if ($row ['method_detail'] == "week") {
1540 $soon_due_date = date("Y-m-d H:i:s", strtotime($dateTarget . " +" . $row ['value'] . " week"));
1542 if ($row ['method_detail'] == "month") {
1543 $soon_due_date = date("Y-m-d H:i:s", strtotime($dateTarget . " +" . $row ['value'] . " month"));
1545 if ($row ['method_detail'] == "hour") {
1546 $soon_due_date = date("Y-m-d H:i:s", strtotime($dateTarget . " -" . $row ['value'] . " hour"));
1548 if ($row ['method_detail'] == "SKIP") {
1549 $soon_due_date = "SKIP";
1553 // empty settings, so use default of one month
1554 $soon_due_date = date("Y-m-d H:i:s", strtotime($dateTarget . " +2 week"));
1557 // Return the array of three dates
1558 return array($soon_due_date,$dateTarget,$past_due_date);
1561 // Adds an action into the reminder array
1563 // $reminderOldArray - Contains the current array of reminders
1564 // $reminderNew - Array of a new reminder
1566 // An array of reminders
1567 function reminder_results_integrate($reminderOldArray, $reminderNew) {
1571 // If reminderArray is empty, then insert new reminder
1572 if (empty($reminderOldArray)) {
1573 array_push($results, $reminderNew);
1577 // If duplicate reminder, then replace the old one
1579 foreach ($reminderOldArray as $reminderOld) {
1580 if ( $reminderOld['pid'] == $reminderNew['pid'] &&
1581 $reminderOld['category'] == $reminderNew['category'] &&
1582 $reminderOld['item'] == $reminderNew['item']) {
1583 array_push($results, $reminderNew);
1587 array_push($results, $reminderOld);
1591 // If a new reminder, then insert the new reminder
1593 array_push($results, $reminderNew);
1599 // Compares number of items with requested comparison operator
1601 // $comp - Comparison operator(eq,ne,gt,ge,lt,le)
1602 // $thres - Threshold used in comparison
1603 // $num_items - Number of items
1605 // Boolean of comparison results
1606 function itemsNumberCompare($comp, $thres, $num_items) {
1608 if ( ($comp == "eq") && ($num_items == $thres) ) {
1611 else if ( ($comp == "ne") && ($num_items != $thres) && ($num_items > 0) ) {
1614 else if ( ($comp == "gt") && ($num_items > $thres) ) {
1617 else if ( ($comp == "ge") && ($num_items >= $thres) ) {
1620 else if ( ($comp == "lt") && ($num_items < $thres) && ($num_items > 0) ) {
1623 else if ( ($comp == "le") && ($num_items <= $thres) && ($num_items > 0) ) {
1631 // Converts a text comparison operator to sql equivalent
1633 // $comp - Comparison operator(eq,ne,gt,ge,lt,le)
1635 // String containing sql compatible comparison operator
1636 function convertCompSql($comp) {
1638 if ($comp == "eq") {
1641 else if ($comp == "ne") {
1644 else if ($comp == "gt") {
1647 else if ($comp == "ge") {
1650 else if ($comp == "lt") {
1653 else { // ($comp == "le")
1658 // Function to find age in years (with decimal) on the target date
1660 // $dob - date of birth
1661 // $target - date to calculate age on
1662 // Return: decimal, years(decimal) from dob to target(date)
1663 function convertDobtoAgeYearDecimal($dob,$target) {
1665 // Grab year, month, and day from dob and dateTarget
1666 $dateDOB = explode(" ",$dob);
1667 $dateTarget = explode(" ",$target);
1669 // Collect differences
1670 $iDiffYear = $dateTarget[0] - $dateDOB[0];
1671 $iDiffMonth = $dateTarget[1] - $dateDOB[1];
1672 $iDiffDay = $dateTarget[2] - $dateDOB[2];
1674 // If birthday has not happen yet for this year, subtract 1.
1675 if ($iDiffMonth < 0 ||
($iDiffMonth == 0 && $iDiffDay < 0))
1683 // Function to find age in months (with decimal) on the target date
1685 // $dob - date of birth
1686 // $target - date to calculate age on
1687 // Return: decimal, months(decimal) from dob to target(date)
1688 function convertDobtoAgeMonthDecimal($dob,$target) {
1690 // Grab year, month, and day from dob and dateTarget
1691 $dateDOB = explode(" ",$dob);
1692 $dateTarget = explode(" ",$target);
1694 // Collect differences
1695 $iDiffYear = $dateTarget[0] - $dateDOB[0];
1696 $iDiffMonth = $dateTarget[1] - $dateDOB[1];
1697 $iDiffDay = $dateTarget[2] - $dateDOB[2];
1699 // If birthday has not happen yet for this year, subtract 1.
1700 if ($iDiffMonth < 0 ||
($iDiffMonth == 0 && $iDiffDay < 0))
1705 return (12 * $iDiffYear) +
$iDiffMonth;