From e752bb2b87b06d2ff7917f62ad7d6277dcc29724 Mon Sep 17 00:00:00 2001 From: Brady Miller Date: Sun, 10 Mar 2024 00:36:02 -0800 Subject: [PATCH] fixes: CDR engine fixes for duplicate use of actions across rules in addition to an optimization The optimization both clarifies the code regarding which $reminder_due status is chosen and also minimizes the calls to reminder_results_integrate, which could get resource intensive if overuse it. --- library/clinical_rules.php | 169 +++++++++++++++++++++++++++------------------ 1 file changed, 102 insertions(+), 67 deletions(-) diff --git a/library/clinical_rules.php b/library/clinical_rules.php index 816052406..5f8091f96 100644 --- a/library/clinical_rules.php +++ b/library/clinical_rules.php @@ -1034,11 +1034,11 @@ function test_rules_clinic($provider = '', $type = '', $dateTarget = '', $mode = //Set date counter and reminder token (applicable for reminders only) if ($dateCounter == 1) { - $reminder_due = "soon_due"; + $reminder_due = "not_due"; } elseif ($dateCounter == 2) { - $reminder_due = "due"; + $reminder_due = "soon_due"; } else { // $dateCounter == 3 - $reminder_due = "past_due"; + $reminder_due = "due"; } // Check if pass filter @@ -1074,31 +1074,37 @@ function test_rules_clinic($provider = '', $type = '', $dateTarget = '', $mode = $temp_track_pass = 1; } - // send to reminder results for reminders-all when not_due (ie. $dateCounter == 1) - if (($mode == "reminders-all") && ($dateCounter == 1)) { - // place the completed actions into the reminder return array + if ( + ($mode != "report") && + (($mode == "reminders-all") || (($mode != "reminders-all") && ($reminder_due != "not_due"))) + ) { + // Place the actions into the reminder return array. + // There are 2 reminder modes, reminders-due and reminders-all. The not_due reminders are not + // shown in reminders-due mode but are shown in reminders-all mode. So this block is skipped + // if due_status is 'not_due' and mode is not 'reminders-all'. $actionArray = resolve_action_sql($rowRule['id'], '1'); foreach ($actionArray as $action) { $action_plus = $action; - $action_plus['due_status'] = "not_due"; + $action_plus['due_status'] = $reminder_due; $action_plus['pid'] = $rowPatient['pid']; $action_plus['rule_id'] = $rowRule['id']; - $results = reminder_results_integrate($results, $action_plus); + $results = reminder_results_integrate($results, $action_plus, $mode); } } break; } else { - // send to reminder results - if ($mode != "report") { - // place the uncompleted actions into the reminder return array + if (($mode != "report") && ($dateCounter == 3)) { + // Did not pass any of the target dates, so place the past_due actions into the reminder + // return array when runnning in one of the reminders mode (either reminders-due mode + // or reminders-all mode). $actionArray = resolve_action_sql($rowRule['id'], '1'); foreach ($actionArray as $action) { $action_plus = $action; - $action_plus['due_status'] = $reminder_due; + $action_plus['due_status'] = 'past_due'; $action_plus['pid'] = $rowPatient['pid']; $action_plus['rule_id'] = $rowRule['id']; - $results = reminder_results_integrate($results, $action_plus); + $results = reminder_results_integrate($results, $action_plus, $mode); } } } @@ -1163,11 +1169,11 @@ function test_rules_clinic($provider = '', $type = '', $dateTarget = '', $mode = //Set date counter and reminder token (applicable for reminders only) if ($dateCounter == 1) { - $reminder_due = "soon_due"; + $reminder_due = "not_due"; } elseif ($dateCounter == 2) { - $reminder_due = "due"; + $reminder_due = "soon_due"; } else { // $dateCounter == 3 - $reminder_due = "past_due"; + $reminder_due = "due"; } // Check if pass filter @@ -1197,31 +1203,37 @@ function test_rules_clinic($provider = '', $type = '', $dateTarget = '', $mode = $temp_track_pass = 1; } - // send to reminder results for reminders-all when not_due (ie. $dateCounter == 1) - if (($mode == "reminders-all") && ($dateCounter == 1)) { - // place the completed actions into the reminder return array + if ( + ($mode != "report") && + (($mode == "reminders-all") || (($mode != "reminders-all") && ($reminder_due != "not_due"))) + ) { + // Place the actions into the reminder return array. + // There are 2 reminder modes, reminders-due and reminders-all. The not_due reminders are not + // shown in reminders-due mode but are shown in reminders-all mode. So this block is skipped + // if due_status is 'not_due' and mode is not 'reminders-all'. $actionArray = resolve_action_sql($rowRule['id'], $i); foreach ($actionArray as $action) { $action_plus = $action; - $action_plus['due_status'] = "not_due"; + $action_plus['due_status'] = $reminder_due; $action_plus['pid'] = $rowPatient['pid']; $action_plus['rule_id'] = $rowRule['id']; - $results = reminder_results_integrate($results, $action_plus); + $results = reminder_results_integrate($results, $action_plus, $mode); } } break; } else { - // send to reminder results - if ($mode != "report") { - // place the actions into the reminder return array + if (($mode != "report") && ($dateCounter == 3)) { + // Did not pass any of the target dates, so place the past_due actions into the reminder + // return array when runnning in one of the reminders mode (either reminders-due mode + // or reminders-all mode). $actionArray = resolve_action_sql($rowRule['id'], $i); foreach ($actionArray as $action) { $action_plus = $action; - $action_plus['due_status'] = $reminder_due; + $action_plus['due_status'] = 'past_due'; $action_plus['pid'] = $rowPatient['pid']; $action_plus['rule_id'] = $rowRule['id']; - $results = reminder_results_integrate($results, $action_plus); + $results = reminder_results_integrate($results, $action_plus, $mode); } } } @@ -2778,30 +2790,6 @@ function collect_database_label($label, $table) } /** - * Simple function to avoid processing of duplicate actions - * - * @param array $actions 2-dimensional array with all current active targets - * @param array $action array of selected target to test for duplicate - * @return boolean true if duplicate, false if not duplicate - */ -function is_duplicate_action($actions, $action) -{ - foreach ($actions as $row) { - if ( - $row['category'] == $action['category'] && - $row['item'] == $action['item'] && - $row['value'] == $action['value'] - ) { - // Is a duplicate - return true; - } - } - - // Not a duplicate - return false; -} - -/** * Calculate the reminder dates. * * This function returns an array that contains three elements (each element is a date). @@ -2886,40 +2874,62 @@ function calculate_reminder_dates($rule, string $dateTarget = null, $type = null } /** - * Adds an action into the reminder array + * Adds an action into the reminder/action array while dealing with duplicate actions. * * @param array $reminderOldArray Contains the current array of reminders * @param array $reminderNew Array of a new reminder - * @return array Reminders + * @param string $mode Options are 'reminders-due' or 'reminders-all' + * @return array Reminders */ -function reminder_results_integrate($reminderOldArray, $reminderNew) +function reminder_results_integrate($reminderOldArray, $reminderNew, $mode) { + $results = []; - $results = array(); - - // If reminderArray is empty, then insert new reminder + // If reminderArray is empty, then insert new reminder if (empty($reminderOldArray)) { $results[] = $reminderNew; return $results; } - // If duplicate reminder, then replace the old one - $duplicate = false; + $duplicateFlag = false; foreach ($reminderOldArray as $reminderOld) { - if ( - $reminderOld['pid'] == $reminderNew['pid'] && - $reminderOld['category'] == $reminderNew['category'] && - $reminderOld['item'] == $reminderNew['item'] - ) { - $results[] = $reminderNew; - $duplicate = true; + if ($mode == "reminders-all") { + // in reminders-all mode, show the status of duplicate actions of different rules + // (which user can hover over to see the rule), however do not show duplicate actions + // within the same rule (and show only the highest priority due status in this case). + $duplicate = $reminderOld['pid'] == $reminderNew['pid'] && + $reminderOld['category'] == $reminderNew['category'] && + $reminderOld['item'] == $reminderNew['item'] && + $reminderOld['rule_id'] == $reminderNew['rule_id']; } else { + // In the standard reminders-due mode, do not show any duplicate actions + // and show only the highest priority due status if duplicate actions. + $duplicate = $reminderOld['pid'] == $reminderNew['pid'] && + $reminderOld['category'] == $reminderNew['category'] && + $reminderOld['item'] == $reminderNew['item']; + } + + if ($duplicate) { + $duplicateFlag = true; + // The new action is a duplicate of an applicable old action (now need to figure out which action to keep). + // Only keep the action with the highest priority due status. + if (dueStatusCompare($reminderOld['due_status'], $reminderNew['due_status'])) { + // New action is higher priority (or same priority) than old action, so will remove old + // action and keep the new action. + $results[] = $reminderNew; + } else { + // Old action is higher priority than new action, so will keep the old action (and not keep + // the new action). + $results[] = $reminderOld; + } + } else { + // Not a duplicate, so will keep the old action. $results[] = $reminderOld; } } - // If a new reminder, then insert the new reminder - if (!$duplicate) { + if (!$duplicateFlag) { + // The new action was not a duplicate, so will keep it. $results[] = $reminderNew; } @@ -2927,6 +2937,31 @@ function reminder_results_integrate($reminderOldArray, $reminderNew) } /** + * Returns true if new due status is higher priority or the same priority + * (ie. high priority to lowest is past_due > due > soon_due > not_due) + * + * @param string $old (options are past_due, due, soon_due, not_due) + * @param string $new (options are past_due, due, soon_due, not_due) + * @return boolean + */ +function dueStatusCompare(string $old, string $new): bool +{ + $comparisonArray = ["not_due" => 1, "soon_due" => 2, "due" => 3, "past_due" => 4]; + + // return false if either $old or $new are not valid strings + if (!array_key_exists($old, $comparisonArray) || !array_key_exists($new, $comparisonArray)) { + return false; + } + + // return true if $new is higher priority or same priority as $old + if ($comparisonArray[$new] >= $comparisonArray[$old]) { + return true; + } else { + return false; + } +} + +/** * Compares number of items with requested comparison operator * * @param string $comp Comparison operator(eq,ne,gt,ge,lt,le) -- 2.11.4.GIT