HL7 FHIR® Implementation Guide: Electronic Case Reporting (eCR) - US Realm, published by HL7 International / Public Health. This guide is not an authorized publication; it is the continuous build for version 2.1.2 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/HL7/case-reporting/ and changes regularly. See the Directory of published versions
This document details the generation of CQL rule filters
Determining reportability for a particular event is a complex process involving multiple considerations. To support the possibility of additional participation in that process by sites producing data, condition-specific filter criteria, including condition-specific value sets, as well as jurisdiction-level configuration information can be made available as part of the eRSD specification.
The general structure of the reportability decision is organized by condition. For each condition, a set of criteria are defined that characterize evidence involved in the decision. These criteria reference data from various sources, including laboratories, clinics and hospitals (provider facilities), and vital records. The condition-specific criteria are expressed in terms of various categories of data, including demographics, diagnoses and problems, laboratory orders and results, medication and immunization information, and vital statistics. These criteria are then combined in different ways to produce rules. In this way, different jurisdictions can have different rules about what constitutes a reportable event, by specifying different combinations of condition-specific criteria. Reporting sites can then participate in the reportability decision by considering jurisdiction configuration for the jurisdiction of residence and the jurisdiction of care in which the event occurred.
Condition-specific criteria are expressed in terms of data elements and characterized using standard terminologies. These condition-specific criteria terminologies are collected into value sets in the following six categories:
valueset "Example Diagnosis/Problem Triggers": 'http://hl7.org/fhir/us/ecr/ValueSet/valueset-dxtc-example'
valueset "Example Lab Order Test Name Triggers": 'http://hl7.org/fhir/us/ecr/ValueSet/valueset-lotc-example'
valueset "Example Lab Observation Test Name Triggers": 'http://hl7.org/fhir/us/ecr/ValueSet/valueset-lrtc-example'
valueset "Example Medications Triggers": 'http://hl7.org/fhir/us/ecr/ValueSet/valueset-mrtc-example'
valueset "Example Organism Substance Triggers": "http://hl7.org/fhir/us/ecr/ValueSet/valueset-ostc-example"
valueset "Example Suspected Disorder Triggers": "http://hl7.org/fhir/us/ecr/ValueSet/valueset-sdtc-example"
These categories can be expressed within CQL as:
define "Condition Diagnosis/Problem Data":
[Condition: code in "Example Diagnosis/Problem Triggers"] DX
where DX.clinicalStatus in { 'active', 'recurrence', 'relapse' }
and DX.verificationStatus in { 'unconfirmed', 'provisional', 'differential', 'confirmed' }
define "Encounter Diagnosis/Problem Data":
[Encounter: reasonCode in "Example Diagnosis/Problem Triggers"] DX
where DX.status in { 'arrived', 'triaged', 'in-progress', 'onleave', 'finished' }
define "Diagnosis/Problem Data":
"Condition Diagnosis/Problem Data"
union "Encounter Diagnosis/Problem Data"
define "Immunizations Data":
[Immunization: vaccineCode in "Example Medications Triggers"] IZ
where IZ.status = 'completed'
define "Lab Order Test Name Data":
[ServiceRequest: code in "Example Lab Order Test Name Triggers"] LO
where LO.status in { 'draft', 'active', 'completed' }
and LO.intent in { 'proposal', 'plan', 'order', 'original-order', reflex-order', 'filler-order', 'instance-order' }
define "Lab Observation Test Name Data":
[Observation: code in "Example Lab Observation Test Name Triggers"] LR
where LR.status in { 'registered', 'preliminary', 'final', 'amended' }
define "Medications Data":
[MedicationAdministration: medicationCodeableConcept in "Example Medications Triggers"] MR
where MR.status in { 'active', 'completed' }
define "Organism Substance Data":
[Observation: code in "Example Organism Substance Triggers"] OS
where OS.status in { 'registered', 'preliminary', 'final', 'amended' }
define "Condition Suspected Disorder Data":
[Condition: code in "Example Suspected Disorder Triggers"] SD
where SD.clinicalStatus in { 'active', 'recurrence', 'relapse' }
and SD.verificationStatus in { 'unconfirmed', 'provisional', 'differential', 'confirmed' }
define "Encounter Suspected Disorder Data":
[Encounter: reasonCode in "Example Suspected Disorder Triggers"] SD
where SD.status in { 'arrived', 'triaged', 'in-progress', 'onleave', 'finished' }
define "Suspected Disorder Data":
"Condition Suspected Disorder Data"
union "Encounter Suspected Disorder Data"
NOTE: The status checks in the above are not necessarily appropriate, it may be that the initial triggering should ignore status entirely, leaving a status check to the more detailed condition-specific criteria. More on this in the data category patterns discussion below.
As of the most recent release of the eRSD specification, condition-specific value sets are available that specify, per condition (by name currently, but planned to be by code) the value sets that are used within criteria for that condition. These value sets are identified by specifying the "focus" within the useContext. For example:
"useContext": [
{
"code": {
"system": "http://terminology.hl7.org/CodeSystem/usage-context-type",
"code": "focus"
},
"valueCodeableConcept": {
"text": "Chlamydia"
}
}
]
NOTE: In addition to the condition, we will need to know the category in order to be able to accurately determine which resource template to use to construct the rule expression.
These condition-specific value sets can then be used to construct CQL expressions referencing the various categories:
valueset "VS: Chlamydia trachomatis Infection (Tests for Chlamydia trachomatis by Culture and Identification Method)": 'TBD'
valueset "VS: Chlamydia trachomatis Infection (Tests for Chlamydia trachomatis Nucleic Acid)": 'TBD'
valueset "VS: Chlamydia trachomatis Infection (Tests for Chlamydia trachomatis Antigen)": 'TBD'
valueset "VS: Chlamydia trachomatis Infection (Tests for Chlamydia species by Culture and Identification Method)": 'TBD'
valueset "VS: Chlamydia trachomatis Infection (Tests for Chlamydia species Nucleic Acid)": 'TBD'
valueset "Indeterminate or Equivocal Lab Result Value": 'TBD'
valueset "Negative or Undetected Lab Result Value": 'TBD'
NOTE: This needs to include value sets that are used across conditions as part of the criteria as well, the "Indeterminate or Equivocal Lab Result Value" and "Negative or Undetected Lab Result Value" value sets in this example.
The reportability criteria are expressed in terms of a specific set of data categories (roughly analagous to the trigger categories, but slightly broader):
For each of these categories, template CQL could be used to build a template expression for data elements used in criteria expressions. For example, for the Laboratory Test Result category, we could use a template:
define "<<Condition Name>> Laboratory Test Results":
[Observation: <<Condition Valueset>>] O
where O.status in { 'preliminary', 'final', 'amended', 'corrected' }
DataTemplateId | Description |
Active Diagnosis | Problem list items that are clinically active |
Encounter Diagnosis | Encounter diagnosis codes |
Negative lab result | Parameterizable query for this category of data |
With these building blocks, we can then construct criteria based on the criteria definitions. For example, consider the following negative test results criteria for Chlamydia:
Each of these criteria have an associated value set, and are referencing a particular data category. Combining these, we can generate appropriate CQL to express the rule:
define "Negative results of tests for identification of Chlamydia trachomatis in a clinical specimen by organism-specific culture method, including identification tests performed on an isolate":
[Observation: VS: Chlamydia trachomatis Infection (Tests for Chlamydia trachomatis by Culture and Identification Method)] O
where O.status in { 'preliminary', 'final', 'amended', 'corrected' }
and (
// TODO: More investigation into the Value here
O.interpretation in "Indeterminate or Equivocal Lab Result Value"
or O.value in "Indeterminate or Equivocal Lab Result Value"
)
To support this generation, we need to identify, for each criteria of each condition, the data category, the associated condition-specific value set, and the criteria expression to be used. From a Chlamydia condition description, this rule is represented as:
21. Negative results of tests for identification of Chlamydia trachomatis in a clinical specimen by organism-specific culture method, including identification tests performed on an isolate (i.e., ‘negative’ culture results)
IF
Patient has lab result with test name of [VS: Chlamydia trachomatis Infection (Tests for Chlamydia trachomatis by Culture and Identification Method)]
AND
lab result value of [VS: Negative or Undetected Lab Result Value]
THEN report
CriteriaId | Description | Data Template Id | Condition Parameters (Condition-Specific Value Set for the Criteria) |
xxx-123 | Chlamydial cervicitis and urethritis, and lymphogranuloma… | Diagnosis | 1.2.640… |
NOTE: This is the most variable aspect of the system, so don't necessarily need this logic evaluated in every case. Could focus exclusively on the criteria that would indicate additional filtering, and by default a trigger match is potentially reportable, unless there is a condition-specific rule filter in place.
Individual data criteria can then be combined by jurisdictions in different ways to support the reportability determination. Criteria can be combined with:
or
and
or
of all the O criteria, with and
To support construction of the CQL representation of the rule, we need to identify the criteria and the combination method used for each criteria in the rule.
For example:
Criteria | Combination Method |
---|---|
Chlamydial conjunctivitis (as diagnosis or active problem) | N |
Patient age < 1 year | N |
For the Chlamydial conjunctivitis criteria:
define "Chlamydial conjunctivitis (as a diagnosis or active problem)":
exists (
[Encounter: reasonCode in "VS: Chlamydia trachomatis Infection [Conjunctivitis] (Disorders) (SNOMED)"]
[Encounter: reasonCode in "VS: Chlamydia trachomatis Infection [Conjunctivitis] (Disorders) (ICD10CM)"]
)
or exists (
[Diagnosis: "VS: Chlamydia trachomatis Infection [Conjunctivitis] (Disorders) (SNOMED)"]
union [Diagnosis: "VS: Chlamydia trachomatis Infection [Conjunctivitis] (Disorders) (ICD10CM)"]
) Dx
where Dx.clinicalStatus ~ "Active"
and Dx.verificationStatus ~ "Confirmed"
And for the Patient age < 1 year criteria:
define "Patient age < 1 year":
AgeInYears() < 1
And then combining them according to the combination method rules:
define "Chlamydial conjunctivitis (as a diagnosis or active problem) AND patient age < 1 year":
"Chlamydial conjunctivitis (as a diagnosis or active problem)"
and "Patient age < 1 year"
The final aspect of the decision is the configuration of different rules in different jurisdictions. Following the steps to this point, we have for each rule, an expression of CQL that returns true if the source data for the patient contains the configured criteria. Because reportability must be determined for both the jurisdiction of care and the jurisdiction of residence, we need to know Patient address, as well as the Location address where the care was provided. We access this information through the Patient.address
element, and through the Encounter.location.location
reference to a Location
resource, and use the address
element there.
Jurisdiction determination is a complex process, but a suitable determination algorithm for the local rules would be to use a zipcode if there is a jurisdiction configured for that zipcode, falling back to a state jurisdiction code. Note that this also needs to account for the possibility that a zipcode is in multiple jurisdictions, which would result in multiple reportability responses. Note that although this approach isn't 100% accurate, in that it will sometimes result in a report to a jurisdiction that will ultimately be determined not reportable, it at least provides a suitable first pass that can be evaluated locally.
To support local determination of jurisdiction, we need, for each jurisdiction, a unique identifier, a description, whether the jurisdiction is a state or local jurisdiction, and what the state and configured zipcodes are for that jurisdiction:
The Jurisdiction Types CodeSystem defines the following jurisdiction types: BURROUGH, CITY, COUNTY, DISTRICT, PARISH, STATE
State/Territory is covered by the STATE code and any non-state code is considered "local" for the purposes of matching, and matching at the local level is always done by zipcode/postalcode.
Use State value set for the value of State column
Identifier | Description | Type | State | Zipcodes |
---|---|---|---|---|
UT | Utah State Health Department | State | UT | 84057,84058,84059 |
UTC | Utah County Health Department | Local | UT | 84057,84058,84059 |
ID | Idaho | State | ID | |
IDC | Idaho County Health Department | Local | ID | 83701 |
For a given event:
Event processing needs to know for jurisdiction of residence and jurisdiction of care, what rules to run
NOTE: The feature to provide a copy of a reportable event from a local jurisdiction to a state jurisdiction is implemented in the reporting platform, not part of this site-based jurisdiction determination logic
Given the above example configuration, the following examples illustrate the expected behavior, assuming that the jurisdiction or residence and the jurisdiction of care are the same:
Example 1: An event occurring in Orem UT, 84057
Example 2: An event occurring in Moab UT, 84532
Example 3: An event occurring in Boise ID, 83701
Example 4: An event occurring in Idaho Falls ID, 83403
Once we know the set of rules, and the set of configurations of those rules for each jurisdiction, and the rules for determining which jurisdictions are applicable for patient and encounter data, then we can apply that information to construct a complete CQL expression of the site-specific reportability decision criteria. For example:
define "Chlamydial conjunctivitis (as a diagnosis or active problem) AND patient age < 1 year (with Jurisdictions)":
"Chlamydial conjunctivitis (as a diagnosis or active problem) AND patient age < 1 year"
and exists ((
GetJurisdictionCodes(Patient.address)
union GetJurisdictionCodes(GetLocation(Encounter.location.location))
) JC
where JC in { 'A', 'B', 'C' }
Note that GetJurisdictionCodes will need to account for the possibility of multiple addresses for both the patient and location. Note explicitly that since there is no requirement for address use, type, and period, the logic will ignore those elements of addresses and process for any address it is given.
In this example, assume a list of hypothetical jurisdictions 'A', 'B', and 'C' are all configured to report with this rule.
To construct this, we would need to identify, for each jurisdiction, which rules they are configured to run.
The criteria for each condition can then be combined into a single, non-condition-specific expression that could be run on any event, and would return true if that event was reportable according to any condition-specific criteria configured for the jurisdictions of care and residence of the event:
define "IsReportable":
"Chlamydial conjunctivitis (as a diagnosis or active problem) AND patient age < 1 year (with Jurisdictions)"
or "Identification of Chlamydia species in a clinical specimen by culture method, including identification tests performed on an isolate (with Jurisdictions)"
or <other Chlamydia condition criteria>...
or <other condition criteria>...
See the eRSD PlanDefinition Example for an example implementation