Anchor | ||||
---|---|---|---|---|
|
Anchor | ||||
---|---|---|---|---|
|
OpenCDS Guvnor 5.4
Anchor | ||||
---|---|---|---|---|
|
Anchor | ||||
---|---|---|---|---|
|
Contents
Change Log
Introduction
I. Pot-Holes
A. Known "pot-holes" in Guvnor 5.4
B. Work-Arounds for the Pot-Holes
II. Best Practices for Writing DSLs
A. Plan the Logical Pieces of a Rule First
B. Write and Test your DSLs
C. Build the Rules using DSLs
D. Export the Package as a KnowledgeModule for OpenCDS
III. Common Patterns for OpenCDS Rules
A. Events in General
B. Entities in General
C. Encounters
D. AdverseEvents
E. Observations
F. Problems
G. Procedures
H. SubstanceAdministrations – Medications
I. SubstanceAdministrations – Immunizations
J. Supplies
IV. Best Practices for Creating Responses from OpenCDS
A. Use Templates for Output
B. One Inference Wrapper per KnowledgeModule
C. Include Both Coded Response and Text (if appropriate)
D. Include Proposals (if appropriate)
E. How to Create Return Messages from your Rules
V. Common Patterns for Creating Responses
A. Write me…
Anchor | ||||
---|---|---|---|---|
|
...
Many of the "pot-holes" that were present in Drools Guvnor v5.3 have been fixed in this version, but some of the fixes have introduced a new "gotcha." It is our hope that the pot-holes will eventually all be fixed in future versions of Guvnor, and that the "pot-hole" portion of this document will no longer be needed.
This document will also furnish some common patterns to accomplish certain tasks in building rules for OpenCDS in Guvnor.
Anchor | ||||
---|---|---|---|---|
|
...
Remove all comments from the Enumeration file.
Anchor | ||||
---|---|---|---|---|
|
...
You will need to have defined and named "Concept instances" that are in your Enumerations before you start writing DSLs.
We have found it useful to put most of our concept instances in a global enumerations list, kept in the Global Area. These are created by the OpenCDS tool named "GuvnorEnumerationCreator" and pasted into Guvnor Enumerations as plain text. Refer to the "Using OpenCDS Concepts.doc" and JBoss Drools documentation for guidance in this process.
These enumerations are the "concept instances" against which you will write your DSLs and your rules.
Anchor | ||||
---|---|---|---|---|
|
...
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.
The comment at the end of the DSL helps us identify the DSL when it is one among many DSLs contained in a single rule. We include the name of the DSL, and show the selected values for all the variables that are being substituted into the rule. This step is invaluable for debugging.
The final DSL named "PatientEncounterEventDsl" contains exactly the following (with no carriage returns in it anywhere – it is all on one logical line):
[when] Pt.Enc.Past - 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 )) //DslUsed==PatientEncounterEventDsl|||X=={X}
If we click the plus sign on our sandbox rule to add this DSL to the sandbox rule, the drop-down list that Guvnor gives us to select from shows the rule like this, which makes it clear exactly what rule we are selecting:
Once we add it to the sandbox rule, and select the option we want from the drop-down list, the rule will look like this:
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):
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.
Test your Sandbox Rule with just One DSL in it
...
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".
However, by combining the name of the DSL with the substituted concept, we have a variable name that can be referenced elsewhere in the rule containing the DSL, and it should be perfectly clear what the variable is referring to by its name. Also, using unique names ensure that duplicate variable names will not be declared within the same rule (which may include many DSLs), since such duplicate declarations will cause compile errors.
Anchor | ||||
---|---|---|---|---|
|
...
Remember that you can create a copy of a Test Scenario and modify or add to it to create another Test Scenario. You don't have to create each test from scratch. The goal is to achieve 100% testing coverage of all aspects of every rule.
Anchor | ||||
---|---|---|---|---|
|
You have two choices: You can either export the compiled binary package as a .pkg file, or you can export the source code for the package as a *.drl file. The *PKG file will run without a lag-time the first time it is called, but is completely opaque, will only run on the version of Drools that it was built in, and you can't modify it. The DRL file on the other hand can be modified with a text editor while it is sitting in a running OpenCDS Knowledge Repository, and that can be very useful for debugging.
Note that a PKG must be used when a Guvnor package includes processes in addition to rules. If processes are used, OpenCDS expects you to configure the setup file that knowledgeModules.xml with the name you assigned to the primary process.
Note also that when you use a PKG in OpenCDS, you must include the classPath of the correct version of the DroolsAdapter in OpenCDS as an attribute of the correct entry in the knowledgeModules.xml configuration file.
Anchor | ||||
---|---|---|---|---|
|
...
[when] Patient has previously had a {X:ENUM:<conceptType>.openCdsConceptCode} =
($x: <conceptType>(openCdsConceptCode == "{X}")
and <clinicalStatement>(id == $x.conceptTargetId, subjectIsFocalPerson == true, <clinicalStatementEventTime>.high <= $evalTime ))
Definitions of elements in the above pattern:
$x should be expanded to a meaningful name,
<conceptType> represents the name of an OpenCDS Concept Type, such as "EncounterTypeConcept"
<clinicalStatement> represents the name of an OpenCDS vMR clinical statement Type, such as "EncounterEvent"
<clinicalStatementEventTIme> represents the name of the relevant event time element in the particular clinical statement Type.
[Event] has happened at least {n} times
[Event] has happened in last {n} [timeUnits]
[Event] has happened at least {n1} times in last {n2} [timeUnits]
[Event] happened between {n1} and {n2} [timeUnits] ago
[Event] as tag {idTag}…
then uses all patterns shown above under Events in General
[Entity] has role {targetRole} to {clinicalStatementClass} identified as tag {idTag}
[Entity] has role {targetRole} to {clinicalStatementType} identified as tag {idTag} during {relationshipTimeInterval}…
[Event] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses all patterns shown above under Events in General
Anchor | ||||
---|---|---|---|---|
|
...
Only the substanceCode is strictly required, but some use cases may require some or all of the additional elements. More specific patterns will be developed as the need arises.
[Entity] [of type {entityType}]
[Facility] [of type {entityType}]
[Organization] [of type {entityType}]
[Person] [of type {entityType}]
[Specimen] [of type {entityType}]
[Entity] as tag {idTag}…
then uses all patterns shown above under Entities in General
[Entity] has role {targetRole} to {entityClass} identified as tag {idTag}
[Entity] has role {targetRole} to {entityClass} identified as tag {idTag} during {relationshipTimeInterval}…
Anchor | ||||
---|---|---|---|---|
|
...
then uses all patterns shown above under Events in General
[Encounter] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses all patterns shown above under Encounters and Events in General
Anchor | ||||
---|---|---|---|---|
|
...
then uses all patterns shown above under Events in General
[AdverseEvent] due to {adverseEventAgent} with {criticality} criticality…
then uses all patterns shown above under Events in General
[AdverseEvent] due to {adverseEventAgent} with {severity} severity…
then uses all patterns shown above under Events in General
[AdverseEvent] due to {adverseEventAgent} with {criticality} criticality and {severity} severity…
then uses all patterns shown above under Events in General
[AdverseEvent] as tag {idTag}…
then uses all patterns shown above under Adverse Events and Events in General
[AdverseEvent] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses all patterns shown above under Adverse Events and Events in General
Anchor | ||||
---|---|---|---|---|
|
[ObservationResult] has {observationValue} (where observationValue may be of any supported datatype)
[ObservationResult] is interpreted as {interpretation}
[ObservationResult] has {observationValue} interpreted as {interpretation}
[ObservationResult] has {observationValue} using specimen from {targetBodySite}
[ObservationResult] using specimen from {targetBodySite} is interpreted as {interpretation}
[ObservationResult] has {observationValue} using specimen from {targetBodySite} interpreted as {interpretation}
[ObservationResult] as tag {idTag}…
then uses all patterns shown above under Observations and Events in General
[ObservationResult] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses all patterns shown above under Observations and Events in General
Anchor | ||||
---|---|---|---|---|
|
[Problem] was present on admission to encounter {x}
[Problem] was present on admission as {priority} to encounter {x}
[Problem] was a discharge diagnosis for encounter {x}
[Problem] was {priority} discharge diagnosis for encounter {x}
[Problem] is currently {problemStatus} and {importance}
[Problem] was {problemStatus} at least {n} times
[Problem] was {problemStatus} in last {n} [timeUnits]
[Problem] was {problemStatus} at least {n1} times in last {n2} [timeUnits]
[Problem] was {problemStatus} between {n1} and {n2} [timeUnits] ago
[Problem] as tag {idTag}…
then uses all patterns shown above under Problems and Events in General
[Problem] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses all patterns shown above under Problems and Events in General
Anchor | ||||
---|---|---|---|---|
|
...
then uses all patterns shown above under Events in General
[Procedure] by way of {approachBodySite}…
then uses all patterns shown above under Events in General
[Procedure] on {targetBodySite} by way of {approachBodySite}…
then uses all patterns shown above under Events in General
[Procedure] as tag {idTag}…
then uses all patterns shown above under Procedures and Events in General
[Procedure] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses all patterns shown above under Procedures and Events in General
Anchor | ||||
---|---|---|---|---|
|
...
[Medication] {substanceCode} since {administrationTimeInterval} [reported {documentationTime} ][by {dataSourceType} ][attested by {informationAttestationType} ]
Medication order (prescription)
[Medication] {substanceCode} [strength {strength} ][form {form} ][dose {doseQuantity} ][route {deliveryRoute} ][frequency {dosingPeriod} [interval matters {dosingPeriodIntervalIsImportant} ]][sig {dosingSig} ][# fills {numberFillsAllowed} ][{criticality} ][ordered on {orderEventTime} ]
Medication administration (in clinic or hospital)
[Medication] {substanceCode} [type {doseType} ][strength {strength} ][form {form} ][dose {doseQuantity} ][route {deliveryRoute} ][site {targetBodySite} ][frequency {dosingPeriod} [interval matters {dosingPeriodIntervalIsImportant} ]] from {administrationTimeInterval.low} [thru {administrationTimeInterval.high} ]
Medication dispensation by clinic or pharmacy
[Medication] {substanceCode} [type {doseType} ] [strength {strength} ][form {form} ][dose {doseQuantity} ][route {deliveryRoute} ][frequency {dosingPeriod} [interval matters {dosingPeriodIntervalIsImportant} ]][ not to exceed {doseRestriction} ][days supply {daysSupply} ][# {dispensationQuantity} ][ fill# {fillNumber} ][fills left {fillsRemaining} ][dispensed {dispensationTime} ]
[Medication] …
then uses many patterns shown above under Events in General
[Medication] as tag {idTag}…
then uses many patterns shown above under Medications and Events in General
[Medication] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses many patterns shown above under Medications and Events in General
Anchor | ||||
---|---|---|---|---|
|
...
[Immunization] {substanceCode} [mfg {manufacturer} ][lot# {lotNo} ][dose# {doseNumber} ][route {deliveryRoute} ][site {targetBodySite} ][valid? {isValid} ] on {administrationTimeInterval.low}
Immunization administration
[Immunization] {substanceCode} [mfg {manufacturer} ][lot# {lotNo} ][dose# {doseNumber} ][route {deliveryRoute} ][site {targetBodySite} ] on {administrationTimeInterval.low}
Immunization forecast
[Immunization] {substanceCode} [dose# {doseNumber} ] not before {administrationTimeInterval.low} ideally on {administrationTimeInterval.high}
[Immunization]…
then uses many patterns shown above under Events in General
[Immunization] as tag {idTag}…
then uses many patterns shown above under Immunizations and Events in General
[Immunization] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses many patterns shown above under Immunizations and Events in General
Anchor | ||||
---|---|---|---|---|
|
...
then uses all patterns shown above under Events in General
[Supply] as tag {idTag}…
then uses all patterns shown above under Supplies and Events in General
[Supply] is {targetRelationshipToSource} of {clinicalStatementClass} identified as tag {idTag}…
then uses all patterns shown above under Supplies and Events in General
Anchor | ||||
---|---|---|---|---|
|
...
Check the OpenCDS website for output templates that are already defined, and select an appropriate one. Use the templateId(s) assigned to that template in your output response message. This furnishes information to external software to define how your output is structured.
If an appropriate output template does not already exist, create a definition of one or more response message(s) that will suit the needs of your KM(s), and ask OpenCDS to assign new templateId values. This will be done quickly, and will be posted with the OpenCDS documentation so that it can be used by others. Write your definition using the TemplateId Request Form available on the OpenCDS website.
Anchor | ||||
---|---|---|---|---|
|
...
Note that you can return both "typed" values and text values in an ObservationResult, and it is sometimes useful to include both a code, and explanatory text designed for humans (for validation and debugging purposes, even if the output is intended solely for machine processing). For example, you might return something like the "originalText" in the following examples to express the full contextual meaning of the code/codeSystem returned in an observationValue:
<observationValue>
<concept code="…" codeSystem="…" originalText="Patient is overdue for an Hba1C test."/>
</observationValue>
<observationValue>
<physicalQuantity value="3.14" units="ml"/>
</observationValue>
<interpretation code="…" codeSystem="…" originalText="Extremely high value, further treatment should be considered"/>
Anchor | ||||
---|---|---|---|---|
|
...
<Include an example of a response message that includes all of the above elements:>
Anchor | ||||
---|---|---|---|---|
|
...
See the sample DSLs whose names begin with "Output…" for examples.
Anchor | ||||
---|---|---|---|---|
|
...