-
Notifications
You must be signed in to change notification settings - Fork 24
Cooking with CQL Q&A Index Using Fast Healthcare Interoperability Resources (FHIR) Category
Each Q&A has the Cooking with CQL session number and date. For the most current and accurate information, please check the CQL Qs&As for a more recent answer to your question.
AllergyIntolerance: In the example on AllergyIntolerance, why are some Quality Improvement (QI)-Core Patterns, such as “allergy-active” and “allergy-confirmed,” represented in the Global Common Library?
/*
Use of AllergyIntolerance
*/
// connectathon/fhir4/cql/EXM105_FHIR4-8.1.000.cql
define "Statin Allergy":
["AllergyIntolerance": "Statin Allergen"] StatinAllergy
where (StatinAllergy.clinicalStatus is null or FHIRHelpers.ToConcept(StatinAllergy.clinicalStatus) ~
Global."allergy-active")
and FHIRHelpers.ToConcept(StatinAllergy.verificationStatus) in { Global."allergy-unconfirmed",
Global."allergy-confirmed" }
(Session 42 - 2/27/20)
/*
Use of AllergyIntolerance
*/
// connectathon/fhir4/cql/EXM105_FHIR4-8.1.000.cql
define "Statin Allergy":
["AllergyIntolerance": "Statin Allergen"] StatinAllergy
where (StatinAllergy.clinicalStatus is null or FHIRHelpers.ToConcept(StatinAllergy.clinicalStatus) ~
Global."allergy-active")
and FHIRHelpers.ToConcept(StatinAllergy.verificationStatus) in { Global."allergy-unconfirmed",
Global."allergy-confirmed" }
- We define several direct reference codes related to AllergyIntolerance and other resources profiled by QI-Core in the Global Common Library so it is easy to reference. If there was a value set established, then that could be referenced directly from the Clinical Quality Language (CQL). In the interim, we reference the direct reference codes in the Global Common Library.
Commonly Used QDM Datatypes in FHIR: How would some of the most commonly used datatypes in Quality Data Model (QDM) be expressed in a Fast Healthcare Interoperability Resource (FHIR)-based measure? (Session 37 - 7/25/19)
- These common datatype examples below are expressed in Fast Healthcare Interoperability Resource (FHIR)-using Quality Data Model version 5.4, FHIR version 4.0.0, and include FHIRHelpers version 4.0.0.
- QDM Diagnosis to FHIR Diagnosis from CMS72v8:
define "QDM Diagnosis":
[Diagnosis: "Intravenous or Intra arterial Thrombolytic (tPA) Therapy Prior to Arrival"]
FHIR Equivalent:
define "FHIR Diagnosis":
[FHIR.Condition: "Intravenous or Intra arterial Thrombolytic (tPA) Therapy Prior to Arrival"] PriorTPA
where PriorTPA.clinicalStatus in "Active Condition"
NOTE: onset and abatement need to be considered, and there is an outstanding question as to whether the clinical status element would always be consistent with onset and abatement.
- Next datatype is Encounter, Performed using the example from MATGlobalCommonFunctions:
define "QDM Encounter Performed":
["Encounter, Performed": "Encounter Inpatient"]
FHIR Equivalent:
define "FHIR Encounter Performed":
[Encounter: "Encounter Inpatient"] EncounterInpatient
where EncounterInpatient.status = 'finished'
NOTE: Encounter, Performed in QDM can include "in progress" encounter, so it's not necessarily always just 'finished', the point is status needs to be checked consistent with measure intent.
QDM to QI Core Mapping would be a good starting point. This goes through all of the QDM datatypes and what the mapping to FHIR looks like.
- Next datatype is Medication, Order using the example from VTE-1.
define "QDM Medication Order":
["Medication, Order": "Low Dose Unfractionated Heparin for VTE Prophylaxis"]
FHIR Equivalent:
define "FHIR Medication Order":
["MedicationRequest": "Low Dose Unfractionated Heparin for VTE Prophylaxis"] M
where M.intent = 'order'
- Last datatype is Procedure, Performed using the example from VTE-1.
define "QDM Procedure Performed":
["Procedure, Performed": "Comfort Measures"]
FHIR Equivalent:
Define "FHIR Procedure Performed":
["Procedure": "Comfort Measures"] P
where P.status = 'completed'
CQL Version for ELM: Over the last year, there have been changes to CQL. Is the version of CQL that was used to build the Expression Logical Model (ELM) available? (Session 43 - 4/23/20)
- Yes, it is included in the latest ELM output version of the CQL-to-ELM translator.
Cumulative Medication Duration - adjacent intervals and the collapse function: Within the Cumulative Medication Duration in Fast Healthcare Interoperability Resources (FHIR®), is there a calculation that works well with adjacent intervals and the collapse function? (Session 54 - 5/27/21)
- The most effective calculation involves the CumulativeDuration function for intervals and collapse. This logic provides the ability to look at the boundaries of the intervals for an adjacent collapse on a per day perspective supplying the necessary granularity to generate a successful collapse.
define function CumulativeDuration(Intervals List<Interval<DateTime>>):
Sum((collapse Intervals per day) X return all difference in days between start of X and end of X)
Next, define a function that rolls out intervals.
define function RolloutIntervals(intervals List<Interval<DateTime>>):
intervals I
aggregate R starting (null as List<Interval<DateTime>>):
R union ({
I X
let
S: Max({ end of Last(R) + 1 day, start of X }),
E: S + duration in days of X
return Interval[S, E]
})
Cumulative Medication Duration - MedicationDispensePeriod: Regarding Cumulative Medication Duration in Fast Healthcare Interoperability Resources (FHIR®), within the define function of the MedicationDispensePeriod logic expression, when would a measure developer use the start of boundsPeriod compared to D.whenPrepared? The structure of the Coalesce line of code is written in a way that suggests start of boundsPeriod is the preferential timing attribute for the MedicationDispensePeriod logic expression in all cases.
define function MedicationDispensePeriod(Dispense "MedicationDispense"):
Dispense D
let
dosage: singleton from D.dosageInstruction,
doseAndRate: singleton from dosage.doseAndRate,
doseRange: doseAndRate.dose as Range,
doseQuantity: doseAndRate.dose as SimpleQuantity,
dose: Coalesce(end of doseRange, doseQuantity),
timing: dosage.timing,
frequency: Coalesce(timing.repeat.frequencyMax, timing.repeat.frequency),
period: System.Quantity { value: timing.repeat.period, unit: timing.repeat.periodUnit.value },
dosesPerDay: Coalesce(ToDaily(frequency, period), Count(timing.repeat.timeOfDay), 1.0),
boundsPeriod: timing.repeat.bounds as Period,
startDate:
Coalesce(
start of boundsPeriod,
D.whenHandedOver,
D.whenPrepared
)
return
if HasEnd(boundsPeriod) then
Interval[startDate, end of boundsPeriod]
else
Interval[startDate, startDate + Coalesce(D.daysSupply, D.quantity / (dose * dosesPerDay))]
(Session 54 - 5/27/21)
define function MedicationDispensePeriod(Dispense "MedicationDispense"):
Dispense D
let
dosage: singleton from D.dosageInstruction,
doseAndRate: singleton from dosage.doseAndRate,
doseRange: doseAndRate.dose as Range,
doseQuantity: doseAndRate.dose as SimpleQuantity,
dose: Coalesce(end of doseRange, doseQuantity),
timing: dosage.timing,
frequency: Coalesce(timing.repeat.frequencyMax, timing.repeat.frequency),
period: System.Quantity { value: timing.repeat.period, unit: timing.repeat.periodUnit.value },
dosesPerDay: Coalesce(ToDaily(frequency, period), Count(timing.repeat.timeOfDay), 1.0),
boundsPeriod: timing.repeat.bounds as Period,
startDate:
Coalesce(
start of boundsPeriod,
D.whenHandedOver,
D.whenPrepared
)
return
if HasEnd(boundsPeriod) then
Interval[startDate, end of boundsPeriod]
else
Interval[startDate, startDate + Coalesce(D.daysSupply, D.quantity / (dose * dosesPerDay))]
- The CQI/Clinical Decision Support (CDS) Work Group discussed this topic at the meeting with the Pharmacy Work Group on May 28, 2021, where the Work Groups agreed that the start of boundsPeriod should not be used to determine when the medication becomes available to the patient. The Work Groups determined the preference is whenHandedOver, but if unavailable, then whenPrepared is a reliable boundary. Therefore, the Work Group removed the start of boundsPeriod from the MedicationDispensePeriod definition in the updated version.
define function MedicationDispensePeriod(Dispense "MedicationDispense"):
Dispense D
let
dosage: singleton from D.dosageInstruction,
doseAndRate: singleton from dosage.doseAndRate,
doseRange: doseAndRate.dose as Range,
doseQuantity: doseAndRate.dose as SimpleQuantity,
dose: Coalesce(end of doseRange, doseQuantity),
timing: dosage.timing,
frequency: Coalesce(timing.repeat.frequencyMax, timing.repeat.frequency),
period: System.Quantity { value: timing.repeat.period, unit: timing.repeat.periodUnit.value },
dosesPerDay: Coalesce(ToDaily(frequency, period), Count(timing.repeat.timeOfDay), 1.0),
startDate:
Coalesce(
D.whenHandedOver,
D.whenPrepared
)
return
Interval[startDate, startDate + Coalesce(D.daysSupply, D.quantity / (dose * dosesPerDay))]
Cumulative Medication Duration - Refills: When calculating Cumulative Medication Duration in Fast Healthcare Interoperability Resources (FHIR®), are refills considered in a medication dispense for individual prescription refills? (Session 54 - 5/27/21)
- Prescription refills are disregarded in a medication dispense period because there is an expectation that there are multiple dispense records: a dispense record for the initial fill and a separate dispense record for each refill.
define function MedicationDispensePeriod(Dispense "MedicationDispense"):
Dispense D
let
dosage: singleton from D.dosageInstruction,
doseAndRate: singleton from dosage.doseAndRate,
doseRange: doseAndRate.dose as Range,
doseQuantity: doseAndRate.dose as SimpleQuantity,
dose: Coalesce(end of doseRange, doseQuantity),
timing: dosage.timing,
frequency: Coalesce(timing.repeat.frequencyMax, timing.repeat.frequency),
period: System.Quantity { value: timing.repeat.period, unit: timing.repeat.periodUnit.value },
dosesPerDay: Coalesce(ToDaily(frequency, period), Count(timing.repeat.timeOfDay), 1.0),
startDate:
Coalesce(
D.whenHandedOver,
D.whenPrepared
)
return
Interval[startDate, startDate + Coalesce(D.daysSupply, D.quantity / (dose * dosesPerDay))]
Cumulative Medication Duration - RolloutIntervals: In Cumulative Medication Duration using the Fast Healthcare Interoperability Resources (FHIR®) standard, what logic expression should the measure developer use to accommodate an early refill on a medication dispense? (Session 54 - 5/27/21)
- The RolloutIntervals function is the appropriate logic expression, which bumps out the intervals without altering the original dispense medication date. The current medication dispense date is set in the system (e.g., every 30 days). When there is a request for an early refill of the medication (e.g., a week early), the patient receives an add on supply of the medication equal to the time period of the current medication supply (e.g., 30 days). The roll out interval algorithm dispenses the second interval of medications (e.g., next 30 days) when the first medication interval has completed the original medication time period.
define function CumulativeMedicationDuration(
Medications List<Choice<"MedicationRequest",
"MedicationDispense",
"MedicationAdministration"
>>):
CumulativeDuration((
Medications M
where M is MedicationRequest
return MedicationPeriod(M)
)
union (
RolloutIntervals(
Medications M
where M is MedicationDispense or M is MedicationAdministration
return MedicationPeriod(M)
)
)
)
Cumulative Medication Duration - semantic clinical drug (SCD) concept: Regarding Cumulative Medication Duration in FHIR, what logic expression should the measure developer use for a prescription specifying a number of tablets and uses a semantic clinical drug (SCD) concept that specifies the tablet strength (e.g., lisinopril 10 mg tablet, administer two tablets)? (Session 54 - 5/27/21)
- The MedicationRequestPeriod logic expression applies the doseQuantity function to calculate the prescription amount, depending on the preparation of the medication (e.g., tablets, liquid, patches). The code for the particular medication (e.g., lisinopril) provides the dosage strength.
define function MedicationRequestPeriod(Request "MedicationRequest"):
Request R
let
dosage: singleton from R.dosageInstruction,
doseAndRate: singleton from dosage.doseAndRate,
doseRange: doseAndRate.dose as Range,
doseQuantity: doseAndRate.dose as SimpleQuantity,
dose: Coalesce(end of doseRange, doseQuantity),
timing: dosage.timing,
frequency: Coalesce(timing.repeat.frequencyMax, timing.repeat.frequency),
period: System.Quantity { value: timing.repeat.period, unit: timing.repeat.periodUnit.value },
dosesPerDay: Coalesce(ToDaily(frequency, period), Count(timing.repeat.timeOfDay), 1.0),
boundsPeriod: timing.repeat.bounds as Period,
daysSupply: R.dispenseRequest.expectedSupplyDuration,
quantity: R.dispenseRequest.quantity,
refills: Coalesce(R.dispenseRequest.numberOfRepeatsAllowed, 0),
startDate:
Coalesce(
start of boundsPeriod,
start of R.dispenseRequest.validityPeriod,
R.authoredOn
)
return
if HasEnd(boundsPeriod) then
Interval[startDate, end of boundsPeriod]
else
Interval[startDate, startDate + Coalesce(daysSupply, quantity / (dose * dosesPerDay)) * (1 + refills)]
EXM130 - FHIR.Timing and FHIR.String: In the EXM130 expression below, would it be better to move the FHIR.Timing and the FHIR.string to the bottom of the list if one were to prioritize the list?
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string,
FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as
FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1
year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).high) + 1 year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a
Timing type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else null as Interval<DateTime>
end
(Session 41 - 1/16/20)
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string,
FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as
FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1
year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).high) + 1 year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a
Timing type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else null as Interval<DateTime>
end
- The case expression in Clinical Quality Language (CQL) doesn’t carry any implication of ordered processing and in any given instance choices are exclusive. Therefore, you will only have one set of the types chosen at a given time. For example, you would not have a FHIR.dateTime and a FHIR.Timing in the same element. However, implementations may walk through each of the instances from top to bottom so it would be reasonable to move the FHIR.Timing and the FHIR.string to the bottom to allow for the more common instances to be hit first, but it doesn’t change the actual semantics of the function.
EXM130 - Message Function: Based on the EXM130 expression below, can you explain the “Message” function and each component of it?
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string,
FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as
FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1 year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).high) + 1
year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a Timing
type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else null as Interval<DateTime>
end
(Session 41 - 1/16/20)
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string,
FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as
FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1 year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).high) + 1
year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a Timing
type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else null as Interval<DateTime>
end
- Clinical Quality Language (CQL) is a functional language, meaning everything in CQL is going to return a value. Even in the case where we’re throwing an ‘Error’ or returning a ‘Warning,’ it will still return a value. The first argument to the Message function is the value it will return as an Interval. You can also add conditions to log the Message. Below are the components of this line broken down: Message(null as Interval, true, '1', 'Error', 'Cannot compute a single interval from a Timing type') • null as Interval = the first argument is the value you want to return • true = the condition, you can conditionally return or log the message. • 1 = the error code. • Error = the message you want to send back - this can be ‘Error,’ ‘Warning,’ or ‘Information.’ If it’s an ‘Error,’ it also stops processing. If it’s a ‘Warning,’ it’s a message that comes back as part of the evaluation. Since it doesn’t stop processing, it needs to send back a result. • Cannot compute a single interval from a Timing type = the message you actually want to send out.
EXM130 - Normalize Interval Function: Based on EXM130 – consider the Procedure.performed element:
http://hl7.org/fhir/STU3/procedure-definitions.html#Procedure.performed_x_
http://hl7.org/fhir/procedure-definitions.html#Procedure.performed_x_
In the Fast Healthcare Interoperability Resources (FHIR) Standard for Trial Use (STU) 3, it is defined as a choice of dateTime|Period and in R4, it is defined as a choice of dateTime|Period|string|Age|Range where the Quality Improvement (QI) Core constrains out the “string” type.
For STU3, the standard “Normalize Interval” function works:
define "Total Colectomy Performed":
[Procedure: "Total Colectomy"] Colectomy
where Colectomy.status = 'completed'
and Global."Normalize Interval"(Colectomy.performed) starts on or before end of "Measurement
Period"
But for R4, we need to expand the “Normalize Interval” function to allow for the new types:
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string,
FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as
FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1
year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).high) + 1 year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a
Timing type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else null as Interval<DateTime>
end
It’s a great idea to have a “Normalize Interval” function in the global library, but will you have one function, “Normalize Interval” function, that contains the flexible arguments in the parameter or will you have multiple ones depending on the FHIR resource? How many choice data type elements will the resource have? (Session 41 - 1/16/20)
For STU3, the standard “Normalize Interval” function works:
define "Total Colectomy Performed":
[Procedure: "Total Colectomy"] Colectomy
where Colectomy.status = 'completed'
and Global."Normalize Interval"(Colectomy.performed) starts on or before end of "Measurement
Period"
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string,
FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as
FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1
year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).high) + 1 year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a
Timing type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else null as Interval<DateTime>
end
- We could define “Normalize Interval” versions that had only the choices for what we wanted but when you start doing that, the differences between the sets of types defined in the different elements becomes unwieldy. By defining one version of “Normalize Interval” that has all the possible choice types we encounter in timing elements, we can define one function that can handle all of those and then as long as the types that the elements can be are a subset of these, then the Clinical Quality Language (CQL) can work with it.
EXM130 - Timing, Instance, and String: Based on the EXM130 expression below, why does the message only happen to the timing, instance, and string? Does it not apply for all resources?
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string,
FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as
FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1
year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).high) + 1 year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a
Timing type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else null as Interval<DateTime>
end
(Session 41 - 1/16/20)
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string,
FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as
FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1
year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as
FHIR.Range).high) + 1 year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a
Timing type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else null as Interval<DateTime>
end
- In the case of a timing element as highlighted above, a FHIR.string means you have a string that the user entered as the value for that time. For example, in this condition the resource string could have a value of “last spring.” With Fast Healthcare Interoperability Resources (FHIR) in the condition resource, it is possible to communicate that the user has actually entered the timing as just a natural language string and that’s what the string choice type means. Regarding FHIR.Instant as highlighted above, it is okay since it is just a DateTime that has to be to the millisecond. Regarding timing, in R4 it is an observation which was expanded to support not only DateTime and Period, but also Timing and Instant.
Timing is a very complex object that lets you build up very complex schedules, therefore, it is very flexible. However, if there is a use case where there is a system that wants to use the timing type of the effective element of an observation it means that the data received cannot be processed, which will result in an error message.
EXM135 - ConditionOnsetAbatementPrevalencePeriod for Delivery: The measure EXM135 is an example of ConditionOnsetAbatementPrevalencePeriod where we determine the Onset/Abatement and Prevalence Period for a condition. We define the Prevalence Period:
define function "Prevalence Period"(condition Condition):
Interval[start of "Normalize Onset"(condition.onset), end of "Normalize Abatement"(condition))
This makes sense for Pregnancy, but could you also use the procedure ‘Delivery’ to say that it ended? (Session 41 - 1/16/20)
define function "Prevalence Period"(condition Condition):
Interval[start of "Normalize Onset"(condition.onset), end of "Normalize Abatement"(condition))
- Yes, this is correct. For all other conditions you can follow the example but for pregnancy you can also use ‘Delivery’.
EXM135 - Use of Parentheses and Brackets in Timepoint of the Abatement: For the measure EXM135, is there a reason you exclude the timepoint of the abatement time by using parentheses “)” instead of brackets “]”? (Session 41 - 1/16/20)
- Yes, there are two reasons:
- This is when they said the condition was over so it really shouldn’t be included during the time that it is happening. It is an exclusive boundary.
- More importantly, if this element is not present and we use the inclusive boundary, then it would mean that this condition goes until the end of time. This is the opposite of what we are trying to say. Using the inclusive boundary means that we don’t know when that interval ends.
So, excluding the abatement time assumes that the condition abated some time before the recorded date. If this is false, then it means the condition goes on until the end of time.
Extensions: For the current Fast Healthcare Interoperability Resource (FHIR) resource version, implementers created an extension due to some missing information that was required for Quality Improvement Core (QI-Core). For the next FHIR version, will this extension be replaced by a formal element? (Session 43 - 4/23/20)
- Extensions are defined to support use cases that are not covered by the base resource. If the extensions are broadly applicable, they could be submitted back to the base specification to be considered for inclusion as an element on that base resource. The committee that stewards the base resource would need to go through the consensus process before that extension can be replaced by a formal element. During the consensus process, the extension would be evaluated to determine whether it is broadly applicable and if so, it would be replaced by a formal element in the resource. Extensions are not automatically included in the next FHIR version until they go through this consensus process to ensure they are applicable universally.
Extensions vs Slices: When using Clinical Quality Language (CQL) with Fast Healthcare Interoperability Resource (FHIR), FHIR has some things called Extensions and Slices. What’s the difference between them? In which scenario should we use them? (Session 43 - 4/23/20)
- An extension is a special kind of slice since it is a repeating element and is always discriminated by a URL. Whereas a slice can be discriminated by any of the elements.
In the blood pressure profile example below, you have a component. If you are not saying something different, but simply slicing to put some constraints on it, then you use a slice. However, if you want to say something new, then you use an extension.
define TestSlices:
[Observation: “Blood Pressure”] BP
let
SystolicBP: singleton from (BP.component C where C.code ~ "Systolic blood pressure"),
DiastolicBP: singleton from (BP.component C where C.code ~ "Diastolic blood pressure")
where SystolicBP.value < 140 'mm[Hg]'
and (DiastolicBP.value < 90 'mm[Hg]'
define TestSlices:
[“observation-bp”] BP
where BP.SystolicBP.value < 140 'mm[Hg]'
and BP.DiastolicBP.value < 90 'mm[Hg]'
FHIR Bulk Export: From a performance perspective of a Quality Data Model (QDM) approach vs. a Fast Healthcare Interoperability Resource (FHIR) approach, you might be able to have a pretty efficient database access with QDM. With the FHIR measure doing one patient at time, you’re going to be making a lot of FHIR application programming interface (API) calls. One option might be to use a FHIR bulk export, which would return a large Newline-Delimited JavaScript Object Notation (ND JSON) file (the file format used for bulk data transfer in FHIR), that might be a way to get a lot of information at one time. Is there some thought to using a bulk concept to frontload the data? (Session 39 - 10/24/19)
- The bulk export is certainly one option for retrieving all the data at one time. The way Clinical Quality Language (CQL) is built, all of the data access is expressed in terms of these retrieves so you can characterize the overall data requirements for this library by only looking at this set of retrieves and use that as parameters for an export operation to say “export all of the data required for this whole population given these data requirements.” You can think of evaluating this patient at the time against a Fast Healthcare Interoperability Resources (FHIR) endpoint where these retrieves are converted into actual FHIR Uniform Resource Locators (URLs) is one approach to implementation. You can also imagine an implementation that went behind the scenes to access the same infrastructure that is supporting the FHIR application programming interface (API) and dig into that directly. With the reference implementation that is plugged into a HAPI FHIR server that reaches into the data access layer, you’re hitting the underlying data access layer instead of going back out through the endpoint when you run the CQL in process in the HAPI FHIR server. It’s still the case that in the reference implementation, the processing is happening a patient at a time, but you can imagine an implementation that does the same kind of thing but across the population rather than a patient at a time. The key aspect that’s being communicated from the logic perspective is what the structure looks like and what the criteria are. It may be the case that you actually have a structured query language (SQL) database that has your FHIR resources in it and the way that you take the CQL and run it in that environment may be to translate it to an SQL query across patients. So there are a lot of potential approaches to evaluating the CQL and one of the primary goals of this approach is to make sure the logic can be shared and evaluated in all those different environments that makes the most sense for those environments.
FHIR Helpers in QI-Core: Does the Quality Improvement Core (QI-Core) model info 4.0.0 Extensible Markup Language (XML) use Fast Healthcare Interoperability Resources (FHIR) Helpers? (Session 43 - 4/23/20)
- It does not. QI-Core maps are in the model info. Example: the CQL to ELM translator is outputting gender.value, but the authoring environment is just gender.
FHIR Helpers in QUICK: Is the Quality Information and Clinical Knowledge (QUICK) data model Extensible Markup Language (XML) using Fast Healthcare Interoperability Resources (FHIR) Helpers? (Session 43 - 4/23/20)
- The QUICK 1.6 version of FHIR was manually built and is not using any type of FHIR Helpers. It is still being developed to secure a more automatic process. There is also QUICK 3.0 XML and 3.0.0 XML which are in various stages of completion, but not ready for use and are not using FHIR helpers.
FHIR Resources: Can you provide some resources for using Clinical Quality Language (CQL) in Fast Healthcare Interoperability Resources (FHIR)? (Session 42 - 2/27/20)
- Here are some CQL resources: • Getting Started with CQL (used at the January 2020 Connectathon in Baltimore MD) https://confluence.hl7.org/download/attachments/46892187/Getting%20Started%20with%20CQL.pptx?version=1&modificationDate=1555608720936&api=v2 • Community Projects – Lists all the known open source community project (discussing all the technical aspects) and implementation projects. https://github.com/cqframework/clinical_quality_language/wiki/Community-Projects • Clinical Quality Language Project Home Page – This Health Level Seven International (HL7) Confluence page houses all of the up to date information about the project. https://confluence.hl7.org/display/CDS/Clinical+Quality+Language) • The Clinical Decision Support (CDS) Work group will be balloting CQL Normative in the 2020 May cycle and the May Ballot plan. https://confluence.hl7.org/download/attachments/76160321/CQL%20Ballot%20Plan%20-%202020%20May.pptx?version=1&modificationDate=1582694882432&api=v2
Fluent function - arithmetic operators: In Fast Healthcare Interoperability Resources® 4.0.1, when writing a calculation that requires complex arithmetic, would it be best to use the fluent function to make the logic more readable to the measure implementer? (Session 58 - 10/28/2021)
- The intent of the fluent function is to help with complicated logic to provide a more human readable expression. You can use all arithmetic operators within this function to chain operations together to obtain closure and achieve a usable fluent syntax. However, it is important to look at each use case to determine whether it is best to use a fluent function or to use an equation with the required mathematics to achieve the most succinct and clear human readable expression. For additional details on the use of fluent functions, please refer to the Clinical Quality Language Developer’s Guide at https://cql.hl7.org/03-developersguide.html#fluent-functions.
An example of a fluent function
define fluent function plus(x Integer, y Integer):
Plus(x, y)
define fluent function minus(x Integer, y Integer):
x - y
define Testplus:
2.plus(2).minus(12)
Fluent function - data requirements: In Fast Healthcare Interoperability Resources® 4.0.1, when using fluent functions, where do the data requirements display for the end user? (Session 58 - 10/28/2021)
- The fluent functions will display in the Function section of the human readable output as a function dependency.
Fluent function - defining intervals : When using fluent functions in Fast Healthcare Interoperability Resources® 4.0.1, do you need to use the prefix if you define the intervals in different files? (Session 58 - 10/28/2021)
- No, you can define the intervals in different files if the libraries include the defined intervals.
Fluent function - extensions in MAT: In Fast Healthcare Interoperability Resources® 4.0.1, the creation of US Core Elements library v3.1.1 and Quality Improvement (QI)-Core Elements library v4.1.0 were to define functions to expose extensions as fluent functions in Clinical Quality Language (CQL). How would the use of these fluent functions appear in the Measuring Authoring Tool (MAT) human readable format for a user? (Session 58 - 10/28/2021)
- The human readable, MAT, and CQL code would be the same and appear as
define "Encounter With First ICU Stay With Principal Procedure of SCIP VTE Selected Surgery (2)":
from
"Encounter With ICU Location" QualifyingEncounterICU,
"SCIP VTE Selected Surgery" SelectedProcedure
let EncounterProcedure: singleton from (
(QualifyingEncounterICU.procedure()) P
where P.rank = 1
and P.procedure.getId() = SelectedProcedure.id
)
where date from end of SelectedProcedure.performed.toInterval()
during VTE.CalendarDayOfOrDayAfter(VTE.StartOfFirstICU(QualifyingEncounterICU))
return QualifyingEncounterICU
Fluent function - libraries with the same name: When using fluent functions in Fast Healthcare Interoperability Resources (FHIR®) 4.0.1, how does FHIR resolve the conflict if two libraries have fluent functions with the same name? (Session 58 - 10/28/2021)
- When two libraries contain fluent functions with the same name, resolution first occurs in the local library then you apply the fluent functions in the order as declared in the include statements.
include FHIRHelpers version '4.0.1'
include Concepts called Concepts
include FHIRCommon called FC
include MATGlobalCommonFunctionsFHIR4 called Global
include VTEFHIR4 called VTE
Fluent function - Normalize interval: In the Fast Healthcare Interoperability Resources®-based Global Common library, is the “Normalize Interval” an example of a fluent function in Clinical Quality Language using syntactic sugar? Syntactic sugar is defined as a syntax in a programming language that also improves the readability of code. (Session 58 - 10/28/2021)
define "Flexible Sigmoidoscopy Performed":
[Procedure: Concepts"Flexible Sigmoidoscopy"] FlexibleSigmoidoscopy
where FlexibleSigmoidoscopy.status = 'completed'
and Global."Normalize Interval"(FlexibleSigmoidoscopy.performed) ends 5 years or less on or before end of "Measurement Period"
define "Flexible Sigmoidoscopy Performed":
[Procedure: Concepts"Flexible Sigmoidoscopy"] FlexibleSigmoidoscopy
where FlexibleSigmoidoscopy.status = 'completed'
and Global."Normalize Interval"(FlexibleSigmoidoscopy.performed) ends 5 years or less on or before end of "Measurement Period"
- Correct. It is exactly like an extension method in C sharp. The ELM translator expands the “toInterval” to a prefix implementation and used purely for readability.
Fluent function - overloading : Do fluent functions support overloading? (Session 58 - 10/28/2021)
- Yes, fluent functions support overloading. Overloading allows a single function to have multiple uses based on the input and output parameters included with the function. Overloading reduces the complexity of the code.
Fluent function - QDM specification : Are fluent functions only feasible in Fast Healthcare Interoperability Resources (FHIR®) and not in a Quality Data Model (QDM) specification? (Session 58 - 10/28/2021)
- FHIR fluent functions are independent of the model. You can apply normalized intervals in the QDM global common functions library in QDM as fluent functions. The QDM global common functions library does not need extension functions because QDM doesn’t have extensions, but they can still be used to provide a post-fix notation.
Fluent function - shared library : In Fast Healthcare Interoperability Resources® 4.0.1, is there a difference in the use of fluent functions if you define the fluent function in a shared library instead of the code? (Session 58 - 10/28/2021)
- The expression does not change. You can define a fluent function in the code or in a shared library and it invokes the same way as if you defined it in the code.
HL7 to Replace QI-Core with QUICK?: Once Quality Improvement Core (QI-Core) becomes stable, will HL7 publish an update to include the new model information and replace the Quality Information and Clinical Knowledge (QUICK) model tab? (Session 43 - 4/23/20)
- Yes, this could be an update since it is not changing QI-Core. At the moment, an update is still in the exploration phase. The team is trying to provide simple and consistent approaches as well as making sure it can be maintained and implemented based on the authoring model. The CQL to ELM translator v1.4.9 snapshot supports using QI-Core. The translator is being used in the Atom plugin and the Measure Authoring Tool (MAT)-on-Fast Healthcare Interoperability Resources (FHIR) to make sure there is consistent usage of Clinical Quality Language (CQL) across the board.
Matches Function: Regarding the measure Device Indicating Frailty, when considering Fast Healthcare Interoperability Resources (FHIR)-based Clinical Quality Language (CQL) Quality Improvement (QI)-Core Patterns (http://build.fhir.org/ig/HL7/fhir-qi-core/patterns.html) within the DeviceRequest.intent comparison, is there a way to ask whether the code is “like ‘%order’”, similar to the what is supported by some database query languages?
/*
Use of DeviceRequest
*/
// connectathon/fhir4/cql/AdvancedIllnessandFrailtyExclusion_FHIR4-4.0.000.cql
define "Device Indicating Frailty":
[DeviceRequest: "Frailty Device"] FrailtyDeviceOrder
where FrailtyDeviceOrder.status in { 'active', 'on-hold', 'completed' }
and FrailtyDeviceOrder.intent in { 'order', 'original-order', 'reflex-order', 'filler-order', 'instance-
order' }
(Session 42 - 2/27/20)
/*
Use of DeviceRequest
*/
// connectathon/fhir4/cql/AdvancedIllnessandFrailtyExclusion_FHIR4-4.0.000.cql
define "Device Indicating Frailty":
[DeviceRequest: "Frailty Device"] FrailtyDeviceOrder
where FrailtyDeviceOrder.status in { 'active', 'on-hold', 'completed' }
and FrailtyDeviceOrder.intent in { 'order', 'original-order', 'reflex-order', 'filler-order', 'instance-
order' }
- Yes, CQL does have that capability. There is a Matches function that supports string-based pattern matching and there are StartsWith or EndsWith functions that you can use. However, because these comparisons are terminological, the recommended approach is to define a value set that contains all and only the specific codes of interest. This example is listing the codes for illustration purposes only. Using a string-based pattern matching function for terminological comparison introduces a risk of matching on unintentional codes.
Pharmacy Workgroup - harmonizing NCDCP and FHIR: Is there a group that is working to harmonize the National Council for Prescription Drug Programs (NCDCP) and Fast Healthcare Interoperability Resources® (FHIR)? (Session 57 - 9/16/21)
- Please email the Pharmacy Workgroup listserv at [email protected] to ask for more information regarding the group that is harmonizing NCDCP and FHIR. You can also subscribe to the Pharmacy Workgroup listserv here: http://www.hl7.org/Special/committees/medication/listserv.cfm. The Pharmacy Workgroup Confluence page can be found here: https://confluence.hl7.org/display/PHAR.
Quality Improvement (QI)-Core QUICK: Which model should measure developers use while coding within Fast Healthcare Interoperability Resources (FHIR)? (Session 43 - 4/23/20)
- The Quality Improvement Core (QI-Core) 4.0.0 model is the correct one at this point. Testing is still being done to cover all of the use cases.
Quality Improvement (QI)-Core QUICK: Once the Quality Improvement (QI)-Core QUICK model is fully mature and supported, will that be the expected mechanism to write a Fast Healthcare Interoperability Resources (FHIR)-based measure? (Session 39 - 10/24/19)
- We’re currently exploring that and making sure that the specifications can be expressed that way and making sure that it’s a reasonable and feasible way of doing this. At this point, we’re still in the exploration phase. We are making sure the specification fully supports the specification of electronic clinical quality measure and quality reporting using this mechanism.
Reference and Identifier resource patterns: Regarding resource patterns in Fast Healthcare Interoperability Resources (FHIR®) version 4.0.1, could both the reference and the identifier fields be populated with different values? (Session 56 - 7/29/21)
- The reference fields could be populated with different values given the underlying structures. Sometimes a reference will be provided with both the reference to the URL as well as an identifier. In this case, the reference datatype and the identifier field should have consistent populated fields, meaning that if the reference is resolved, one of the identifier elements on the resolved resource should match the identifier in the reference. The identifier element in FHIR references can also be used as an additional display element to help human readers locate the information.
References Within Bundles: Using Fast Healthcare Interoperability Resources® (FHIR®) version 4.0.1 resources, is there information on how to resolve references within bundles? (Session 56 - 7/29/21)
- Within a bundle of resources, there are references that can be resolved in the context of that bundle utilizing specific rules. These rules are especially important when posting a transaction requiring a URL to new resources that do not currently have logical ids on the server. The link to Resolving References in Bundles provides a method and rules for resolving references correctly in bundles. There is also a helpful link to contained resources, which provides notes about the use and interpretation of contained resources. In general, you can access, query, and trace bundles and resources with contained elements in Clinical Quality Language.
Slices in Blood Pressure Profile Example: Regarding Slices in Quality Improvement Core (QI-Core) version 4.0.0, how do you get the blood pressure profile in the example? Is it using the name or the code? Because it is not really defined with the condition we are going to follow. Right?
define TestSlices:
[Observation: “Blood Pressure”] BP
let
SystolicBP: singleton from (BP.component C where C.code ~ "Systolic blood pressure"),
DiastolicBP: singleton from (BP.component C where C.code ~ "Diastolic blood pressure")
where SystolicBP.value < 140 'mm[Hg]'
and (DiastolicBP.value < 90 'mm[Hg]'
define TestSlices:
[“observation-bp”] BP
where BP.SystolicBP.value < 140 'mm[Hg]'
and BP.DiastolicBP.value < 90 'mm[Hg]'
(Session 43 - 4/23/20)
define TestSlices:
[Observation: “Blood Pressure”] BP
let
SystolicBP: singleton from (BP.component C where C.code ~ "Systolic blood pressure"),
DiastolicBP: singleton from (BP.component C where C.code ~ "Diastolic blood pressure")
where SystolicBP.value < 140 'mm[Hg]'
and (DiastolicBP.value < 90 'mm[Hg]'
define TestSlices:
[“observation-bp”] BP
where BP.SystolicBP.value < 140 'mm[Hg]'
and BP.DiastolicBP.value < 90 'mm[Hg]'
- The slice is defined in the profile and this profile’s code is Blood Pressure (BP). You would use the print name. It is important to note that typically when we say BP in a measure, we are talking about more than one code. It is a value set with a specified list of codes that constitutes all the values of BP. However, for observation BP, Fast Healthcare Interoperability Resources (FHIR) states “observations shall use LOINC code…” We are relying on the definition of the profile to provide the slice. The bottom expression is saying give me observation BP and then I can treat it like a BP and it behaves and manifests in Clinical Quality Language (CQL) using the slicing. As part of the CQL to ELM translation, the top expression is the output. The underlying Expression Logical Model (ELM) runs against the base FHIR. Therefore, systems do not change what they are doing on the implementation side. This is just an authoring simplification.
Slices - "observation-bp": In regard to Slices in Quality Improvement Core (QI-Core), where do you find the naming to define a new QI-Core profile more directly? How do you define values that you need to put in? For example, how is the “observation-bp” defined and applied?
define TestSlices:
[Observation: “Blood Pressure”] BP
let
SystolicBP: singleton from (BP.component C where C.code ~ "Systolic blood pressure"),
DiastolicBP: singleton from (BP.component C where C.code ~ "Diastolic blood pressure")
where SystolicBP.value < 140 'mm[Hg]'
and (DiastolicBP.value < 90 'mm[Hg]'
define TestSlices:
[“observation-bp”] BP
where BP.SystolicBP.value < 140 'mm[Hg]'
and BP.DiastolicBP.value < 90 'mm[Hg]'
(Session 43 - 4/23/20)
define TestSlices:
[Observation: “Blood Pressure”] BP
let
SystolicBP: singleton from (BP.component C where C.code ~ "Systolic blood pressure"),
DiastolicBP: singleton from (BP.component C where C.code ~ "Diastolic blood pressure")
where SystolicBP.value < 140 'mm[Hg]'
and (DiastolicBP.value < 90 'mm[Hg]'
define TestSlices:
[“observation-bp”] BP
where BP.SystolicBP.value < 140 'mm[Hg]'
and BP.DiastolicBP.value < 90 'mm[Hg]'
- That is a good question. Navigate to the latest published QI-Core Implementation Guide (IG), go to Section 1.2 Contents and click on Profiles. This will take you to the QI-Core Profiles page. If the profile has QI-Core written in front of it, just use the name (e.g., AdverseEvent) as in the example:
2 QI-Core Profiles The table lists the QI-Core profiles that are part of the IG, from which USCore profile they are derived, if any, and the underlying Fast Healthcare Interoperability Resources (FHIR) resources:
QI-Core Profile | USCore Profile | Base Resource |
---|---|---|
QICoreAdverseEvent | AdverseEvent |
However, if the element you are defining is pulled in from an underlying source and does not have QI-Core written in front of it, you would select the element (e.g., FHIR Vital Signs) from the QI-Core Profile table, as in the example below:
2 QI-Core Profiles The table lists the QI-Core profiles that are part of the IG, from which USCore profile they are derived, if any, and the underlying FHIR resources:
QI-Core Profile | USCore Profile | Base Resource |
---|---|---|
FHIR Vital Signs | Observation |
This will take you to the element’s profile page. Scroll to and select the profile name of choice (e.g., Blood pressure systolic and diastolic). This will bring you to the Content tab of the StructureDefinition page. Click on the Differential Table tab to locate the ID of the profile (e.g., observation-vitalsigns).
Side note: As Bryn was answering this question; he realized that he did not have all of the QICore 4.0.0 elements displayed on the StructureDefintion pages. He will post all of the definitions on the Clinical Quality Language Test QICore github page for users. He will submit this as a tracker to surface the name of the profile. The list of elements he will display and track are:
define TestAdverseEvent: ["AdverseEvent"]
define TestAllergyIntolerance: ["AllergyIntolerance"]
define TestBodyStructure: ["BodyStructure"]
define TestCarePlan: ["CarePlan"]
define TestCareTeam: ["CareTeam"]
define TestClaim: ["Claim"]
define TestCommunication: ["Communication"]
define TestCommunicationNotDone: ["CommunicationNotDone"]
define TestCommunicationRequest: ["CommunicationRequest"]
define TestCondition: ["Condition"]
define TestCoverage: ["Coverage"]
define TestDevice: ["Device"]
define TestDeviceNotRequested: ["DeviceNotRequested"]
define TestDeviceRequest: ["DeviceRequest"]
define TestDeviceUseStatement: ["DeviceUseStatement"]
define TestDiagnosticReport: ["DiagnosticReportLab"]
define TestDiagnosticReportNote: ["DiagnosticReportNote"]
define TestEncounter: ["Encounter"]
define TestFamilyMemberHistory: ["FamilyMemberHistory"]
define TestFlag: ["Flag"]
define TestGoal: ["Goal"]
define TestImagingStudy: ["ImagingStudy"]
define TestImmunization: ["Immunization"]
define TestImmunizationEvaluation: ["ImmunizationEvaluation"]
define TestImmunizationNotDone: ["ImmunizationNotDone"]
define TestImmunizationRecommendation: ["ImmunizationRecommendation"]
define TestImplantableDevice: ["USCoreImplantableDeviceProfile"]
define TestLaboratoryResult: ["USCoreLaboratoryResultObservationProfile"]
define TestLocation: ["Location"]
define TestMedication: ["Medication"]
define TestMedicationAdministration: ["MedicationAdministration"]
define TestMedicationAdministrationNotDone: ["MedicationAdministrationNotDone"]
define TestMedicationDispense: ["MedicationDispense"]
define TestMedicationNotDispensed: ["MedicationDispenseNotDone"]
define TestMedicationNotRequested: ["MedicationNotRequested"]
define TestMedicationRequest: ["MedicationRequest"]
define TestMedicationStatement: ["MedicationStatement"]
define TestObservation: ["Observation"]
define TestObservationNotDone: ["ObservationNotDone"]
define TestOrganization: ["Organization"]
define TestPatient: ["Patient"]
define TestVitalsPanel: ["observation-vitalspanel"]
define TestRespRate: ["observation-resprate"]
define TestHeartRate: ["observation-heartrate"]
define TestOxygenSat: ["observation-oxygensat"]
define TestBodyTemp: ["observation-bodytemp"]
define TestBodyHeight: ["observation-bodyheight"]
define TestHeadCircum: ["observation-headcircum"]
define TestBodyWeight: ["observation-bodyweight"]
define TestBMI: ["observation-bmi"]
define TestBP: ["observation-bp"]
define TestSmokingStatus: ["USCoreSmokingStatusProfile"]
define TestPulseOximetry: ["USCorePulseOximetryProfile"]
define TestPediatricBMIForAge: ["USCorePediatricBMIforAgeObservationProfile"]
define TestPediatricWeightForHeight: ["USCorePediatricWeightForHeightObservationProfile"]
define TestPractitioner: ["Practitioner"]
define TestPractitionerRole: ["PractitionerRole"]
define TestProcedure: ["Procedure"]
define TestProcedureNotDone: ["ProcedureNotDone"]
define TestRelatedPerson: ["RelatedPerson"]
define TestServiceNotRequested: ["ServiceNotRequested"]
define TestServiceRequest: ["ServiceRequest"]
define TestSpecimen: ["Specimen"]
define TestSubstsance: ["Substance"]
define TestTask: ["Task"]
Status with Negation: In the draft of the Exclusive Breast Milk Feeding Measure - PC-05 (snippet from EXM9_FHIR4 version 8.1.000), there are some specific changes on how to represent some of the concepts used in this measure within Fast Healthcare Interoperability Resources (FHIR). Specifically, there are some changes around representation of total parenteral nutrition and nutrition intake resource that are planned for R5. When looking at a single newborn with no parenteral nutrition given, generally does status matter if it’s a negation? (Session 39 - 10/24/19)
- The example below states that “there is no evidence that parenteral nutrition was applied because there is no record that it happened.” That’s not a guarantee that it didn’t happen since some occurrences may not be reflected in the database. The statement is merely saying that there is no evidence that parenteral nutrition was applied. We could take it one step further and say we are looking for positive evidence (for example, documentation) that it wasn’t applied, but such documentation rarely, if ever, exists. Appropriate statuses are: in-progress, not-done, on-hold, completed, entered-in-error, stopped, and unknown. Other than entered-in-error and unknown, this set of responses are valid statuses in this context. However, when you get Fast Healthcare Interoperability Resources (FHIR) to indicate the Quality Data Model (QDM) concept of negation rationale, you have to add a status to say it didn’t happen and there is no evidence of it. In the example below, the expression only evaluates that there is no evidence of MedicationAdministration: Parenteral Nutrition. A reason for absence of the activity is not requested. Note, the code below was tested and the updated measure was presented at the 11/4/2019 QDM Weekly Touchpoint meeting.
define "Single Live Birth Encounter Without Galactosemia and Parenteral Nutrition":
"Single Live Birth Encounter" SingleLiveBirthEncounter
without ["MedicationAdministration": "Parenteral Nutrition"] ParenteralNutrition
such that ParenteralNutrition.effective as Period starts during SingleLiveBirthEncounter.period
//where not (Global.EncounterDiagnosis(SingleLiveBirthEncounter).code in "Galactosemia")
where not exists (
(Global.EncounterDiagnosis(SingleLiveBirthEncounter)) EncounterDiagnosis
where EncounterDiagnosis.code in "Galactosemia"
)
Using 'Statement': Where can I find the using statement that references Quality Improvement Core (QI-Core) 4.0.0? (Session 43 - 4/23/20)
- It is located on the Clinical Quality Framework Github repository in the Clinical Quality Language (CQL)-to-Expression Logical Model (ELM) translator. Quality Improvement Core (QI-Core) is one of the models that are in line as resources in that translator.
Value Sets with Procedure Codes: With the Exclusive Breast Milk Feeding Measure - PC-05 (snippet from EXM9_FHIR4 version 8.1.001), currently Breast Milk is a value set with a substance code. In order to use the Procedure resource, can we create a new value set containing all the Procedure codes for Breast Milk? If we have that value set, can we just say procedure breast milk? (Session 39 - 10/24/19)
- If you can find specific procedure codes that say “feeding breast milk” then that would be your procedure, but if you’re looking for exclusive breast milk feeding and no other substances then you would have to find a procedure code for “exclusive breast milk feeding” or indicate without “procedure enteral feeding for any substance other than breast milk.” It will be easier to find a procedure code (or codes) more generic to enteral feeding, meaning feeding through the gastrointestinal track and then indicate a Procedure.usedCode of breast milk substances and indicate no feeding with anything else.
When the Quality Data Model says Substance Administered, empirically we know what that means, but the only substances that Fast Healthcare Interoperability Resources (FHIR) references as administered are those that can be classified as medications. We count total parenteral nutrition like a medication but breast milk isn’t handled that way. Until such time as FHIR includes a way to represent intake and output of substances, the best approach is to use the Procedure resource to reference “feeding” as a procedure and Procedure.usedCode as the substance used to accomplish the feeding procedure. The implementation question and the terminology choice require consideration regarding how the data would be represented in the systems. A procedure that used Enteral Feeding as the code would capture more of the intent. Note, the code below was tested and the updated measure was presented at the 11/4/2019 QDM Weekly Touchpoint meeting.
define "Single Live Birth Encounter With Newborn Fed Breast Milk Only Since Birth":
"Single Live Birth Encounter With Gestational Age 37 Weeks or More" QualifyingEncounter
with ["Procedure": "Enteral Feeding"] BreastMilkFeeding
such that BreastMilkFeeding.status in { 'complete', 'in-progress' }
and BreastMilkFeeding.performed as Period starts during QualifyingEncounter.period
and BreastMilkFeeding.usedCode in "Breast Milk"
//with ["Procedure": usedCode in "Breast Milk"] BreastMilkFeeding
// such that BreastMilkFeeding.status = 'complete'
// and BreastMilkFeeding.performed as Period starts during QualifyingEncounter.period
without ["Procedure": "Enteral Feeding"] OtherFeeding
such that OtherFeeding.status in {'complete', 'in progress'}
and OtherFeeding.performed as Period starts during QualifyingEncounter.period
and not (OtherFeeding.usedCode in "Breast Milk")
//without ["Procedure": usedCode in "Dietary Intake Other than Breast Milk"] OtherFeeding
// such that OtherFeeding.status in {'complete', 'in progress'}
// and OtherFeeding.performed as Period starts during QualifyingEncounter.period
Authoring Patterns - QICore v4.1.1
Authoring Patterns - QICore v5.0.0
Authoring Patterns - QICore v6.0.0
Cooking with CQL Q&A All Categories
Additional Q&A Examples
Developers Introduction to CQL
Specifying Population Criteria