Clinical Decision Rules

From OpenEMR Project Wiki

MU Requirements

NEEDS TO BE UPDATED WITH CURRENT REQUIREMENTS.

See Patient Reminders for specifications.

Summary for the Clinical Decision Rule Engine

Engine by Brady Miller, summary by Aron Racho

In clinical_rules.php, function test_rules_clinic() collects either all a provider's patients, a specific patient, or all the patients in the clinic, then iterates through each rule (from table clinical_rules) for each patients, checking whether or not the rule applies to that patient.

The way it does that is that it uses the function test_targets() is what retrieves the data in openemr tables/columns relevant to the rule being tested and the patient. It ends up checking table rule_target.value for the expression relating to the openemr table. For example:

   ::immunizations::immunization_id::eq::20::ge::1

This string appears to describe that the particular rule must look into the immunizations table for the given patient to see whether or not the rule applies.

test_rules_clinic() is used in places such as the patient reminders widget and the cqm report. The patient reminders widget (from the demographics page) uses test_rules_clinic() to determine, for the currently selected patient, which rules apply (eg. which patient needs a pap smear or new blood test today), and and persists reminders relating to these rules in table patient_reminders if they are not already persisted.

Similarly, cqm.php (the CQM report, available from the Admin -> Reports nav) uses test_rules_clinic to report on patients who need actions (eg. have triggered a rule) across the clinic.

In summary: studying the flow from update_reminders.php (which is called by the patient reminders widget) to test_rules_clinic will show you how reminders are persisted for patients. The CQM report (cqm.php) and the patient reminders widget will show you how persisted rule 'hits' per patient are consumed.

My feeling is that we will work with the messaging subsystem to surface something like what the CQM report is already doing. We could surface messages relating to the physician's particular patients.

Project Overview

Project 1:

  • CDR Activation Manager - Functional Spec for GUI w/ screen mock ups: (ownership: MI2) - Target Date: 12-29 Done
  • Code Dev and QA: (ownership: EnSofttek/MI2)

Project 2:

Project 3:

Project 4: Build a fully functional admin gui that allows making/editing new rules, filters, targets, actions. This will be a huge project and suggest doing it last.

  • Research and document specifics on how the list_options items are used by the CDR Engine: (ownership: EnSofttek/MI2) Target: 12/31
  • Rules Engine Admin GUI Functional Spec w/ screen mock ups: (ownership: MI2) - Target Date: 01/09/2011

Project 5: (ownership: none) Get the email and voice (maviq) reminder code to work. I've refactored all of Thomas's code (except for the maviq api, which is in its original state), so at this point just need to get the email and maviq voice connectors to work. The nice thing is that this is a really isolated project. The script that does this is interface/batchcom/batch_reminders.php, which can be run in OpenMR via Adminsitration->Patient Reminders (click Send Reminders Batch button). I'm pretty clueless on how to get the maviq voice feature to work. For the email, looks like need to place some required from email fields as new settings in interface/globals.inc.php ($sender_name, $email_address, etc.). Teh really nice thing about this project is that the reminder sql table and data (self-populated whenever a new patient is made) already exist; very self-explanatory if look at the current scripting in interface/batchcom/batch_reminders.php and the patient_reminders sql table.

Project 6: (ownership: none) Get the CQM report to output the file that needs to be submitted (some XML thing) and ensure we get all items for MU. The CQM report is at Reports->Clinical->Quality Measures. Simply need to put a button on this page that will export the data to an xml (following mu standards). Then can also research and figure out what exactly needs to go into that report which is missing (such as number exlusions etc.).

Project 7: (ownership: bradymiller) Get the Automated Measure Calculations functional. The skeleton rules and report are entered in, but not functional. Will likely need to incorporate a date range (rather than a target range) for this feature. Then will simply be a matter of working out the rules.

Project 8: (ownership: bradymiller) Get the Clinical Quality Measures functional. The skeleton rules and report are entered in, but not functional. These are all rather complicated rule sets for each quality measure, so will likely require some customized functions (called by rule engine) to deal with some of these (to avoid overburdening the core rules engines with a bunch of confusing implementations).

Distributed Development Guideline

[Brady] will maintain the official github branch for this code (I know you guys are functioning as the project managers of this module, but better to let me manage the repo, since it's now easy for me, and can then avoid wasting your billable hours on this) at http://github.com/bradymiller/openemr/commits/rules_develop. As the 'master' and 'rel-320' are, this branch should also be treated as a sacred branch (ie. you should do all development in branches derived from this and only update it by pulling the branch from my repository).

So, to set up this branch in your local repo do following:

   git remote add brady git://github.com/bradymiller/openemr.git
   git fetch brady
   git checkout brady/rules_develop (you get a long sort of warning message)
   git checkout -b rules_develop (you now have a local rules_develop branch)
   git push github rules_develop (you now have a published rules_develop branch; not needed but helps if have any problems in the future)

Keep your local rules_develop updated by frequently pulling from the official rules_develop branch on my repo:

   git checkout rules_develop
   git pull brady rules_develop

Treat the official rules_develop branch and your local rules_develop branch as sacred branches(like the 'master' and 'rel-320' branches). So, develop CDR module code in branches derived from your local rules_develop branch, but don't ever add to or modify your local rules_develop branch (ie. as above, only update your local rules_develop branch from the official branch). When ready to add to the official rules_develop branch then publish it on github in another branch and ask me to commit it to the official rules_develop branch on my repo. Then you'll pull your code into your local repo by updating from the official repo. I hope this makes sense. Following this will avoid any merging/conflict issues of developers collaborating on the CDR module (and will keep the history relatively clean for when we're ready to merge this code into the sourceforge codebase). Also, this will allow us to easily keep the code updated (merge in 'master') when needed in a controlled manner (ie. I'll merge in master into the official rules_develop branch, and then you'll get the merge by updating your local branch from this).

Notes and Issues

Please add your issues, comments and suggestions and rember to use the 'signature' tag to sign and date the entry.

1. Permissions levels for each part of the process need to be clearly defined --Tony - www.mi-squared.com 00:26, 30 December 2010 (UTC)
Agreed, need to ensure basic acl's are set up (for now, only people that are allowed to see clinical stuff on patient should be able to see these widgets in the patient sumamry screen. --Bradymiller 19:12, 31 December 2010 (UTC)
2. Patient Reminders work list may be better in Miscellaneous than administration. It is similar in function to 'BatchCom' for patient communications. --Tony - www.mi-squared.com 00:26, 30 December 2010 (UTC)
After all the pieces and links are in place, then would start thinking about the best place to put them. --Bradymiller 19:12, 31 December 2010 (UTC)
3. On patient summary/demographic.php the patient reminders and clinical reminders widgets contain the nearly same information in different sort orders. Is in necessary to have two widgets for reminders or would we be better with one that indicates the difference between the two or combines information? --Tony - www.mi-squared.com 00:44, 30 December 2010 (UTC)
Yes, it is necessary, because they do different things. The clinical reminders widget is for real time indicators (and activated by the active_alert_flag in clinical_rules mysql table) (very high utility for a clinical encounter, which is why at the top right). The patient reminders widget is for sending/tracking reminders to the patient (and activated by the patient_reminder_flag in clinical_rules mysql table). The same clinical_rules entry can be activated to do both (which is now set for most during development) but in real life, many of the rules will be set for only as a real time indicator or as a patient reminder; and these mechanisms will be even more powerful when the per patient customization gui's are set up. In the end, the clinical reminders widget (real time indicators) edit/list button screen will also be the perfect place to put plan (ie. diabetes, HTN, ..) stuff (but don't want to get to far ahead of ourselves). --Bradymiller 19:12, 31 December 2010 (UTC)
4. What is the difference between a rule target and a rule filter? As far as I can tell, they are both involved in determining whether or not a rule applies to a patient; it is not clear to me from the code. Please help us understand the difference, as I believe the admin gui utility for configuring the rules engine may have to mess with these.--Aron Racho 22:58, 3 January 2011 (UTC)
The flow of the rule engine is as follows. Lets say were looking at patient 'Bill'. We look at the first rule 'rule_A'. In 'rule_A' we first grab the associated filter set (note only one filter set per rule, for now) and test the filter set to patient 'Bill'. If Bill passes the filter set (he is now part of the group being tested), he then moves on to the next test (the target). So, we now grab the associated target set(s) for the rule 'rule_A' (note there is a mechanism for multiple target sets per rule), and test them to the patient 'Bill'. If 'Bill' does not pass the target set, then the action (associated with failed target set from 'rule_A') happens (or in the case of cqm, it is used in the calculation). To get further differentiation of filters vs targets, check out the functions test_filters() and test_targets(); note how many more features are required to be dealt with in test_filters() than test_targets().--Bradymiller 08:08, 4 January 2011 (UTC)
  • The flow is not the actual question: What is a Target? and What is a Filter?. How do they differ to the USER? We are trying to create a UI that users can understand and configure, so we need clear definition of the terms themselves and how they are applied. The flow in the code itself is fairly obvious, and not the issue at hand. --Tony - www.mi-squared.com 18:40, 5 January 2011 (UTC)
  • The filter is to select the population that you want to run the test (ie. check for the target) on. For example, lets use a pap smear. The filter would be for women only over a certain age. The target is the pap smear itself. This is important, because the filter defines the population of patients that are pertinent to the rule (this is used as denominator in cqm rules and in the future to auto-report results in the clinical reminder widget even if the target is true (ie. show the hgba1c result even if it is up to date)). I hope this helps.--Bradymiller 18:55, 5 January 2011 (UTC)
5. We are proposing that the rule admin GUI will allow you to define, per rule, where the source of the filtering criteria. For example, rule_dm_bp_control_cqm wants to source its targets from form_vitals, bps and bpd, etc. columns. We want to present an human language 'nice title' for things like form_vitals (which really can be any developer form table, or other source such as CUSTOM:act_cat_assess). There doesn't appear to be a linkage from the rules_target or rules_filter table to any such table that could provide this friendlier name. Any suggestions on how we surface this friendlier name?--Aron Racho 00:49, 4 January 2011 (UTC)
Several things here. First, the actions categories (ie. act_cat_assess) do have list_options lists; for example act_act_assess is Assessment. And CUSTOM basically means the data is stored in the 'rule_patient_data' table, which in actuality is simply a garbage bin to hold data points that do not yet exist within the OpenEMR; as procedures and other openemr code is developed more for MU, less and less things will be stored in this table; nonetheless it will still always be an important mechanism that will add another nice option for customization. For example, lets say I want to track yearly fingernail length in some weirdo clinic; they could simply create the rule in the clinical rule admin GUI, use the CUSTOM method, and not need to worry about creating a sql data container or form for this information. On to the more pressing question; how to create user friendly constants (and translations) for stuff such as bps form_vitals etc. This is actual gonna be quite simple, since the actual number of terms that are used is not that big, and can be added to as more places in the database are used. Basically a simple list_options list(s) will suffice and can be done as building the admin gui.--Bradymiller 08:22, 4 January 2011 (UTC)
6. I'm trying to understand what the purpose is of group_id in rule_target. From inspecting the code, I gathered that you were using group_id to pick up rule_target(s) associated with a given rule (from clinical_rules) to test against a given patient. I interpreted this to mean that any given rule has multiple targets. Is this correct?
From brady: This is done when a rule has more than one action associated with it. So, in the rule_wt_assess_couns_child, there are two separate actions (Weight and Education;so will create two separate entries in the clinical widget and two separate rows in the CQM report). However the rule_htn_bp_measure is only linked to one action (ie. Blood Pressure). As an aside, in the future there will be a potential group_id instituted in the rule_filter table to allow separate filters per rule (since may be needed for some of the more complicated CQM rules).
7. How do we handle foreign key relationships such as the one in:
    ::immunizations::immunization_id::eq::30::ge::1
The column immunization_id is actually a foreign key to a list options entry. The value must be looked up somewhere else. Can we make the assumption that if the column is *_id (eg ends in _id), we can try to look up the appropriate value in the list_options table? Because obviously we can't simply indicate on the gui that 'immunization_id' must be 30! It should be 'Influenza 1'.
From brady: This is gonna require table/method specific operations (note many of these types of mechanisms are built into the rules engine already; such as procedures, where to get pid, date etc.). While building the gui, I'd rec. just focusing on the raw data, and can then further customize mechanism as needed as the examples require. Note, this was basically the strategy in building the rules engine (ie. started with simple rules and the engine then underwent changes, sometimes drastic, as more rules sets were added). My guess is that you'll rely on raw data for most (such as form_vitals stuff), and then will catch and put other things, such as immunizations through a different if/case statement (could even put into a function to further abstract it like the interval,date,patient_id functions). Once you have the raw mechanism in place, these further customizations shouldn't be that difficult (ie. icing on the cake).

Effected Code, Tables, etc

In addition to meeting the MU requirements here, we also take into consideration of the CMS Quality Reporting MU requirements.

Owner and Status

Consortium of: Brady Miller, Medical Information Integration, LLC and EnSoftek, LLC Brady has been working on the low level design MI2 and Ensoftek launch collaborative development starting 12/11/2010.

  • Original design and work done as of 2/4/2010: was rejected as not robust enough

Links