...
OpenCDS Guvnor 5.4 and DSL Best Practices
...
Change Log. 3
Introduction. 4
I. Pot-Holes. 5
A. Known “pot-holes” in Guvnor 5.4. 5
B. Work-Arounds for the Pot-Holes. 7
II. Best Practices for Writing DSLs. 12
A. Plan the Logical Pieces of a Rule First. 12
B. Write and Test your DSLs. 13
C. Build the Rules using DSLs. 17
D. Export the Package as a KnowledgeModule for OpenCDS. 17
III. Common Patterns for OpenCDS Rules. 18
A. Events in General 18
B. Entities in General 19
C. Encounters. 20
D. AdverseEvents. 20
E. Observations. 21
F. Problems. 22
G. Procedures. 23
H. SubstanceAdministrations – Medications. 24
I. SubstanceAdministrations – Immunizations. 25
J. Supplies. 26
IV. Best Practices for Creating Responses from OpenCDS. 27
A. Use Templates for Output. 27
B. One Inference Wrapper per KnowledgeModule. 27
C. Include Both Coded Response and Text (if appropriate). 27
D. Include Proposals (if appropriate). 28
E. How to Create Return Messages from your Rules. 29
V. Common Patterns for Creating Responses. 30
A. Write me….. 30
Introduction
The objective of this document is threefold:
...
1. No Warning on Close without Save
still an issue in 5.4
All of the “Assets” that are part of a package can be opened for editing. They will all show a gray “X” on the right-hand side of the tab at the top of the screen when you hover your mouse over the tab. You need to think of this “X” as meaning “revert” or “abort,” because it will happily throw away all the work you have just done on the asset, with no warning.
2. Inconsistent Refresh of Elements on Screen
still an issue in 5.4, but improved
There are not as many cases where the screen does not match what has been saved, but there is still at least one case where this happens. If you rename an asset such as a rule, it will remain on the screen with the former name, even though the new name has actually been saved.
3. Possible to Add Invalid DSL to a Guided Rule
no longer an issue in 5.4
4. Possible to Permanently Damage a Guided Rule
no longer an issue in 5.4
5. Possible to put a Package into Inconsistent State
no longer an issue in 5.4
6. Possible to Archive Global Area
no longer an issue in 5.4
It is possible to archive the Global Area. If you did this in 5.3, it destroyed the Global Area, permanently. In 5.4, it merely archives the contents of the Global Area, and the contents can be retrieved using the Administration tools, if necessary.
7. Possible to Add an Asset to Multiple Packages
still an issue in 5.4
It is possible to create a new DSL or Enumeration (and possibly other types of assets) and have them become an unintended asset of more packages than the single package you specified when you created it. This sometimes happens when you have more than one package open as you create the element, and other times it happens for no apparent reason. For example, if you create a new Enumeration that you intend to be a part of a specific package, but you have the Global Area (and possibly other packages) open when you create the Enumeration, it will be added to all the other packages that are currently open.
8. Saving the Package and Signing Out
still an issue in 5.4 (was present in 5.3, but not documented)
When you save a package and “Sign Out”, Guvnor will happily throw away all of any unsaved work you have done on any open assets within that package, with no warning.
9. Some Assets from Global Area Randomly Appear or Disappear from Active Package
still an issue in 5.4 (was present in 5.3, but not documented)
This may be just a different view on Problem # 7. Although any DSL you have specifically copied from the Global Area to your current working package will remain an asset in that package, other DSLs from Global will randomly appear or disappear from the working package. While they are present, they can be used. If they don’t show at a particular point, they also cannot be added to a rule, and a build of the package will generate errors.
10. Using Techniques to “refresh” Lists or Views may Refresh Modified Assets Elsewhere
new issue in 5.4
They resolved most of the issues around disparities between what is “saved” and what shows in a list of assets, or even the contents of an asset. However, this has introduced a new problem that is apparently a side-effect of the “refresh” fixes. A modified asset that has not been saved may get “refreshed” from disk if you do anything that causes a package list of assets to be refreshed (such as clicking on a button named “refresh list”).
11. Enumerations Do Not Allow Comments
new issue in 5.4
In Guvnor 5.3 it was possible to imbed comments in Enumeration assets. In Guvnor 5.4, this will cause the enumerations to fail, except in one specific structure: You can add a block comment to the last line of the enumeration (e.g., /* comment… */ ). A comment in any other location will cause the enumeration to fail to be processed at all by Guvnor. Since OpenCDS has a tool to export enumerations which placed a comment at the beginning of the enumeration list, this will have to be removed, or your enumerations will not show up in DSLs.
...
The following approaches can help you avoid the pot-holes, or recover from falling into them. Of course, it is always better to avoid them in the first place… J
1. Develop Habit of Save before Changing to a Different Tab EVERY TIME
Before you click on another tab, use the File menu, and select “Save changes.” Some things you do in another tab can cause the screen to be refreshed from the last saved value, thereby losing all your updates in any open tabs.
2. Develop Habit of Save before Close EVERY TIME
Instead of using that “X” to close the asset, use the File menu, and select “Save and Close.” Developing that habit will avoid a lot of lost work.
...
NOTE: We maintain a text copy of all the import statements for vMR version 1.0 in the OpenCDS Maven project, as a text resource in the vMR internal module. In most cases, you can probably use that list without changes.
3. Develop Habit of Exporting the Guvnor Repository FREQUENTLY
Because it is possible to get Guvnor packages and rules into inconsistent or “locked” and unopenable states, it is critical to export the package to create backups. You will avoid a lot of grief if you make it a habit to always do an export frequently and especially before you do any of the following:
Rename anything
Remove a DSL or an Enumeration (no longer as big an issue as it was in 5.3, but still a good idea)
Import an updated Model
4. Techniques to Refresh Screen
There are three common techniques to refresh the screen. These were more useful in 5.3 than they are in 5.4, which is a lot better about refreshing the screen. Note that all of these techniques may refresh assets that have been changed, thereby losing any changes you have not saved. Save First!
Click the “Refresh List” button in the Assets view of the package. This will often make an asset that you just created appear in the list, or an asset that you just deleted/archived disappear from the list.
Click the icons which change the view of Knowledge Base Packages from nested to listed. This will close the group of package listings and reopen them, and tends to refresh all the lists in all packages.
When all else fails, and you know that you have something saved in the package, but it doesn’t appear appropriately (or the contents seem to be from the previous version), you can refresh everything, including the element lists that show when you are adding things to a Guided Rule, by logging out and logging back in.
NOTE: All of the above methods may refresh assets that are currently open (and possibly have changes!). Save all your changes first!
5. Avoid Damaging a Guided Rule
Guvnor 5.4 no longer appears to have a problem with “damaged rules,” but the following suggestions are still probably good ones…
...
TIP: Put a comment at the end of your DSLs which includes the name of the DSL, and references all of the Enumerations in the DSL. Then, in the event of a problem, looking at a Source | View Source on a Guided Rule which has this comment imbedded in the source code, will show where you have a bad DSL or a missing Enumeration. For Example:
Note that the two comments highlighted above (one at the end of the [when] clause, and the other at the end of the [then] clause) will show in the generated source code, and the macro replacement of the DSL will include the selected drop-down value of the Enumeration named “VMRTemplateConcept.determinationMethodCode”. If the displayed source simply shows X=={X}, then the enumeration was not selected, or there was no Enumeration list by that name.
TIP: You can look for Enumerations all in one place by opening the package and clicking on the Edit tab. Then select the URL for package source, and save the source to an editor such as Notepad++. You can then use the search facilities in the editor to look for the Enumeration names. If you followed the previous TIP about putting a comment in the DSL with the name of the DSL, you will know exactly which DSL is referencing the Enumeration.
6. Recovering from a Single Damaged Rule
This information no longer seems to be pertinent in Guvnor 5.4, but is kept here “just in case.”
...
Archive and Restore the package. Select the package, and select File | Archive. Go to Administration | Archives and delete the damaged rule. Restore the entire remaining package. Go to the package and recreate the rule, fixing the bad DSL or Enumeration before you save the new rule.
The Nuclear option: import a backup of the repository. Go to Administration : Import – Export, and import your most recent backup (Guvnor is nice enough to number them). NOTE: this will destroy all the work you have done since you exported the backup. You did backup frequently, didn’t you? J
7. Recovering from an Inconsistent Package
I haven’t seen this happen in Guvnor 5.4. But if it does, here are some things to try.
...
In the worst cases, your only choice is the nuclear option. This will lose all of the work you have done in all packages since you last made a useable backup. Which option you choose will partly depend on how recently you created a backup. Having a recent backup is always a good thing… J
8. Recovering from Archived Global Area
It was impossible to recover from an archived global area in Guvnor 5.3, but is relatively simple to do in Guvnor 5.4. Unlike Guvnor 5.3, Guvnor 5.4 archives the assets in the Global Area, but not the Global Area itself. Simply go to Administration | Archive, and restore the assets.
9. Recovering from Assets that Appear in Wrong Package
This will still sometimes happen in Guvnor 5.4, and if it does, here is what I found that sometimes would resolve it in Guvnor 5.3:
...
Another technique to fix the problem is to manually update the asset using a text editor or Eclipse. Access the source code file by connecting to Guvnor’s working version of the repository using webdav (http://localhost:8080/drools-guvnor/org.drools.guvnor.Guvnor/webdav/ -- substitute the hostname and port of your Guvnor installation, and add the package and filename that you need to mess with, e.g.: globalarea/AssertionDsl.dsl ).
10. Recovering from Unintended Refresh
Unsaved changes are gone. There is no recovery.
11. Recovering from Non-Functioning Enumerations
Remove all comments from the Enumeration file.
...
Best Practices for Writing DSLs
The following suggestions represent the approach that we have learned to use in writing Guided Rules using DSLs for Guvnor. Although we write most of our rules in Guvnor, the same general steps probably apply to writing technical rules, and developing rules in Eclipse. Please add (or let us know about) any further suggestions or comments that you come up with as you work with OpenCDS and Guvnor DSLs.
...
Plan the Logical Pieces of a Rule First
Use a whiteboard, or Notepad, or scratch paper to think about the different logical pieces that you need to complete the rule. It may be helpful to build a flowchart. If you plan to use Drools Flow / BPMN2 / Oryx for your rules, then you can use this tool to build the flowchart. Stay in this process until you can isolate elements that you need in the LHS of a rule, and elements that you need in the RHS of a rule. You should probably stay in this process until you have a first-cut idea of how many rules you need, and what each rule should do.
1. Think in Terms of Concepts, rather than Data
In all stages of this process you should be thinking in terms of “concepts” about the data, and not in terms of specific values or codes within the data. This means that you think about the disease, problem, medication as a concept, such as “diabetes mellitus, asthma, hypertension, ACE inhibitor, NSAID, specified age group, etc.
2. Develop the Structure to Map Concepts to Data
This process is described in a separate document named “Using OpenCDS Concepts.doc.” You will need to begin the process of creating concept instances before you start writing DSLs. These concept instances that you identify will have a name in an Enumeration that you will need to create before you can write your DSLs.
...
You will not normally need to worry about the actual mapping of data values to the concept instances until you are ready to deploy the rules in OpenCDS and do the final integration testing.
3. Create Enumerations in Guvnor
You will need to have defined and named “Concept instances” that are in your Enumerations before you start writing DSLs.
...
These enumerations are the “concept instances” against which you will write your DSLs and your rules.
...
Write and Test your DSLs
Before you start writing your final rules, develop re-useable Domain Specific Language (DSL) elements for as much of them as possible.
These DSLs should be written in terms of “concepts”, so that they are abstracting actual data and code values. For example, write your rules against a concept of “asthma”, instead of writing your rules against an ICD9 or SNOMED code that represents asthma. OpenCDS is designed to support the flexible mapping of codes at run-time to the logical concepts that you use to write the rules.
1. Start a DSL as a SandBox rule
A single named DSL can have zero to many [when] clauses (aka the left-hand-side or LHS of a rule), and zero to many [then] clauses (aka the right-hand-side or RHS of a rule). The purpose of the multiple elements is that each individual element is optional, and can be individually chosen or rejected when you build the rules. Until you are adept at creating DSLs, it is probably a good idea to keep your DSL simple, and avoid multiple elements.
...
Repeat this process in the sandbox rule until it both validates, and includes the logical element of a rule that you want to turn into a DSL. Here is an example of a sandbox rule with the logic that you might want to turn into a DSL:
Go to the Source | View Source tab, and view the text of the generated sandbox rule. This text is going to become your DSL:
Create a new DSL, and copy and paste the text from the [when] clause of the generated sandbox rule into the new DSL. Since there are two lines in the [when] clause, and Drools implies an “and” connection between separate lines in the LHS of a rule, we will need to insert an “and” between the two lines, and put them all on one line. We will also parenthesize the entire thing.
Our first effort might look like this (and it is all on one logical line):
($encTypeConcept : EncounterTypeConcept( openCdsConceptCode == "C44" ) and $encEvent : EncounterEvent( id == $encTypeConcept.conceptTargetId, subjectIsFocalPerson == true, encounterEventTime.high < evalTime ))
If we then clean up the variable names (the elements beginning with “$”) to make them globally unique in a long and complex rule with many DSLs, this text might look like this (and it is still all on one line):
($PatientEncounterEventDsl_encounterConcept_OutpatientEncounter : EncounterTypeConcept(openCdsConceptCode == "C44") and EncounterEvent(id == $PatientEncounterEventDsl_encounterConcept_OutpatientEncounter.conceptTargetId, subjectIsFocalPerson == true, encounterEventTime.getHigh()< $evalTime ))
This is now a working DSL, but it only knows how to do the one thing you hard-coded into it. You are going to do several more steps to turn this text into a very useful and re-useable DSL:
- Write Write a clear plain-language statement of what the DSL is supposed to describe, follow it by an equal-sign (“=”) and place it in front of the text you copied from the sandbox rule. This might be something like
Patient has previously had an outpatient encounter =
followed followed by the text of the rule that you wrote to implement this, as shown above.
...
Abstract out the concept instances into enumerations that you identify by {X} – any variable name surrounded by curly braces, and with some additional information that will be demonstrated in examples below.
Also abstract out any quantities you might want to change into variables that you identify by {n} – any variable name surrounded by curly braces.
...
Patient has previously had a {X:ENUM:EncounterTypeConcept.openCdsConceptCode} =
Replace the specific concept on the right-hand side of the = with the variable name[s] in curly braces, and you get this for the entire DSL:
Patient has previously had a {X:ENUM:EncounterTypeConcept.openCdsConceptCode} = ($PatientEncounterEventDsl_encounterConcept_{X} : EncounterTypeConcept(openCdsConceptCode == "{X}") and EncounterEvent(id == $PatientEncounterEventDsl_encounterConcept_{X}.conceptTargetId, subjectIsFocalPerson == true, encounterEventTime.getHigh()< $evalTime ))
Finally clean it up by adding a list name at the beginning, and a comment at the end.
The list name is the first thing you will see in the Guvnor list of elements that you can add to a Guided Rule, and helps you choose the right rule. It does not become a part of the final source code for the rule.
...
If we look at the Source | view Source, the source will look like this (and you can see the comment at the end of the DSL has been populated with the selected concept instance ID for “Outpatient encounter” that we selected in the drop-down):
2. Study the Sample Rules Provided
They include examples of more complicated rules that combine multiple concepts and settable values in useful and re-useable ways.
3. Test your Sandbox Rule with just One DSL in it
Make sure your sandbox rule validates, and save it.
...
Test each DSL you create in this same way before you start putting them in a rule you want to keep.
4. Use Meaningful Names for Variables
You may have noticed that the example rule we built above has variable names that can be quite long. The variable “$PatientEncounterEventDsl_encounterConcept_{X}” would work in a simple rule if it was only “$x1”.
...
Date | Author | Notes |
12-14-2011 | David Shields | Initial document without best practices section |
12-15-2011 | David Shields | Finished first draft of best practices section |
12-20-2011 | Ken Kawamoto | Minor edits |
12-20-2011 | David Shields | Added screen shots that were omitted earlier |
1-12-2012 | David Shields | Added one new “pot-hole” and work-around / fix |
6-28-2012 | David Shields | Edited for Drools Guvnor v5.4 |
7-5-2012 | David Shields | Added more “pot-holes”, worked on DSL Patterns |