Esempio n. 1
0
/**
 * Process clinic rules.
 *
 * Test the clinic rules of entire clinic and create a report or patient reminders (can also test
 * on one patient or patients of one provider). The structure of the returned results is dependent on the
 * $organize_mode and $mode parameters.
 * <pre>The results are dependent on the $organize_mode parameter settings
 *   'default' organize_mode:
 *     Returns a two-dimensional array of results organized by rules (dependent on the following $mode settings):
 *       'reminders-due' mode - returns an array of reminders (action array elements plus a 'pid' and 'due_status')
 *       'reminders-all' mode - returns an array of reminders (action array elements plus a 'pid' and 'due_status')
 *       'report' mode        - returns an array of rows for the Clinical Quality Measures (CQM) report
 *   'plans' organize_mode:
 *     Returns similar to default, but organizes by the active plans
 * </pre>
 *
 * @param  integer      $provider      id of a selected provider. If blank, then will test entire clinic. If 'collate_outer' or 'collate_inner', then will test each provider in entire clinic; outer will nest plans  inside collated providers, while inner will nest the providers inside the plans (note inner and outer are only different if organize_mode is set to plans).
 * @param  string       $type          rule filter (active_alert,passive_alert,cqm,amc,patient_reminder). If blank then will test all rules. 
 * @param  string/array $dateTarget    target date (format Y-m-d H:i:s). If blank then will test with current date as target. If an array, then is holding two dates ('dateBegin' and 'dateTarget').
 * @param  string       $mode          choose either 'report' or 'reminders-all' or 'reminders-due' (required)
 * @param  integer      $patient_id    pid of patient. If blank then will check all patients.
 * @param  string       $plan          test for specific plan only
 * @param  string       $organize_mode Way to organize the results (default, plans). See above for organization structure of the results.
 * @param  array        $options       can hold various option (for now, used to hold the manual number of labs for the AMC report)
 * @param  string       $pat_prov_rel  How to choose patients that are related to a chosen provider. 'primary' selects patients that the provider is set as primary provider. 'encounter' selectes patients that the provider has seen. This parameter is only applicable if the $provider parameter is set to a provider or collation setting.
 * @param  integer      $start         applicable patient to start at (when batching process)
 * @param  integer      $batchSize     number of patients to batch (when batching process)
 * @return array                       See above for organization structure of the results.
 */
function test_rules_clinic($provider = '', $type = '', $dateTarget = '', $mode = '', $patient_id = '', $plan = '', $organize_mode = 'default', $options = array(), $pat_prov_rel = 'primary', $start = NULL, $batchSize = NULL)
{
    // If dateTarget is an array, then organize them.
    if (is_array($dateTarget)) {
        $dateArray = $dateTarget;
        $dateTarget = $dateTarget['dateTarget'];
    }
    // Set date to current if not set
    $dateTarget = $dateTarget ? $dateTarget : date('Y-m-d H:i:s');
    // Prepare the results array
    $results = array();
    // If set the $provider to collate_outer (or collate_inner without plans organize mode),
    // then run through this function recursively and return results.
    if ($provider == "collate_outer" || $provider == "collate_inner" && $organize_mode != 'plans') {
        // First, collect an array of all providers
        $query = "SELECT id, lname, fname, npi, federaltaxid FROM users WHERE authorized = 1 ORDER BY lname, fname";
        $ures = sqlStatementCdrEngine($query);
        // Second, run through each provider recursively
        while ($urow = sqlFetchArray($ures)) {
            $newResults = test_rules_clinic($urow['id'], $type, $dateTarget, $mode, $patient_id, $plan, $organize_mode, $options, $pat_prov_rel, $start, $batchSize);
            if (!empty($newResults)) {
                $provider_item['is_provider'] = TRUE;
                $provider_item['prov_lname'] = $urow['lname'];
                $provider_item['prov_fname'] = $urow['fname'];
                $provider_item['npi'] = $urow['npi'];
                $provider_item['federaltaxid'] = $urow['federaltaxid'];
                array_push($results, $provider_item);
                $results = array_merge($results, $newResults);
            }
        }
        // done, so now can return results
        return $results;
    }
    // If set organize-mode to plans, then collects active plans and run through this
    // function recursively and return results.
    if ($organize_mode == "plans") {
        // First, collect active plans
        $plans_resolve = resolve_plans_sql($plan, $patient_id);
        // Second, run through function recursively
        foreach ($plans_resolve as $plan_item) {
            //  (if collate_inner, then nest a collation of providers within each plan)
            if ($provider == "collate_inner") {
                // First, collect an array of all providers
                $query = "SELECT id, lname, fname, npi, federaltaxid FROM users WHERE authorized = 1 ORDER BY lname, fname";
                $ures = sqlStatementCdrEngine($query);
                // Second, run through each provider recursively
                $provider_results = array();
                while ($urow = sqlFetchArray($ures)) {
                    $newResults = test_rules_clinic($urow['id'], $type, $dateTarget, $mode, $patient_id, $plan_item['id'], 'default', $options, $pat_prov_rel, $start, $batchSize);
                    if (!empty($newResults)) {
                        $provider_item['is_provider'] = TRUE;
                        $provider_item['prov_lname'] = $urow['lname'];
                        $provider_item['prov_fname'] = $urow['fname'];
                        $provider_item['npi'] = $urow['npi'];
                        $provider_item['federaltaxid'] = $urow['federaltaxid'];
                        array_push($provider_results, $provider_item);
                        $provider_results = array_merge($provider_results, $newResults);
                    }
                }
                if (!empty($provider_results)) {
                    $plan_item['is_plan'] = TRUE;
                    array_push($results, $plan_item);
                    $results = array_merge($results, $provider_results);
                }
            } else {
                // (not collate_inner, so do not nest providers within each plan)
                $newResults = test_rules_clinic($provider, $type, $dateTarget, $mode, $patient_id, $plan_item['id'], 'default', $options, $pat_prov_rel, $start, $batchSize);
                if (!empty($newResults)) {
                    $plan_item['is_plan'] = TRUE;
                    array_push($results, $plan_item);
                    $results = array_merge($results, $newResults);
                }
            }
        }
        // done, so now can return results
        return $results;
    }
    // Collect applicable patient pids
    $patientData = array();
    $patientData = buildPatientArray($patient_id, $provider, $pat_prov_rel, $start, $batchSize);
    // Go through each patient(s)
    //
    //  If in report mode, then tabulate for each rule:
    //    Total Patients
    //    Patients that pass the filter
    //    Patients that pass the target
    //  If in reminders mode, then create reminders for each rule:
    //    Reminder that action is due soon
    //    Reminder that action is due
    //    Reminder that action is post-due
    //Collect applicable rules
    // Note that due to a limitation in the this function, the patient_id is explicitly
    //  for grouping items when not being done in real-time or for official reporting.
    //  So for cases such as patient reminders on a clinic scale, the calling function
    //  will actually need rather than pass in a explicit patient_id for each patient in
    //  a separate call to this function.
    if ($mode != "report") {
        // Use per patient custom rules (if exist)
        // Note as discussed above, this only works for single patient instances.
        $rules = resolve_rules_sql($type, $patient_id, FALSE, $plan);
    } else {
        // $mode = "report"
        // Only use default rules (do not use patient custom rules)
        $rules = resolve_rules_sql($type, $patient_id, FALSE, $plan);
    }
    foreach ($rules as $rowRule) {
        // If using cqm or amc type, then use the hard-coded rules set.
        // Note these rules are only used in report mode.
        if ($rowRule['cqm_flag'] || $rowRule['amc_flag']) {
            require_once dirname(__FILE__) . "/classes/rulesets/ReportManager.php";
            $manager = new ReportManager();
            if ($rowRule['amc_flag']) {
                // Send array of dates ('dateBegin' and 'dateTarget')
                $tempResults = $manager->runReport($rowRule, $patientData, $dateArray, $options);
            } else {
                // Send target date
                $tempResults = $manager->runReport($rowRule, $patientData, $dateTarget);
            }
            if (!empty($tempResults)) {
                foreach ($tempResults as $tempResult) {
                    array_push($results, $tempResult);
                }
            }
            // Go on to the next rule
            continue;
        }
        // If in reminder mode then need to collect the measurement dates
        //  from rule_reminder table
        $target_dates = array();
        if ($mode != "report") {
            // Calculate the dates to check for
            if ($type == "patient_reminder") {
                $reminder_interval_type = "patient_reminder";
            } else {
                // $type == "passive_alert" or $type == "active_alert"
                $reminder_interval_type = "clinical_reminder";
            }
            $target_dates = calculate_reminder_dates($rowRule['id'], $dateTarget, $reminder_interval_type);
        } else {
            // $mode == "report"
            // Only use the target date in the report
            $target_dates[0] = $dateTarget;
        }
        //Reset the counters
        $total_patients = 0;
        $pass_filter = 0;
        $exclude_filter = 0;
        $pass_target = 0;
        // Find the number of target groups
        $targetGroups = returnTargetGroups($rowRule['id']);
        if (count($targetGroups) == 1 || $mode == "report") {
            //skip this section if not report and more than one target group
            foreach ($patientData as $rowPatient) {
                // Count the total patients
                $total_patients++;
                $dateCounter = 1;
                // for reminder mode to keep track of which date checking
                foreach ($target_dates as $dateFocus) {
                    //Skip if date is set to SKIP
                    if ($dateFocus == "SKIP") {
                        $dateCounter++;
                        continue;
                    }
                    //Set date counter and reminder token (applicable for reminders only)
                    if ($dateCounter == 1) {
                        $reminder_due = "soon_due";
                    } else {
                        if ($dateCounter == 2) {
                            $reminder_due = "due";
                        } else {
                            // $dateCounter == 3
                            $reminder_due = "past_due";
                        }
                    }
                    // First, deal with deceased patients
                    //  (for now will simply not pass the filter, but can add a database item
                    //   if ever want to create rules for dead people)
                    // Could also place this function at the total_patients level if wanted.
                    //  (But then would lose the option of making rules for dead people)
                    // Note using the dateTarget rather than dateFocus
                    if (is_patient_deceased($rowPatient['pid'], $dateTarget)) {
                        continue;
                    }
                    // Check if pass filter
                    $passFilter = test_filter($rowPatient['pid'], $rowRule['id'], $dateFocus);
                    if ($passFilter === "EXCLUDED") {
                        // increment EXCLUDED and pass_filter counters
                        //  and set as FALSE for reminder functionality.
                        $pass_filter++;
                        $exclude_filter++;
                        $passFilter = FALSE;
                    }
                    if ($passFilter) {
                        // increment pass filter counter
                        $pass_filter++;
                    } else {
                        $dateCounter++;
                        continue;
                    }
                    // Check if pass target
                    $passTarget = test_targets($rowPatient['pid'], $rowRule['id'], '', $dateFocus);
                    if ($passTarget) {
                        // increment pass target counter
                        $pass_target++;
                        // send to reminder results
                        if ($mode == "reminders-all") {
                            // place the completed actions into the reminder return array
                            $actionArray = resolve_action_sql($rowRule['id'], '1');
                            foreach ($actionArray as $action) {
                                $action_plus = $action;
                                $action_plus['due_status'] = "not_due";
                                $action_plus['pid'] = $rowPatient['pid'];
                                $results = reminder_results_integrate($results, $action_plus);
                            }
                        }
                        break;
                    } else {
                        // send to reminder results
                        if ($mode != "report") {
                            // place the uncompleted actions into the reminder return array
                            $actionArray = resolve_action_sql($rowRule['id'], '1');
                            foreach ($actionArray as $action) {
                                $action_plus = $action;
                                $action_plus['due_status'] = $reminder_due;
                                $action_plus['pid'] = $rowPatient['pid'];
                                $results = reminder_results_integrate($results, $action_plus);
                            }
                        }
                    }
                    $dateCounter++;
                }
            }
        }
        // Calculate and save the data for the rule
        $percentage = calculate_percentage($pass_filter, $exclude_filter, $pass_target);
        if ($mode == "report") {
            $newRow = array('is_main' => TRUE, 'total_patients' => $total_patients, 'excluded' => $exclude_filter, 'pass_filter' => $pass_filter, 'pass_target' => $pass_target, 'percentage' => $percentage);
            $newRow = array_merge($newRow, $rowRule);
            array_push($results, $newRow);
        }
        // Now run through the target groups if more than one
        if (count($targetGroups) > 1) {
            foreach ($targetGroups as $i) {
                //Reset the target counter
                $pass_target = 0;
                foreach ($patientData as $rowPatient) {
                    $dateCounter = 1;
                    // for reminder mode to keep track of which date checking
                    foreach ($target_dates as $dateFocus) {
                        //Skip if date is set to SKIP
                        if ($dateFocus == "SKIP") {
                            $dateCounter++;
                            continue;
                        }
                        //Set date counter and reminder token (applicable for reminders only)
                        if ($dateCounter == 1) {
                            $reminder_due = "soon_due";
                        } else {
                            if ($dateCounter == 2) {
                                $reminder_due = "due";
                            } else {
                                // $dateCounter == 3
                                $reminder_due = "past_due";
                            }
                        }
                        // First, deal with deceased patients
                        //  (for now will simply not pass the filter, but can add a database item
                        //   if ever want to create rules for dead people)
                        // Could also place this function at the total_patients level if wanted.
                        //  (But then would lose the option of making rules for dead people)
                        // Note using the dateTarget rather than dateFocus
                        if (is_patient_deceased($rowPatient['pid'], $dateTarget)) {
                            continue;
                        }
                        // Check if pass filter
                        $passFilter = test_filter($rowPatient['pid'], $rowRule['id'], $dateFocus);
                        if ($passFilter === "EXCLUDED") {
                            $passFilter = FALSE;
                        }
                        if (!$passFilter) {
                            // increment pass filter counter
                            $dateCounter++;
                            continue;
                        }
                        //Check if pass target
                        $passTarget = test_targets($rowPatient['pid'], $rowRule['id'], $i, $dateFocus);
                        if ($passTarget) {
                            // increment pass target counter
                            $pass_target++;
                            // send to reminder results
                            if ($mode == "reminders-all") {
                                // place the completed actions into the reminder return array
                                $actionArray = resolve_action_sql($rowRule['id'], $i);
                                foreach ($actionArray as $action) {
                                    $action_plus = $action;
                                    $action_plus['due_status'] = "not_due";
                                    $action_plus['pid'] = $rowPatient['pid'];
                                    $results = reminder_results_integrate($results, $action_plus);
                                }
                            }
                            break;
                        } else {
                            // send to reminder results
                            if ($mode != "report") {
                                // place the actions into the reminder return array
                                $actionArray = resolve_action_sql($rowRule['id'], $i);
                                foreach ($actionArray as $action) {
                                    $action_plus = $action;
                                    $action_plus['due_status'] = $reminder_due;
                                    $action_plus['pid'] = $rowPatient['pid'];
                                    $results = reminder_results_integrate($results, $action_plus);
                                }
                            }
                        }
                        $dateCounter++;
                    }
                }
                // Calculate and save the data for the rule
                $percentage = calculate_percentage($pass_filter, $exclude_filter, $pass_target);
                // Collect action for title (just use the first one, if more than one)
                $actionArray = resolve_action_sql($rowRule['id'], $i);
                $action = $actionArray[0];
                if ($mode == "report") {
                    $newRow = array('is_sub' => TRUE, 'action_category' => $action['category'], 'action_item' => $action['item'], 'total_patients' => '', 'excluded' => '', 'pass_filter' => '', 'pass_target' => $pass_target, 'percentage' => $percentage);
                    array_push($results, $newRow);
                }
            }
        }
    }
    // Return the data
    return $results;
}
Esempio n. 2
0
/**
 * Function to update reminders.
 *
 * Function that updates reminders and returns an array with a specific data structure.
 * <pre>The data structure of the return array includes the following elements
 *  'total_active_actions'         - Number of active actions.
 *  'total_pre_active_reminders'   - Number of active reminders before processing.
 *  'total_pre_unsent_reminders'   - Number of unsent reminders before processing.
 *  'total_post_active_reminders'  - Number of active reminders after processing.
 *  'total_post_unsent_reminders'  - Number of unsent reminders after processing.
 *  'number_new_reminders'         - Number of new reminders
 *  'number_updated_reminders'     - Number of updated reminders (due_status change)
 *  'number_inactivated_reminders' - Number of inactivated reminders.
 *  'number_unchanged_reminders'   - Number of unchanged reminders.
 * </pre>
 *
 * @param  string   $dateTarget  target date (format Y-m-d H:i:s). If blank then will test with current date as target.
 * @param  integer  $patient_id  pid of patient. If blank then will check all patients.
 * @param  integer  $start       applicable patient to start at (when batching process)
 * @param  integer  $batchSize   number of patients to batch (when batching process)
 * @return array                 see above for data structure of returned array
 */
function update_reminders($dateTarget = '', $patient_id = '', $start = NULL, $batchSize = NULL)
{
    $logging = array();
    // Set date to current if not set
    $dateTarget = $dateTarget ? $dateTarget : date('Y-m-d H:i:s');
    // Collect reminders (note that this function removes redundant and keeps the most distant
    //   reminder (ie. prefers 'past_due' over 'due' over 'soon_due')
    // Note that due to a limitation in the test_rules_clinic function, the patient_id is explicitly
    //  needed to work correctly. So rather than pass in a '' patient_id to do the entire clinic,
    //  we instead need to pass in each patient_id separately.
    $collectedReminders = array();
    $patient_id_complete = "";
    if (!empty($patient_id)) {
        // only one patient id, so run the function
        $collectedReminders = test_rules_clinic('', 'patient_reminder', $dateTarget, 'reminders-due', $patient_id);
        $patient_id_complete = $patient_id;
    } else {
        // as described above, need to pass in each patient_id
        // Collect all patient ids
        $patientData = buildPatientArray('', '', '', $start, $batchSize);
        for ($iter = 0; $row = sqlFetchArray($rez); $iter++) {
            $patientData[$iter] = $row;
        }
        $first_flag = TRUE;
        foreach ($patientData as $patient) {
            // collect reminders
            $tempCollectReminders = test_rules_clinic('', 'patient_reminder', $dateTarget, 'reminders-due', $patient['pid']);
            $collectedReminders = array_merge($collectedReminders, $tempCollectReminders);
            // build the $patient_id_complete variable
            if ($first_flag) {
                $patient_id_complete .= $patient['pid'];
                $first_flag = FALSE;
            } else {
                $patient_id_complete .= "," . $patient['pid'];
            }
        }
    }
    $logging['total_active_actions'] = count($collectedReminders);
    // For logging purposes only:
    //  Collect number active of active and unsent reminders
    $logging['total_pre_active_reminders'] = count(fetch_reminders($patient_id_complete));
    $logging['total_pre_unsent_reminders'] = count(fetch_reminders($patient_id_complete, 'unsent'));
    // Migrate reminders into the patient_reminders table
    $logging['number_new_reminders'] = 0;
    $logging['number_updated_reminders'] = 0;
    $logging['number_unchanged_reminders'] = 0;
    foreach ($collectedReminders as $reminder) {
        // See if a reminder already exist
        $sql = "SELECT `id`, `pid`, `due_status`, `category`, `item` FROM `patient_reminders` WHERE " . "`active`='1' AND `pid`=? AND `category`=? AND `item`=?";
        $result = sqlQueryCdrEngine($sql, array($reminder['pid'], $reminder['category'], $reminder['item']));
        if (empty($result)) {
            // It does not yet exist, so add a new reminder
            $sql = "INSERT INTO `patient_reminders` (`pid`, `due_status`, `category`, `item`, `date_created`) " . "VALUES (?, ?, ?, ?, NOW())";
            sqlStatementCdrEngine($sql, array($reminder['pid'], $reminder['due_status'], $reminder['category'], $reminder['item']));
            $logging['number_new_reminders']++;
        } else {
            // It already exist (see if if needs to be updated via adding a new reminder)
            if ($reminder['due_status'] == $result['due_status']) {
                // No change in due status, so no need to update
                $logging['number_unchanged_reminders']++;
                continue;
            } else {
                // Change in due status, so inactivate current reminder and create a new one
                // First, inactivate the previous reminder
                $sql = "UPDATE `patient_reminders` SET `active` = '0', `reason_inactivated` = 'due_status_update', " . "`date_inactivated` = NOW() WHERE `id`=?";
                sqlStatementCdrEngine($sql, array($result['id']));
                // Then, add the new reminder
                $sql = "INSERT INTO `patient_reminders` (`pid`, `due_status`, `category`, `item`, `date_created`) " . "VALUES (?, ?, ?, ?, NOW())";
                sqlStatementCdrEngine($sql, array($reminder['pid'], $reminder['due_status'], $reminder['category'], $reminder['item']));
            }
        }
    }
    // Inactivate reminders that no longer exist
    // Go through each active reminder and ensure it is in the current list
    $sqlReminders = fetch_reminders($patient_id_complete);
    $logging['number_inactivated_reminders'] = 0;
    foreach ($sqlReminders as $row) {
        $inactivateFlag = true;
        foreach ($collectedReminders as $reminder) {
            if ($row['pid'] == $reminder['pid'] && $row['category'] == $reminder['category'] && $row['item'] == $reminder['item'] && $row['due_status'] == $reminder['due_status']) {
                // The sql reminder has been confirmed, so do not inactivate it
                $inactivateFlag = false;
                break;
            }
        }
        if ($inactivateFlag) {
            // The sql reminder was not confirmed, so inactivate it
            $sql = "UPDATE `patient_reminders` SET `active` = '0', `reason_inactivated` = 'auto', " . "`date_inactivated` = NOW() WHERE `id`=?";
            sqlStatementCdrEngine($sql, array($row['id']));
            $logging['number_inactivated_reminders']++;
        }
    }
    // For logging purposes only:
    //  Collect number of active and unsent reminders
    $logging['total_post_active_reminders'] = count(fetch_reminders($patient_id_complete));
    $logging['total_post_unsent_reminders'] = count(fetch_reminders($patient_id_complete, 'unsent'));
    return $logging;
}