-
Notifications
You must be signed in to change notification settings - Fork 24
Cooking with CQL Q&A Index Measure Logic in CQL 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.
Abbreviation 'Cs' as an alias: In this code, what does the abbreviation Cs mean?
define "Flexible Sigmoidoscopy Performed":
[Procedure: Cs."Flexible Sigmoidoscopy"] FlexibleSigmoidoscopy
where FlexibleSigmoidoscopy.status = 'completed'
(Session 69 - 12/01/22)
define "Flexible Sigmoidoscopy Performed":
[Procedure: Cs."Flexible Sigmoidoscopy"] FlexibleSigmoidoscopy
where FlexibleSigmoidoscopy.status = 'completed'
- The 'Cs', as used in this example, is an alias for the "ColorectalCancerConcepts" library. The "ColorectalCancerConcepts" library references all concepts for colorectal cancer, as seen in the code. Putting all the concepts in their own library allows for swapping out value sets and reusing decision support.
library ColorectalCancerElements version '0.1.0'
using FHIR version '4.0.1'
include FHIRHelpers version '4.0.1'
include FHIRCommon version '4.0.1' called FC
include ColorectalCancerConcepts called Cs
AgeInYearsAt(): Which is recommended; use patient directly or patient.birthdatetime? (Session 29 - 10/25/18)
- For the simplest case of determining the age of the patient in years, use the
AgeInYearsAt()
function directly.
Ambulatory Numerator: In the example below, N (line 8) is the alias of Ambulatory Numerator. Does that definition return a list of the dates?
define "Ambulatory Numerator Days":
"Ambulatory Numerator" N
return date from start of N.relevantPeriod
define OneEncounterPerDay:
"Ambulatory Numerator Days" D
return Last(
"Ambulatory Numerator" N
where N.relevantPeriod starts same day as D
sort by end of relevantPeriod
)
(Session 43 - 4/23/20)
define "Ambulatory Numerator Days":
"Ambulatory Numerator" N
return date from start of N.relevantPeriod
define OneEncounterPerDay:
"Ambulatory Numerator Days" D
return Last(
"Ambulatory Numerator" N
where N.relevantPeriod starts same day as D
sort by end of relevantPeriod
)
- Ambulatory Numerator is an alias in the CQL using list of elements, each described by a QDM “Encounter, Performed”. In the first line of the example, the Ambulatory Numerator Days definition is used to range over the days that had an encounter that ended. Since we are dealing with ambulatory, it’s unlikely that the encounter will be more than one day, but it is a possibility.
Ambulatory Numerator: When using the Quality Data Model (QDM) 5.5 as shown in the example, does this return a date?(Session 43 - 4/23/20)
define "Ambulatory Numerator Days":
"Ambulatory Numerator" N
return date from start of N.relevantPeriod
- Yes, the return is saying for every entry in the "Ambulatory Numerator" N input, return this date from start of N.relevantPeriod output. Since the Ambulatory Numerator is a list of encounters, this expression will be evaluated for each encounter in that list so you’ll get the dates. The result of that expression is then a list of System.Date as shown in the popup:
define "Ambulatory Numerator Days":
"Ambulatory Numerator" N
return date from start of N.relevantPeriod
list<System.Date>
Attribute Result vs Attribute Component: I see there is an attribute result. How does this differ from the attribute component? (Session 25 - 5/31/18)
- The attribute component is the list of results. This example illustrates that. For example, a diagnostic study performed may have a single result or a result and a code. It is a way to represent both components within the QDM datatype.
library StudyComponents
using QDM version '5.3'
valueset "Macular Exam": '1.1' // { ME001: Macular Exam }
valueset "Retinopathy Severity Finding": '2.1' // { F001: Retinopathy Severity }
valueset "Level of Severity of Retinopathy Findings": '3.1' // { Mild: Mild Retinopathy, Moderate: Moderate Retinopathy, Severe: Severe Retinopathy }
valueset "Macular Edema Finding": '2.2' // { F002: Macular Edema Finding }
valueset "Macular Edema Findings Absent": '3.2' // { Absent: No macular edema findings }
valueset "Macular Edema Findings Present": '3.3' // { Present: Macular edema findings present }
parameter "Measurement Period" Interval<DateTime>
context Patient
define "Appropriate Macular Exam":
["Diagnostic Study, Performed": "Macular Exam"] Study
where exists ( Study.components component1
where component1.code in "Macular Exam"
and component1.result in "Level of Severity of Retinopathy Findings"
)
and exists ( Study.components component2
where component2.code in "Macular Exam"
and ( component2.result in "Macular Edema Findings Absent"
or component2.result in "Macular Edema Findings Present"
)
)
define "Retinopathy Findings":
["Diagnostic Study, Performed": "Macular Exam"] Study
where Study.result in "Level of Severity of Retinopathy Findings"
define "Macular Edema Findings":
["Diagnostic Study, Performed": "Macular Exam"] Study
where Study.result in "Macular Edema Findings Absent"
or Study.result in "Macular Edema Findings Present"
Bleeding events encounters: Using Quality Data Model (QDM) 5.5 and encounter-based logic, we are looking for encounters where bleeding events occur during both the encounter and within 5 days following an anticoagulant medication administration. If we use the 'from' and 'let' query, does that eliminate an occurrence issue to ensure that all three things (qualifying encounter, anticoagulant medication administration, and bleeding event diagnosis) occurred on the one encounter? There could be multiple qualifying encounters and some of these could happen across encounters but not all occurring on the same exact encounter. (Session 52 - 3/25/21)
* The keyword 'from' followed by multiple sources of qualifying encounters, bleeding diagnoses, and administered anticoagulant medications specifies a “multi-source query”. In a multi-source query, conceptually, all combinations of elements from each of the input sources are considered. This particular query has 3 sources, so the query ranges over “triples” representing the combined elements from each source. The elements from each source can be referenced within the query by its alias name. The Global "HospitalizationWithObservation" function expands the time constraints of a "Qualifying Encounters" inpatient hospitalization to include an immediately preceding Observation and/or Emergency Department stay. The 'where' clause filters the results to encounters where the bleeding diagnosis started during the “HospitalizationWithObservation” period of the encounter. The where clause also filters the results to encounters where there is an anticoagulant medication administered during the hospitalization period of the encounter and the bleeding diagnosis starts 120 hours or less after the anticoagulant medication was administered. Note that if the where clause does not constrain the results, all possible combinations of the input sources will be returned.define "Encounters With Critical Bleeding Diagnosis":
from
"Qualifying Encounters" QualifyingEncounter,
["Diagnosis": "Bleeding at Critical Site"] BleedingDiagnosis,
["Medication, Administered": "Anticoagulants for VTE Prophylaxis"] AnticoagulantMedication
where BleedingDiagnosis.prevalencePeriod during Global."HospitalizationWithObservation" ( QualifyingEncounter )
and BleedingDiagnosis.prevalencePeriod starts 120 hours or less after AnticoagulantMedication.relevantDatetime
and AnticoagulantMedication.relevantDatetime during Global."HospitalizationWithObservation" ( QualifyingEncounter )
return QualifyingEncounter
Blood Pressure as an Observation vs Component of an Observation: Is blood pressure identified as an observation or as a component of an observation? (Session 28 - 9/27/18)
- For blood pressure, there is a physical exam result or components result and the challenge depends on what you're looking for. It is a single observation, but it depends on how it is recorded.
CalculateAgeInYearsAt(): For the calculate age in years expression, do we have to add the date from patient birth time and the date from start of measurement period? (Session 29 - 10/25/18)
CalculateAgeInYearsAt(date from Patient.birthDatetime, date from start of "Measurement Period")
Casting with choice statements in Clinical Quality Language: When casting with choice statements in Clinical Quality Language, in the if-then statement, the if statement value is set to a quantity, why is there a redundant statement to express the quantity as an integer function?
/*
@description: Returns the value, interpreted as an Integer
@comments: If the input is a Quantity, the result is the same as invoking `ToInteger(value as Quantity)`.
If the input is an Integer, the result is the same as invoking `value as Integer`
*/
define function ToInteger(value Choice<Integer, Quantity>):
if value is Quantity then
ToInteger(value as Quantity)
else
value as Integer
(Session 68 - 10/27/22)
/*
@description: Returns the value, interpreted as an Integer
@comments: If the input is a Quantity, the result is the same as invoking `ToInteger(value as Quantity)`.
If the input is an Integer, the result is the same as invoking `value as Integer`
*/
define function ToInteger(value Choice<Integer, Quantity>):
if value is Quantity then
ToInteger(value as Quantity)
else
value as Integer
- In this CQL snippet, “ChoiceToInteger” accepts data value with a choice of integer or quantity. If not a quantity, it accepts the input attribute as an integer. If a quantity, it calls the QuantityToInteger function, which tests the value of quantity for appropriateness of conversion to integer and the unit as ‘1,’ which is the default Unified Code for Units of Measure. If both are suitable, it converts the quantity.value to an integer.
/*@description: Returns the value of the given Quantity, interpreted as an Integer if it is safe to do so.
@comments: A Quantity value can be safely interpreted as an integer if it has no decimal component (i.e. zeros after the decimal),
and it has the default UCUM unit of '1'*/
define function "QuantityToInteger"(value Quantity):
case
when Abs(value.value - Truncate(value.value))> 0.00000001 then Message(null, true, 'ToInteger.InvalidArgument', ErrorSeverity, 'The quantity has a non-zero decimal component and cannot be safely interpreted as an Integer')
when value.unit != '1' then Message(null, true, 'ToInteger.InvalidUnit', ErrorSeverity, 'The quantity has non-default units specified and cannot be safely interpreted as an Integer')
else Truncate(value.value)
end
/*@description: Returns the value, interpreted as an Integer
@comments: If the input is a Quantity, the result is the same as invoking `ToInteger(value as Quantity)`.
If the input is an Integer, the result is the same as invoking `value as Integer`*/
define function "ChoiceToInteger"(value Choice<Integer, Quantity>):
if value is Quantity then "QuantityToInteger"(value as Quantity)
else value as Integer
Clinical Quality Language evaluation service: When running Clinical Quality Language (CQL) for clinical decision support, does the evaluation service remember the calculation, like a RESTful server? (Session 69 - 12/01/22)
- The ability to remember the calculation is dependent on the service. Typically, the servers are stateless and do not have the ability to remember a calculation. However, within an evaluation, calculations can be cached but would not be retained for future access once the evaluation is completed.
CumulativeMedicationsDuration - Atom plug-in for Medication Dispense Data: When calculating the Cumulative Medication Duration using Medication Dispense data, are the functions all executed using the Atom plug-in for CQL? Do you tell the Atom plug-in what version of CQL or execution engine to run? (Session 59 - 12/09/21)
- Yes, the Cumulative Medication Duration is calculated with the Atom plug-in for CQL. The Atom plug-in downloads a translator to support the language, compiling, and error messages as well as an evaluator to support running the test. There is also the same Atom plug-in for Visual Studio Code if that is your preferred environment.
CumulativeMedicationsDuration - authorDatetime vs date: When calculating a CumulativeMedicationsDuration version ‘0.3.000’ library in the Quality Data Model (QDM) version 5.6, is the authorDatetime function the same as the date the provider writes the prescription? (Session 57 - 9/16/21)
- In QDM, the authorDatetime is the time of entry of the data element into the clinical software. In Fast Healthcare Interoperability Resources® (FHIR), it is preferred to use the relevantPeriod, if it is present. The relevantPeriod is the time period the dispense is expected to cover. If the relevantPeriod is not present, then use the authorDatetime to calculate the start date and time for the prescription.
define function "MedicationOrderPeriod"(Order "Medication, Order" ):
if Order.relevantPeriod.low is null and Order.authorDatetime is null then
null
else if Order.relevantPeriod.high is not null then
Interval[date from Coalesce(Order.relevantPeriod.low, Order.authorDatetime), date from end of Order.relevantPeriod]
else
(
Coalesce(
Order.daysSupplied,
Order.supply.value / (Order.dosage.value * ToDaily(Order.frequency))
) * (1 + Coalesce(Order.refills, 0))
) durationInDays
let startDate: date from Coalesce(Order.relevantPeriod.low, Order.authorDatetime)
return
if durationInDays is not null then
Interval[startDate, startDate + Quantity { value: durationInDays, unit: 'day' }]
else
null
CumulativeMedicationsDuration - calculating age and evaluating it against two integers: When calculating age and evaluating it against two integers using CumulativeMedicationsDuration version ‘0.3.000’ library in the Quality Data Model (QDM) version 5.6, can we use a closed bracket instead of an open parenthesis? (Session 57 - 9/16/21)
- You can use a closed bracket. In Clinical Quality Language (CQL), a closed bracket is an inclusive boundary and the parenthesis is an open boundary.
Example with an open parenthesis - in this example, the bracket before 12 and parentheses after 20 indicates the interval is all values >= 12 and <20. 20 is not included.
define "Stratification 3 (74) (Global v7)":
AgeInYearsAt(date from start of "Measurement Period" ) in Interval[12, 20 )
define "Initial Population (74)":
exists ( ["Patient Characteristic Birthdate": "Birth date"] BirthDate
where Global."CalendarAgeInMonthsAt" ( BirthDate.birthDatetime, start of "Measurement Period" ) >= 6
and Global."CalendarAgeInYearsAt" ( BirthDate.birthDatetime, start of "Measurement Period" ) < 20
)
and exists ( "Qualifying Encounters" )
Example with a closed bracket - in this example, the bracket before 12 and bracket after 19 indicates the interval is all values >=12 and <=19.
define "Stratification 3 (74) (Global v7)":
AgeInYearsAt(date from start of "Measurement Period" ) in Interval[12, 19 ]
define "Initial Population (74)":
exists ( ["Patient Characteristic Birthdate": "Birth date"] BirthDate
where Global."CalendarAgeInMonthsAt" ( BirthDate.birthDatetime, start of "Measurement Period" ) >= 6
and Global."CalendarAgeInYearsAt" ( BirthDate.birthDatetime, start of "Measurement Period" ) <= 19
)
and exists ( "Qualifying Encounters" )
CumulativeMedicationsDuration - Collapse Interval per day: In the Cumulative Medication Duration expression (collapse Interval per day), does the per day need to be declared? (Session 59 - 12/09/21)
define function CumulativeDuration(Intervals List<Interval<Date>>):
Sum((collapse Intervals per day) X return all difference in days between start of X and end of X) + 1
- The per day interval needs to be declared or it will collapse at the least granular interval in the expression. For example, if you have intervals per minute, it will collapse the interval to the minute so specifying per day is important. Otherwise, the logic will consider dates on sequential days as gaps. For example, if the event ends at 10 AM on Saturday and starts at 10 AM on Sunday, from a day perspective, those meet, equal one day and would collapse. If the calculation were performed per minute, there would be a gap of 24 hours.
CumulativeMedicationsDuration - combined and split doses: Does the Quality Data Model (QDM) version 5.6 allow for combined or split doses when calculating CumulativeMedicationsDuration version ‘0.3.000’ library logic? (Session 57 - 9/16/21)
- In QDM version 5.6, the medication order is not able to accommodate a combined or split dose. There is representation in Fast Healthcare Interoperability Resources® (FHIR) for ordering a split dose, but in FHIR-based calculations, currently there is a restriction to one dosage level, or unit dose, to align with the capability within QDM. This needs to be extended in the future.
CumulativeMedicationsDuration - date vs date/time: If using the date instead of date/time when calculating CumulativeMedicationsDuration version ‘0.3.000’ library in the Quality Data Model (QDM) version 5.6, do you still have to add a quantity or can just the integer be used in the calculation? (Session 57 - 9/16/21)
- You must include the quantity since the integer may be days, weeks, or months.
CumulativeMedicationsDuration - date/time vs date: In the CumulativeMedicationsDuration version ‘0.3.000’ library, is it important to return date/time as opposed to just date? (Session 57 - 9/16/21)
- The HL7 Clinical Quality Information and Pharmacy Workgroups will discuss the use of date, rather than date/time at the HL7 Working Group Meeting session on Thursday, January 23, 2022. Prescription data is generally not at the time level so there would be no advantage to returning date/time. The only scenario where returning date/time that might be helpful is in inpatient scenarios where there is medication administration data. However, the intent of this logic is for use in outpatient prescription data where most use cases are oral medications.
CumulativeMedicationsDuration - data model: Is the data model used to create the MedicationRequestPeriod function in the cumulative medication duration Fast Healthcare Interoperability Resource (FHIR®) 4.0.1 CQL library part of US Core profile? (Session 59 - 12/09/21)
- Yes, the model used is based on the US Core 4.0.0 profile, which is based on FHIR® 4.0.1 specification. Information regarding the US Core Implementation Guide is located at URL hl7.org/fhir/us/core.
CumulativeMedicationsDuration - MedicationRequestPeriod Function: The MedicationRequestPeriod function in the cumulative medication duration CQL library is using Fast Healthcare Interoperability Resource (FHIR®) 4.0.1 but not US Core 4.0.0, is this correct? (Session 59 - 12/09/21)
- Correct. There is still some further investigation and tooling development needed to provide the essential capabilities to reference US Core, QI Core, and other implementation guides directly. Eventually you would have the ability to change the expression from FHIR® to US Core, QI Core, or whatever implementation guide is appropriate.
CumulativeMedicationsDuration - metric dosing (mg/dose) instead of tablets or capsules: When calculating CumulativeMedicationsDuration using version ‘0.3.000’ library in the Quality Data Model (QDM) version 5.6, should we use metric dosing (mg/dose) instead of tablets or capsules? (Session 57 - 9/16/21)
- In QDM version 5.6, you can express the current dosage used for calculating CumulativeMedicationsDuration version ‘0.3.000’ library in metric or tablets/capsules as long as you express the supply and dosage in the same units.
CumulativeMedicationsDuration - tapering medications: How does the CumulativeMedicationsDuration version ‘0.3.000’ library in the Quality Data Model (QDM) version 5.6 work with tapering medications? (Session 57 - 9/16/21)
- Currently, the CumulativeMedicationsDuration version ‘0.3.000’ library does not handle tapering. The CumulativeMedicationsDuration version ‘0.3.000’ library provides cumulative medication duration calculation logic for use with QDM prescription, discharge, administration, and dispensing events and the logic assumes single-instruction dosing information. Since providers do not typically write tapers as multiple prescriptions with one dosing each, the taper needs multiple dosage instructions and needs to be handled explicitly. Right now, we are hesitant to write the logic without understanding what the representation would be in Fast Healthcare Interoperability Resources® (FHIR). Examples of real-world, tapered prescription data would help us write the code because it would show how a tapered dose coming from an electronic health record would look in FHIR. Tapering is the next use case for development in the CumulativeMedicationsDuration version ‘0.3.000’ library.
Change of Status from Outpatient to Inpatient: Does the logic shown cover a scenario where the patient goes from outpatient status directly to inpatient?
define function "Hospitalization with Observation"(Encounter "Encounter, Performed" ):
Encounter E
let
ObsVisit: Last(["Encounter, Performed": "Observation"] O
where O.relevantPeriod ends 1 hour or less on or before start of E.relevantPeriod
sort by end of relevantPeriod),
VisitStart: Coalesce(start of ObsVisit.relevantPeriod, start of E.relevantPeriod),
EDVisit: Last(["Encounter, Performed": "Emergency Department Visit"] ED
where ED.relevantPeriod ends 1 hour or less on or before VisitStart
sort by end of relevantPeriod
)
return Interval[Coalesce(start of EDVisit.relevantPeriod, VisitStart), end of E.RelevantPeriod]
(Session 28 - 7/26/18)
define function "Hospitalization with Observation"(Encounter "Encounter, Performed" ):
Encounter E
let
ObsVisit: Last(["Encounter, Performed": "Observation"] O
where O.relevantPeriod ends 1 hour or less on or before start of E.relevantPeriod
sort by end of relevantPeriod),
VisitStart: Coalesce(start of ObsVisit.relevantPeriod, start of E.relevantPeriod),
EDVisit: Last(["Encounter, Performed": "Emergency Department Visit"] ED
where ED.relevantPeriod ends 1 hour or less on or before VisitStart
sort by end of relevantPeriod
)
return Interval[Coalesce(start of EDVisit.relevantPeriod, VisitStart), end of E.RelevantPeriod]
- Yes, because there would be no observation or ED visit. The outpatient visit can be compared to the start of the encounter’s relevant period.
Coalesce with Data Types and Attributes: Did you have to use Coalesce since you are using two different data types or are the attributes the same? (Session 22 - 2/22/18)
- Coalesce is used when the attributes differ to give preference to a particular value if it is present. For Procedure, Performed, we have both an authorDatetime and a relevantPeriod, but for Immunization, Administered, we only have an authorDatetime, so the Coalesce in this case is saying “Use the start of the relevantPeriod if it’s present, otherwise use the authorDatetime.”
Coalesce Patterns: Is there a preferred pattern to use if not using Coalesce? (Session 22 - 2/22/18)
- It depends how often the definition needs to be used in subsequent logic. If the definition is used often, and the Coalesce pattern is required for each usage, then it makes sense to change the definition so that it returns the same kinds of tuples, so that subsequent references can just reference a single attribute and not have to use the Coalesce pattern.
Combined Numerators for the Linear Combination Options: When calculating the combined numerators for the linear combination options, does that mean one patient could be counted in two or more events depending on exclusions? Is that what this composite measure is looking for?
* V2_Risk-standardized Major Bleeding Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
* V2_Risk-standardized Venous Thromboembolism (VTE) Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
Option 1. linear combination:
Performance Rate = (Numerator 1 + Numerator 2) / (Denominator – Denominator Exclusions)
Option 2. Any-Or-None
Performance Rate = (Numerator 1 + Numerator 2 - number with both) / (Denominator – Denominator Exclusions)
(Session 47 - 10/01/20)
* V2_Risk-standardized Major Bleeding Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
* V2_Risk-standardized Venous Thromboembolism (VTE) Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
Option 1. linear combination:
Performance Rate = (Numerator 1 + Numerator 2) / (Denominator – Denominator Exclusions)
Option 2. Any-Or-None
Performance Rate = (Numerator 1 + Numerator 2 - number with both) / (Denominator – Denominator Exclusions)
- Yes, that is why we have to switch to the units of analysis of VTE and bleeding events rather than patients because if we just say it is the patient measure population then we only get one count per patient even though what we are looking for is a VTE or bleeding rate so we have to expand the basis to let us count that. Patients can be counted multiple times based on the events.
Combining Several Definitions into 1: For the measure CMS646, Bladder Cancer (https://oncprojectracking.healthit.gov/support/browse/CQLIT-206), how do you express specific references for bladder cancer and combine several definitions into one? (Session 40 - 12/5/19)
- The clinical workflow presented by the measure developer is that bladder cancer is diagnosed and then the tumor is staged and coded with either Ta HG (noninvasive papillary carcinoma – high grade), Tis (carcinoma in situ), or T1 (tumor has spread to the connective tissue). Bacillus Calmette-Guerin (BCG) is the standard immunotherapy drug (regardless of tumor stage) for bladder cancer and will be administered up to six times, but for this measure we are only interested in the first administration. The intent of this measure is to assure that staging occurs prior to the first BCG administration. The measure only looks at the first BCG administration, not at BCG dose/frequency/etc., even if the staging occurred up to six months prior; we are interested in the first encounter after the bladder cancer diagnosis within the measurement period. Note, the diagnosis must overlap the measurement period. The first BCG given is associated with the staging and obviously the encounter. We are specifically checking that the BCG administered happened after the staging.
The suggested approach is to look for any qualifying encounter that occurred during the submission period and we define Qualifying Encounter, Initial Population, Bladder Cancer Staging, Bladder Cancer Diagnosis, and First BCG Administered as:
Qualifying Encounter:
define "Qualifying Encounter":
["Encounter, Performed": "Office Visit"] Encounter
where Encounter.relevantPeriod during "Measurement Period"
Initial Population:
define "Initial Population:
exists ( "Bladder Cancer Diagnosis" )
and "Most Recent Bladder Cancer Staging Tumor Ta HG, Tis, T1"
and exists "Qualifying Encounter"
Bladder Cancer Staging:
Define "Most Recent Bladder Cancer Staging Tumor Ta HG, Tis, T1":
"Bladder Cancer Staging".result ~ "T1: Tumor invades lamina propria or submucosa (finding)"
or "Bladder Cancer Staging".result ~ "Ta: Noninvasive papillary carcinoma (urinary tract) (finding)"
or "Bladder Cancer Staging".result in ( "Bladder Cancer Staging Tis for Urology Care" )
Bladder Cancer Diagnosis:
Define "Bladder Cancer Diagnosis":
["Diagnosis": "Bladder Cancer for Urology Care"] BladderCancer
where BladderCancer.prevalencePeriod starts same day or before
end "Measurement Period"
First BCG Administered:
define "First BCG Administered":
First(["Medication, Administered": "BCG Bacillus Calmette Guerin for Urology Care"] BCG
with "Bladder Cancer Diagnosis" BladderCancer
such that BCG.relevantPeriod starts 6 months or less after start BladderCancer.prevalencePeriod
and BCG.relevantPeriod overlaps "Measurement Period"
sort by start of relevantPeriod
)
Solutions:
- Use exists() function to provide Boolean (true/false) results for Initial Population definitions.
- Remove singleton from office visit expression since timing requirements addressed by other definitions.
- Use ~ equivalence function for code but use in() function to Value Set Authority Center object identifiers for tumor staging definition.
Composite Measure Scoring: We are attempting to determine the most appropriate composite measure scoring type to use for a performance measure we are considering. We are considering two options, 1) Linear combination, and 2) Any-or-none. However, given the fact that the measures are component measures, then we cannot go with option 1 because it only applies to individual measure components. Is that correct?
* V2_Risk-standardized Major Bleeding Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
* V2_Risk-standardized Venous Thromboembolism (VTE) Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
Option 1. linear combination:
Performance Rate = (Numerator 1 + Numerator 2) / (Denominator – Denominator Exclusions)
Option 2. Any-Or-None
Performance Rate = (Numerator 1 + Numerator 2 - number with both) / (Denominator – Denominator Exclusions)
(Session 47 - 10/01/20)
* V2_Risk-standardized Major Bleeding Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
* V2_Risk-standardized Venous Thromboembolism (VTE) Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
Option 1. linear combination:
Performance Rate = (Numerator 1 + Numerator 2) / (Denominator – Denominator Exclusions)
Option 2. Any-Or-None
Performance Rate = (Numerator 1 + Numerator 2 - number with both) / (Denominator – Denominator Exclusions)
- This question was a primary focus of this Cooking with CQL Session, and is answered in full in the composite measure walkthrough. The summary answer is that in a proportion measure, each case can contribute at most once to the numerator, so you can combine component measures with an “OR” or an “AND”, but not with a “+”, because that would require counting the case in the denominator more than once. See the linear combination approaches in the examples for this session for how to express these.
Computation of Measures:If there are differences regarding the computation of measures, can you walk through how the measure is computed and point out differences? (Session 23 - 3/29/18)
*We will add this as a topic for a future Cooking with CQL session.
Contraception.code: “Most or Moderately Effective Contraception Provided” Contraception Return Contraception.code This will return different types of contraceptives including counseling. Do they need to be listed independently or can a definition be used in the supplemental element? (Session 26 - 6/28/18)
- The supplemental data element definition can just return the code, even though that will, in general, return multiple different types of contraceptives. For each actual instance, the specific code in the reporting system will be returned. In a summary report, these would be aggregated by code. In an individual report, the data for this individual would be returned.
Define Function: Can the ‘define function’ pattern be used to customize measures where an outpatient visit is not to be considered as an episode of care? (Session 28 - 7/26/18)
- Yes, in that case, you would use the Hospitalization with Observation function rather than the Hospitalization with Outpatient function. In addition, measures that did not consider observation status to be part of the episode of care would continue to use the current Hospitalization function.
D.code reference in "Venous Foot Pumps (VFP)": In this Quality Improvement-Core negation example, is the reference to D.code in “Venous Foot Pumps (VFP)” referencing a value set, or is it referencing a direct reference code?
define TestGeneralDeviceNotRequestedExplicit:
["DeviceNotRequested"] D
where D.code in "Venous Foot Pumps (VFP)"
or D.code ~ "Venous Foot Pumps (VFP)"
(Session 67 - 9/29/22)
define TestGeneralDeviceNotRequestedExplicit:
["DeviceNotRequested"] D
where D.code in "Venous Foot Pumps (VFP)"
or D.code ~ "Venous Foot Pumps (VFP)"
- In this example, “Venous Foot Pumps (VFP),” the DeviceNotRequested.code element is a choice of either a value set, or a coded value. Because the data being accessed can represent “what is being negated” as either a coded value or a value set reference, the logic needs to account for both possibilities. The first part of the where clause ‘D.code in “Venous Foot Pumps (VFP)” is treating D.code as a value set, and the second part of the where clause ‘D.code ~ “Venous Foot Pumps (VFP)” is treating D.code as a coded value and asking if that coded value is in the Venous Foot Pumps (VFP) value set.
Denominator Exclusions in the Patient-Based Linear Combination Formula: Regarding the denominator exclusions in the patient-based linear combination formula, are the cases in the component individual numerators mutually exclusive?
* V2_Risk-standardized Major Bleeding Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
* V2_Risk-standardized Venous Thromboembolism (VTE) Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
Option 1. linear combination:
Performance Rate = (Numerator 1 + Numerator 2) / (Denominator – Denominator Exclusions)
Option 2. Any-Or-None
Performance Rate = (Numerator 1 + Numerator 2 - number with both) / (Denominator – Denominator Exclusions)
(Session 47 - 10/01/20)
* V2_Risk-standardized Major Bleeding Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
* V2_Risk-standardized Venous Thromboembolism (VTE) Rate
Following Elective Primary Total Hip Arthroplasty (THA)
and/or Total Knee Arthroplasty (TKA) Electronic Clinical Quality Measure (eCQM)
Option 1. linear combination:
Performance Rate = (Numerator 1 + Numerator 2) / (Denominator – Denominator Exclusions)
Option 2. Any-Or-None
Performance Rate = (Numerator 1 + Numerator 2 - number with both) / (Denominator – Denominator Exclusions)
- No, not necessarily. The exclusions that are specific for each event are excluding specific events that could be considered a false positive. For example, exclusions that are specific to venous thromboembolism (VTE) that would otherwise show up as VTE, but are not necessarily VTE. This is the same for bleeding rate. Exclusions for VTE rate include a diagnosis of VTE 30 days prior to admission so the exclusion for VTE may still be valid in looking for bleeding, but the exclusion for VTE would not be valid in looking for VTE.
Denominator in Patient-based and Non-Patient-based Measures: Is the statement, “The denominator is always a subset of the initial population, so the criteria entered to define ‘Initial Population’ does not have to be duplicated for define ‘Denominator’” only true for patient-based measures? (Session 24 - 4/26/18)
- No, it is true for both patient-based and non-patient based measures. However, for non-patient based measures, because the criteria expression has to return the same type as the rest of the measure population criteria, it is often easiest to just reference the “Initial Population”.
Diagnosis Appears in Both the Encounter Diagnosis and in the Present on Admission Indicator and a Diagnosis Record: In the example measure on Pressure Injury, how would the code for diagnosis display the information if the diagnosis appears in both the Encounter diagnosis and in the Present on Admission indicator and a diagnosis record? It seems that you would say there is a diagnosis record and it has the same code as the diagnosis code in the encounter diagnosis. (Session 36 - 6/27/19)
- When you’re talking about a pressure ulcer like this, the location is not necessarily in a pre-coordinated SNOMED term, it’s just pressure ulcer, but there are some ICD-10 codes that have a location attached. The Encounter diagnosis attribute includes some components, but it doesn’t specify anatomical location as a component. However, the CQL expression could reference a diagnosis with anatomical location = right (or left) and that diagnosis.id could be used to reference the Encounter diagnosis. Thus, the CQL can allow further definition of the Encounter diagnosis to address laterality, as needed, for this measure. Alternatively, you could use the pre-coordinated code (i.e., ICD-10 with location attached).
You can incorporate a definition for Pressure Injury Diagnosis and would name the anatomical location site with Pressure Injury Diagnosis as part of the New Pressure Injury measure.
[“Diagnosis”: “Pressure Injury”]
“Denominator” EncounterWithQualityingAge
with “Pressure Injury Diagnosis” PressureInjury
such that PressureInjury.prevalencePeriod during EncounterWithQualifyingAge.relevantPeriod
and exists (“Pressure Injury Diagnosis” SeparateInjury
where separateInjury.prevelancePeriod during EncounterWithQualifyingAge.relevancePeriod and
PressureInjury.anatomicalSiteLocation !~ SeparateInjury.anatomicalSiteLocation
Difference in meaning between two General Not Done defining statements: Using this Quality Improvement-Core negation example, what is the difference in the meaning between the two General Not Done defining statements?
define TestGeneralDeviceNotRequested:
["DeviceNotRequested": "Venous Foot Pumps (VFP)"]
define TestGeneralDeviceNotRequestedActual:
["DeviceNotRequested": code in "Venous Foot Pumps (VFP)"]
union ["DeviceNotRequested": code ~ "Venous Foot Pumps (VFP)"]
(Session 67 - 9/29/22)
define TestGeneralDeviceNotRequested:
["DeviceNotRequested": "Venous Foot Pumps (VFP)"]
define TestGeneralDeviceNotRequestedActual:
["DeviceNotRequested": code in "Venous Foot Pumps (VFP)"]
union ["DeviceNotRequested": code ~ "Venous Foot Pumps (VFP)"]
- The first expression is a retrieve of any negated DeviceRequest, whether that request is indicating a specific device was not ordered (i.e., represented with a code) or indicating that none of a class of devices was ordered (i.e., represented with a value set). This first example is the best practice for retrieving general negation statements. The second example is just an illustration of how the first example is expanded for implementation to support the different approaches to representing negation.
Direct reference code in negation: Regarding negation in Quality Improvement-Core and Specific Not Done, is a direct reference code allowed in negation? (Session 67 - 9/29/22)
- Supporting direct-reference codes in negation criteria is on the roadmap, but not currently supported. To use a single code, add it to a value set and use the value set negation criteria approach.
Encounter with Pressure Injury: CMS826v0 - Hospital Harm - Pressure Injury – How would you express an encounter with a new harmful pressure injury? (Session 40 - 12/5/19)
- Paraphrased description of the measure: This measure assesses the proportion of inpatient encounters of patients 18 years of age or older at admission, who have a pressure ulcer present on admission (POA) and suffer the harm of developing a new pressure injury (stage 2, stage 3, stage 4, deep tissue pressure injury, or nonstageable pressure injury) subsequent to admission. The measure does not consider the number of new pressure injuries per encounter, it is only concerned about a new pressure injury in patients admitted with an existing pressure injury. Thus, the measure must identify a pressure injury that is POA and a new pressure injury in a different anatomical location identified at least 24 hours after arrival. This new harmful pressure injury’s prevalence period overlaps, but starts after an existing pressure ulcer.
For this particular encounter, the measure is looking for the presence of an Encounter diagnosis consistent with codes in a specific value set ‘diagnosis: Pressure Ulcer Stage’ with a ‘Pressure injury stage’ (e.g., containing values: stage 2, stage 3, stage 4, nonstageable pressure ulcer, pressure injury of deep tissue). This approach will set up an alias for pressure injury POA where that diagnosis (harmful pressure injury) has a POA indicator. Next, we are looking for a ‘harmful pressure injury’ during the hospitalization period where the anatomical location site is not the same as the one that was POA and it starts more than 24 hours after the start of hospitalization.
Option 1 (using ‘let’):
define "Encounter With New Harmful Pressure Injury":
"Denominator" EncounterWithQualifyingAge
let PressureInjuryPOA:
"Harmful Pressure Injury" PressureInjury
where PressureInjury.prevalencePeriod
during "Hospitalization, Potentially Starting in Emergency Department and
or with Observation"(EncounterWithQualifyingAge)
and exists (
EncounterWithQualifyingAge.diagnoses EncounterDiagnosis
where EncounterDiagnosis.code ~ PressureInjury.code // Best
approximation in QDM, no way to link encounter diagnoses directly to Diagnosis elements
and EncounterDiagnosis.presentOnAdmissionIndicator ~ "y"
)
where exists ("Harmful Pressure Injury" PressureInjury
where PressureInjury.prevalencePeriod during "Hospitalization, Potentially Starting in Emergency
Department and or with Observation"(EncounterWithQualifyingAge)
and AllTrue(
PressureInjuryPOA POA
where PressureInjury.prevalencePeriod starts more than 24 hours after
start of "Hospitalization, Potentially Starting in Emergency Department and or with Observation"
(EncounterWithQualifyingAge)
and PressureInjury.anatomicalLocationSite !~
POA.anatomicalLocationSite
return true
)
)
Option 2 (using a ‘with’ statement in place of the ‘let’ and include a ‘such that’ statement, only if there is one condition):
define "Encounter With New Harmful Pressure Injury":
"Denominator" EncounterWithQualifyingAge
with (
"Harmful Pressure Injury" PressureInjury
where PressureInjury.prevalencePeriod
during "Hospitalization, Potentially Starting in Emergency Department and or with
Observation"(EncounterWithQualifyingAge)
and exists (
EncounterWithQualifyingAge.diagnoses EncounterDiagnosis
where EncounterDiagnosis.code ~ PressureInjury.code // Best
approximation in QDM, no way to link encounter diagnoses directly to Diagnosis elements
and EncounterDiagnosis.presentOnAdmissionIndicator ~ "y"
)
) PressureInjuryPOA
such that exists ("Harmful Pressure Injury" PressureInjury
where PressureInjury.prevalencePeriod during "Hospitalization,
Potentially Starting in Emergency Department and or with Observation"(EncounterWithQualifyingAge)
and AllTrue(
PressureInjuryPOA POA
where PressureInjury.prevalencePeriod starts more
than 24 hours after start of "Hospitalization, Potentially Starting in Emergency Department and or with Observation"
(EncounterWithQualifyingAge)
and PressureInjury.anatomicalLocationSite
!~ POA.anatomicalLocationSite
return true
)
)
The difference between the ‘with’ and ‘let’ is that the ‘let’ introduces “PressureInjuryPOA” so you can talk about it in multiple places throughout the query, whereas the ‘with’ only lets you talk about it within ‘such that’. If you need to talk about it in a subsequent ‘with’ or ‘where’ then the ‘let’ permits you to repeatedly talk about the same expression. The ‘with’ only references the Pressure Injury POA within the ‘such that’ condition. The scope of the ‘let’ variable goes on after the initial ‘where’ or filtering that is done. As far as performance, expressing it as a ‘with’ or a ‘let’ depends on how the underlying implementation decides to deal with it because it might translate into a ‘join’ in a structured query language database environment vs. an ‘apply’. It is up to the optimizer of the target system to determine how best to run it.
Regardless of whether the expression uses ‘let’ or ‘with’, the following issue may represent a challenge in testing and evaluating this measure. A potential challenge with this approach is the terminology used when recording an anatomical site. For example, if the anatomical location site is recorded as hip ulcer, but didn’t specify laterality (right or left) and then a new hip ulcer developed on the other hip, the straight code comparison here wouldn’t differentiate the two. Conversely, if the existing ulcer code indicates “greater trochanter,” and a separate reference to the same pressure ulcer used the code “hip,” the result might suggest there are two pressure ulcers when only one exists. Thus coding variation could lead to missing a new pressure ulcer or over-counting an existing pressure ulcer as newly developed. A possible solution would be to develop very specific value sets that distinguish the encounter.
Hepatitis B Immunizations Procedure - Value Set or Definition?: Is the Hepatitis B immunizations procedure a value set or definition? (Session 22 - 2/22/18)
- It is a definition. Value set declarations are defined in the CQL header and are local identifiers for value sets stored in a terminology service like the Value Set Authority Center. Definitions are defined in the body of the CQL library and specify chunks of logic that can be used throughout the library or within the measure as population criteria.
Median Admit Decision Time to ED Departure Time for Admitted Patients: CMS111, Median Admit Decision Time to ED Departure Time for Admitted Patients (https://oncprojectracking.healthit.gov/support/browse/CQLIT-207), we are having a measure observation calculation issue, where some duration calculation results are based on the ending interval (end time of encounter). If the emergency department (ED) encounter does not include the facility location, it should not calculate a "Visit" encounter for the measure observation time. If the ED facility location departure time is missing, a measure observation time should not be calculated, but in Bonnie this does occur. Is this due to the logic below? Should we add a null clause in the Initial Population to indicate if there is no ED facility location departure time the encounter will not be included?(Session 40 - 12/5/19)
Global.HospitalizationLocations (Encounter "Encounter, Performed") Encounter Visit
let EDVisit: Last(["Encounter, Performed": "Emergency Department Visit"] LastED
where LastED.relevantPeriod ends 1 hour or less on or before start of
Visit.relevantPeriod
sort by end of relevantPeriod)
return if EDVisit is null then Visit.facilityLocations
else flatten
{ EDVisit.facilityLocations, Visit.facilityLocations }
- The measure intent is to capture the median time interval from when the admit decision is made (represented by an AssessmentPerformed.relevantDatetime or by an AdmitOrder.authorDatetime) to when the patient leaves the ED (represented by the end of HospitalLocation.locationPeriod).
define function "AdmitDecisionUsingAssessment"(Encounter "Encounter, Performed" ):
Last(["Assessment, Performed": "ED Evaluation"] EDEvaluation
where EDEvaluation.relevantDatetime during "RelatedEDVisit"(Encounter).relevantPeriod
and EDEvaluation.result in "Admit Inpatient"
sort by relevantDatetime
)
The “EDDepartureTime”(“RelatedEDVisit”(Encounter)) is represented by the end of HospitalLocations.locationPeriod. However, if HospitalLocations.code is present (ED Department), but the end of the locationPeriod is missing (null), then the time interval calculation would continue to infinity. If the ED Location EndTime was not noted, the measure performs the calculation until infinity, or the end of time, instead of to zero. To address this calculation issue, the initial encounter is expressed so that it includes an EDEncounter code and could not be null for the EDDepartureTime. In this case, the only ones passing through the measure would have an observation measure time.
define “Initial Population”:
Global."Inpatient Encounter" EncounterInpatient
with "ED Encounter with Decision to Admit" EDAdmit
such that EDAdmit.relevantPeriod 1 hour or less before or on start of EncounterInpatient.relevantPeriod
define “ED Encounter with Decision to Admit”:
( ( Global."ED Encounter" EDVisit where "AdmitDecisionUsingAssessmentDuringRelatedEDBeforeDeparture"
(EDVisit).relevantDatetime
during EDVisit.relevantPeriod)
union ( Global."ED Encounter" EDVisit where "AdmitDecisionUsingEncounterOrderDuringRelatedEDandBeforeDeparture"
(EDVisit).authorDatetime
during EDVisit.relevantPeriod
) ) EDVisitAdmit
where EDVisitAdmit.facilityLocations.locationPeriod is not null
If an ED visit is followed by an inpatient encounter, it’s safe to assume there’s missing data that wasn’t reported so you could assert that the end of the location period where it isn’t specified is the start of the inpatient encounter that wasn’t specified. Another thing you could do is to introduce a stratification that would detect the cases where EDFacilityLocation doesn’t have an ending date. This is a way to get the measure to indicate how many we’re getting that don’t have an end date documented. The measure observation would still be calculated but the AdmitTime would take place of the EDDepatureTime.
MedicationOrderPeriod vs MedicationDispensePeriod when calculating CumulativeMedicationsDuration: How does the MedicationOrderPeriod differ from the MedicationDispensePeriod when calculating CumulativeMedicationsDuration version ‘0.3.000’ library in the Quality Data Model (QDM) version 5.6? (Session 57 - 9/16/21)
- From a QDM perspective, a medication order is a prescription event and a medication dispense is a dispense event. QDM considers these separate events.
MedicationOrderPeriod - day vs days: In the MedicationOrderPeriod function in Clinical Quality Language (CQL), how does day versus days differ in the code? (Session 57 - 9/16/21)
- In CQL, a natural language expression is allowed. The functions day and days are equivalent.
define function "MedicationOrderPeriod"(Order "Medication, Order" ):
if Order.relevantPeriod.low is null and Order.authorDatetime is null then
null
else if Order.relevantPeriod.high is not null then
Interval[date from Coalesce(Order.relevantPeriod.low, Order.authorDatetime), date from end of Order.relevantPeriod]
else
(
Coalesce(
Order.daysSupplied,
Order.supply.value / (Order.dosage.value * ToDaily(Order.frequency))
) * (1 + Coalesce(Order.refills, 0))
) durationInDays
let startDate: date from Coalesce(Order.relevantPeriod.low, Order.authorDatetime)
return
if durationInDays is not null then
Interval[startDate, startDate + Quantity { value: durationInDays, unit: 'day' }]
else
null
Medication Present on Admission: What is the best way to specify in Clinical Quality Language (CQL) that a medication was present on admission for an inpatient encounter? There have been conversations recently about a "presentOnAdmissionIndicator" for diagnoses in Quality Data Model (QDM) 5.5, but not something equivalent for medications. (Session 37 - 7/25/19)
- This will be documented as “Medication Active at Admission”.
define "Medications Active at Admission":
["Medication, Active"] M
with ["Encounter, Performed"] E
such that M.relevantPeriod contains start of E.relevantPeriod
The Medication, Active must have a relevant period that includes the start date of the encounter. Note also that this is only addressing the simplest case of an inpatient admission. It may need to be expanded to account for an immediately preceding emergency department visit, depending on measure intent. Also note that the Quality Data Model (QDM) specifies relevant period start for a Medication, Active as the time when the patient is first known to have been taking the medication. Whether this is captured by the electronic health records is a question for investigation.
'Most recent' versus 'last' in Clinical Quality Language: In this Clinical Quality Language (CQL) statement, is 'most recent' used as a CQL expression? Why is 'most recent' used instead of 'last'?
define "Rationale":
Coalesce({
'most recent FOBT issued on ' + ToString(date from CCE."Most Recent Fecal Occult Blood Test Result".issued),
'most recent FIT DNA issued on ' + ToString(date from CCE."Most Recent Fecal Immunochemical Test DNA Result".issued),
'most recent CT Colonography performed on ' + ToString(date from start of FC.ToInterval(CCE."Most Recent CT Colonography
Performed".performed)),
'most recent Flexible Sigmoidoscopy performed on ' + ToString(date from start of FC.ToInterval(CCE."Most Recent Flexible
Sigmoidoscopy Performed".performed)),
'most recent Colonoscopy performed on ' + ToString(date from start of FC.ToInterval(CCE."Most Recent Colonoscopy
Performed".performed)),
'no evidence of appropriate screening'
})
(Session 69 - 12/01/22)
define "Rationale":
Coalesce({
'most recent FOBT issued on ' + ToString(date from CCE."Most Recent Fecal Occult Blood Test Result".issued),
'most recent FIT DNA issued on ' + ToString(date from CCE."Most Recent Fecal Immunochemical Test DNA Result".issued),
'most recent CT Colonography performed on ' + ToString(date from start of FC.ToInterval(CCE."Most Recent CT Colonography
Performed".performed)),
'most recent Flexible Sigmoidoscopy performed on ' + ToString(date from start of FC.ToInterval(CCE."Most Recent Flexible
Sigmoidoscopy Performed".performed)),
'most recent Colonoscopy performed on ' + ToString(date from start of FC.ToInterval(CCE."Most Recent Colonoscopy
Performed".performed)),
'no evidence of appropriate screening'
})
- 'Most recent' is not a CQL expression but used instead as a more descriptive name for the defined function. The 'most recent' function allows for the application of the 'last' function with the application to sort by an interval. Since 'last' references the final element in a list, if the sort was other than chronological (e.g., result.value), 'last' could reference a different concept.
No End Date: If you have overlapping depression periods with no end-date in the measurement period, do you get a query with all the diagnoses? (Session 30 - 12/6/18)
- That's correct because when you don't have an end to the prevalence period and it’s characterized with a closed brace, which is typically how they are characterized, then that means it goes to the end of time. So, when we intersect that with the measurement period, we only get the portion of the prevalence period that is entirely within the measurement period. For example, if you have a diagnosis that starts in March and another that starts in April and they both have no end-date then you'll just get one result.
Opened vs Closed Boundaries: In the specifications, is it necessary to specify that the boundary is closed or open? (Session 24 - 4/26/18)
- No, whether a boundary is closed or open is an aspect of the value, not the type. The systems providing the information indicate whether they are giving an interval that has closed or open boundaries.
Operational logic in Clinical Quality Language: When using the operation for unit conversion in Clinical Quality Language, in the example, does the operational logic perform the math as well as change the unit?
// Explicit Conversions: https://cql.hl7.org/03-developersguide.html#explicit-conversion
define ConvertValues: convert '1' to Integer
define ConvertUnits: convert 5 'mg' to 'g'
define InvalidConvert: convert 'Foo' to Integer // Results in null
(Session 68 - 10/27/22)
// Explicit Conversions: https://cql.hl7.org/03-developersguide.html#explicit-conversion
define ConvertValues: convert '1' to Integer
define ConvertUnits: convert 5 'mg' to 'g'
define InvalidConvert: convert 'Foo' to Integer // Results in null
- The 'convert' operation performs the math and changes the unit. You must follow the Unified Code for Units of Measure conversion rules if a conversion is performed.
Present Upon Arrival with an Active Diagnosis Record: In discussing CLONE Hospital Harm – Pressure Injury Draft 0.00, the measure assesses the proportion of inpatient encounters for patients 18 years and older upon admission and the presence of stage 2, 3, or 4 deep tissue pressure injury or unstageable deep tissue pressure injury. This measure documents deep tissue pressure injury upon arrival or 24 hours after admission. (May be a need to create value sets for anatomical sites to support this measure.) The measure code will be written (continued in the answer). (Session 36 - 6/27/19)
- The measure code will be written:
“Denominator” EncounterwithQualifyingAge
with [“Diagnosis”: “Pressure Ulcer Stage”] PressureInjuryStage
such that (PressureInjuryStage.code ~ Pressure Ulcer Stage 2 (disorder)”
or PressureInjuryStage.code ~ Pressure Ulcer Stage 3 (disorder)”
or PressureInjuryStage.code ~ Pressure Ulcer Stage 4 (disorder)”
or PressureInjuryStage.code ~ Nonstageable Pressure Ulcer(disorder)”
or PressureInjuryStage.code ~ Pressure Injury of Deep Tissue (disorder)”
and PressureInjuryStage.prevelancePeriod starts more than 24 hours after start of “Hospitalization, Potentially Starting in
Emergency Department and or with Observation” (EncounterwithQualifyingAge)
and PressureInjuryStage.prevelancePeriod during “Hospitalization, Potentially Starting in Emergency Department and or with
Observation” (EncounterwithQualifyingAge)
The primary issue seems to be a need to relate a diagnosis that is indicated as present upon arrival with an active Diagnosis record and be able to reliably ensure they are the “same” diagnosis. How do you accomplish this?
ANSWER: This is done by comparing the codes of the diagnosis to the diagnosis as seen upon arrival. The diagnosis code is written as:
“Denominator” EncounterwithQualifyingAge
where [“Diagnosis”: “Pressure Ulcer Stage”] PressureInjuryStage
such that (PressureInjuryStage.code ~ Pressure Ulcer Stage 2 (disorder)”
or PressureInjuryStage.code ~ Pressure Ulcer Stage 3 (disorder)”
or PressureInjuryStage.code ~ Pressure Ulcer Stage 4 (disorder)”
or PressureInjuryStage.code ~ Nonstageable Pressure Ulcer(disorder)”
or PressureInjuryStage.code ~ Pressure Injury of Deep Tissue (disorder)”
and PressureInjuryStage.prevelancePeriod starts more than 24 hours after start of “Hospitalization, Potentially Starting in
Emergency Department and or with Observation” (EncounterwithQualifyingAge)
Notice that “with” has been changed to “where”.
The code for a Pressure Injury Present On Admission is as:
“Initial Population” Encounter
with [Diagnosis] Dx
such that Dx.prevelencePeriod overlaps Encounter.relevantPeriod
and Dx.code in (Encounter.diagnoses D return D.code)
where exists (Encounter.diagnosis EncounterDiagnosis where EncounterDiagnosis.code in “Pressure Injury” and
EncounterDiagnosis.presentOnAdmissionIndicator ~ “Y”)
This “Pressure Injury” will return an error since it does not have the value sets.
Note that in QDM 5.5, we added support for “PresentOnAdmission” using the model:
define “Encounter with “Ischemic Stroke Diagnosis Present On Admission”:
[“Encounter. Performed”: “Inpatient”] E
where exists (E.diagnoses D where D.code in “Ischemic Stroke”
and D.presentOnAdmissionIndicator ~” Y”)
QDM Medication Elements: In calculating methods for determining doses per day using the QDM medication elements, can active medications be used instead of ordered, assuming the patient is taking the medication as directed? (Session 28 - 9/27/18)
- If the frequency is available, then the QDM medication elements calculation would work, but if you are using the supply and the relevant periods to back into a daily value, that might not work because there is no certainty that supply is correlated with the relevant period. Refer to the Cooking with CQL presentation for this session for examples on how to calculate using both methods: https://github.com/cqframework/CQL-Formatting-and-Usage-Wiki/blob/master/Source/Cooking%20With%20CQL/28/68_DosesPerDayCalculations.cql
Querying for Instances of negation using QI-Core: When querying for instances of negation using Quality Improvement-Core, why is the negation not included in a value set? If a value set has logic that says it should be accepted or excluded, what is the condition by which a value set or a certain value is not performed? (Session 67 - 9/29/22)
- The fact that a particular resource is documenting a negation (i.e., an event that did not take place) is usually represented with a status indicating the event was not done, or sometimes with a modifying element such as “doNotPerform”. The “negated” aspect of the statement is independent of “what” is being negated. For example, I can say that I “did not order a Venous Foot Pump”. So, the value sets that we use to describe the “What” typically don’t have negation in them (and are almost always the same value sets we use to look for the positive statement that the event was done).
That something was not done, and what was not done, are also independent from the “reason” that it wasn’t done. For example, if the provider did not order a Venous Foot Pump because they determined there was no indication for the device for this patient, that could be represented using the “Not indicated” code as the reason for the negation.
{
"system": "http://snomed.info/sct",
"code": "410534003",
"display": "Not indicated (qualifier value)"
}
Risk Adjustment: What value is returned in risk adjustment? Is it different between EP measures vs. EH measures? (Session 30 - 12/6/18)
- No, risk adjustment variables can return any value. They are presented in the context of the patient and can return anything. For example, consider this risk adjustment variable definition from the TestRiskAdj_CQL example measure from the CQL-Based HQMF IG:
define "Bilirubin Test": ["Laboratory Test, Performed": "Bilirubin"] L
with "CABG_Open and Endoscopic During Encounter" C
such that L.relevantPeriod starts before start of C.relevantPeriod
with "Encounter Inpatient 365" E
such that L.relevantPeriod starts during E.relevantPeriod
where L.result as Quantity > 2 'mg/dL'
This returns all Bilirubin Tests for the patient that occurred during an Open and Endoscopic CABG during a qualifying encounter and with a result > 2 'mg/dL'.
Stratification - libraries:With the stratifications being somewhat independent of the measure logic itself, it would be interesting to put the stratifiers in a stratifier library, but if you do, how do you reference outside that measure to the library in Fast Healthcare Interoperability Resources® (FHIR)? This would effectively have a library referencing another library resource (Session 54 - 5/27/21)
- The use of a library, in this case a stratifier library, is a specific conformance requirement put on measures, and the Quality Measure Implementation Guide references specifically, so that all the expressions in a given measure are referencing the expressions in a single library. From an implementation perspective, that means the engine does not have to resolve library references outside of the engine, it only has to resolve references within that boundary.
Stratifiers within a numerator:The expectation is for a stratifier to be a complete and disjoint partitioning of the population. For implementations that perform case-at-a-time processing, the calculation and collection of the stratifiers may be part of processing each time, whereas for implementations that evaluate at the population level, there may be independent calculation of the stratifiers. If the logic applies stratifiers to each set of population criteria by default, what is the advantage of applying a stratifier only within a numerator? What is a use case for which that scenario would come up?
define "Initial Population for Stratifier 1":
AgeInYearsAt(start of "Measurement Period") >= 6
and AgeInYearsAt(start of "Measurement Period") < 20
and exists ( "Qualifying Encounters" )
// AND the Stratifier 1 criteria
and AgeInYearsAt(start of "Measurement Period") >= 12
and AgeInYearsAt(start of "Measurement Period") < 20
define "Denominator":
"Initial Population"
// AND the Stratifier 1 criteria
and AgeInYearsAt(start of "Measurement Period") >= 12
and AgeInYearsAt(start of "Measurement Period") < 20
define "Denominator Exclusions":
Hospice."Has Hospice"
// AND the Stratifier 1 criteria
and AgeInYearsAt(start of "Measurement Period") >= 12
and AgeInYearsAt(start of "Measurement Period") < 20
define "Numerator":
exists ( ["Procedure": "Fluoride Varnish Application for Children"] FluorideApplication
where FluorideApplication.performed during "Measurement Period"
and FluorideApplication.status = 'completed'
)
// AND the Stratifier 1 criteria
and AgeInYearsAt(start of "Measurement Period") >= 12
and AgeInYearsAt(start of "Measurement Period") < 20
(Session 54 - 5/27/21)
define "Initial Population for Stratifier 1":
AgeInYearsAt(start of "Measurement Period") >= 6
and AgeInYearsAt(start of "Measurement Period") < 20
and exists ( "Qualifying Encounters" )
// AND the Stratifier 1 criteria
and AgeInYearsAt(start of "Measurement Period") >= 12
and AgeInYearsAt(start of "Measurement Period") < 20
define "Denominator":
"Initial Population"
// AND the Stratifier 1 criteria
and AgeInYearsAt(start of "Measurement Period") >= 12
and AgeInYearsAt(start of "Measurement Period") < 20
define "Denominator Exclusions":
Hospice."Has Hospice"
// AND the Stratifier 1 criteria
and AgeInYearsAt(start of "Measurement Period") >= 12
and AgeInYearsAt(start of "Measurement Period") < 20
define "Numerator":
exists ( ["Procedure": "Fluoride Varnish Application for Children"] FluorideApplication
where FluorideApplication.performed during "Measurement Period"
and FluorideApplication.status = 'completed'
)
// AND the Stratifier 1 criteria
and AgeInYearsAt(start of "Measurement Period") >= 12
and AgeInYearsAt(start of "Measurement Period") < 20
- Generally, a stratifier reports the stratification for all measure criteria. From the code in the question, the initial population, including the numerator and denominator, and each stratum with the associated score can be seen. To calculate the score for the stratifier, one should partition each stratifier into a mini measure with the same criteria. The application of the stratifiers is to the criteria to establish that stratum score. The reason the measure developer might only apply the stratifier to the numerator is if they want the actual counts for the numerator and do not care what the counts are for the other components of the measure. In this case, the numerator is the reported result, but the measure developer still needs to calculate the full stratification in order to get the numerator value. One example use case where one might only apply a stratifier to the numerator is CMS 111, the Emergency Department (ED) Measure, where the measure stratifies the measure population by mental health diagnoses, but does not stratify for the other populations.
Structured Query Language (SQL) joins: How do you represent Structured Query Language (SQL) joins in Clinical Quality Language (CQL)? (Session 37 - 7/25/19)
- In Structured Query Language (SQL), joins are used to combine data from multiple tables. In Clinical Quality Language (CQL), the focus is on simplest possible expression of single-source queries. But multi-source queries are possible as well. Some examples:
define "Semi-join Example":
["Encounter, Performed": "Outpatient"] Encounter
with ["Laboratory Test, Performed": "Streptococcus Test"] Test
such that Test.resultDatetime during Encounter.relevantPeriod
define "Semi-minus Example":
["Encounter, Performed": "Outpatient"] Encounter
without ["Laboratory Test, Performed": "Streptococcus Test"] Test
such that Test.resultDatetime during Encounter.relevantPeriod
define "Join Example":
from
["Encounter, Performed": "Outpatient"] Encounter,
["Laboratory Test, Performed": "Streptococcus Test"] Test
where Test.resultDatetime during Encounter.relevantPeriod
return { Encounter: Encounter, Test: Test }
define "Join Example with Select":
from
["Encounter, Performed": "Outpatient"] Encounter,
["Laboratory Test, Performed": "Streptococcus Test"] Test
where Test.resultDatetime during Encounter.relevantPeriod
return {
encounterId: Encounter.id,
encounterCode: Encounter.code,
encounterRelevantPeriod: Encounter.relevantPeriod
testId: Test.id,
testCode: Test.code,
testResultDatetime: Test.resultDatetime,
testResult: Test.result
}
define "Cartesian-product Example":
from
["Encounter, Performed": "Outpatient"] Encounter,
["Laboratory Test, Performed": "Streptococcus Test"] Test
The Default result from a multi-source query is a tuple with an element for each query source { Encounter: "Encounter, Performed", Test: "Laboratory Test, Performed" }
define "Left-outer-join Example":
["Encounter, Performed": "Outpatient"] Encounter
let Test: singleton from (["Laboratory Test, Performed": "Streptococcus Test"] LabTest
where LabTest.resultDatetime during Encounter.relevantPeriod)
return { Encounter: Encounter, Test: Test }
Note, there is an open source project that allows you to translate CQL to SQL. It was a pilot project in the Healthe-Decisions initiative. It is available on the Clinical Quality Framework Repository. It’s a little outdated, but the structure is there.
You can have a native environment that runs SQL directly. There are vendor systems that can do this and three open source implementations of a SQL engine that runs SQL directly. On the GitHub Wiki, there is a community projects page that has links to all of those open source implementations.
Troponin Continuous Variable Measure - CQL calculated sort order: Regarding the Troponin Continuous Variable measure (which refers to critical value reporting timeliness) and the use of the CQL calculated sort order within the logic expression where the relatedTo events appear first, will this function calculate the combination that has the minimum or least number of minutes between the two?
define function "Minutes between results and communication by id THEN timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by if exists (relatedTo) then 0 else 1, sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
(session 45 - 6/25/20)
define function "Minutes between results and communication by id THEN timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by if exists (relatedTo) then 0 else 1, sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
- Yes, the calculation performed would select the communication associated with this lab test and choose the least number of minutes between the two. For example, if you have a lab test with related communications that span 60 minutes and then you have a lab test with unrelated communications that span 50 minutes, the return would be 60 minutes between T.resultDatetime and the Communication.sentDatetime. The lab test that had related communications would return 60 minutes and the lab test that had no related communications would return 50 minutes. Therefore, the calculation performed would select the related results first, then the minimum number of minutes out of that value set. The point of the exists (relateTo) is to find related items first, with fallback to unrelated only if related results are not present.
Troponin Continuous Variable Measure - multiple Toponin tests and communication: The Troponin Continuous Variable measure is a pathology-driven quality measure under development that relates to critical value reporting and is seeking the time interval in minutes from the time the results for troponin tests are determined and when the laboratory communicates the critical troponin levels to the responsible provider using the electronic health record (EHR) as the source of the data. The measure observation evaluates only the first troponin test followed by a communication (e.g., email, fax) during the encounter. The communication should be completed after the test and should be related to the test for which it is attributed. The issue is in trying to create the logic to relate the communication about a specific test when there are multiple troponin tests during an encounter. As an example, during an encounter we have two troponin tests and one communication:
- The first troponin test result date time: 2/12/2019 1100
- The second troponin test result date time: 2/12/2019 1205
- The first communication sent date time: 2/12/2019 1305
The measure should be comparing the communication to test result to which it corresponds. The current logic relates the communication to the first troponin test in the example. How can one address a scenario in which there are multiple toponin tests and related each communication to its respective test result? (session 44 - 5/28/20)
- Using Quality Data Model (QDM) version 5.5, there are several approaches to consider. The approach depends on the availability of information about the relationship between the communication and the troponin lab test id and whether there is information in the EHR that can link the communication with the lab test.
Expression 1: The first expression is a multi-source query that says “for every encounter, for every troponin test performed, and for every communication performed, only consider the tests that were performed in the relevant period. The communication should be related to the id.”
define function "Minutes between results and communication by id"(Encounter "Encounter, Performed"):
Min(
from
["Laboratory Test, Performed": "Cardiac Troponin"] T,
["Communication, Performed": "Lab Communications"] C
where T.resultDatetime during Encounter.relevantPeriod
and C.sentDatetime during Encounter.relevantPeriod
and C.relatedTo includes T.id
return minutes between T.resultDatetime and C.sentDatetime
)
Expression 2: The second approach is performed based on timing. The second expression says, “for every laboratory test during this encounter, return the minutes between the result date time and the dateTime the communication is sent.” Using this expression for every laboratory test, it will look for the first communication after that laboratory test. It will return the duration in minutes between availability of the test result and the time the communication is sent so you will get the length of time for every test before a communication came back.
define function "Minutes between results and communication by timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
sort by sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
Expression 3: The third expression returns the minutes between communications for the first test and a second test if there are no relatedTo elements in the communications or the relatedTo includes the id. If the EHR is able to provide information whether the communication is relatedTo a specific troponin test, then it will come back as the first and only communication. However, if the EHR is not able to provide the relationship between the communication and the test result, then it will find the first ‘sent’ time of a communication during the encounter that came after the result retrieved.
define function "Minutes between results and communication by id or timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
Troponin Continuous Variable Measure - minutes between results and communication by id or timing: With respect to the Troponin Continuous Variable measure (which refers to critical value reporting timeliness) and the logic expression defining the function of “Minutes between results and communication by id THEN timing”, should the relatedTo in the expression actually be written as C.relatedTo for sorting purposes?
define function "Minutes between results and communication by id THEN timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by if exists (relatedTo) then 0 else 1, sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
(Session 45 - 6/25/20)
define function "Minutes between results and communication by id THEN timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by if exists (relatedTo) then 0 else 1, sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
- No, this is a really important point. The reason we have a C.relatedTo in the previous line is because this portion of the expression is performing a query that is potentially about having multiple items (e.g., from, clauses, with).
define “Communications Performed”:
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
By the time you get to the sort function of the expression, the query is completed and the shape of the result is established. The alias does not apply anymore. You can refer to things in the sort that happen in the return. Since relatedTo is an attribute of the communication performed returned from the query, it can be used in an expression to determine a sort order. Therefore, the expression of sort is only in terms of the result and does not happen during the query, it happens during the final result of the query.
Troponin Continuous Variable Measure - minutes between results and communication by id or timing: With respect to the Troponin Continuous Variable measure (which refers to critical value reporting timeliness) and the logic expression defining the function of “Minutes between results and communication by id THEN timing”, what is the measure intent and what is the highlighted portion (not exists (C.relatedTo) of the expression looking for?
define function "Minutes between results and communication by id THEN timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by sentDatetimeif exists (relatedTo) then 0 else 1, sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
(Session 45 - 6/25/20)
define function "Minutes between results and communication by id THEN timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by sentDatetimeif exists (relatedTo) then 0 else 1, sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
-
The intent of this measure is to drive standardization of practice with communication from the pathologist to the provider. The challenge was to find the difference in time between tests and their related communication, but account for the fact that the test may not capture the related communication. Therefore, that information may not be available. We came up with two approaches to find the difference in time between tests and their related communication.
- The first approach is to relate the results and communication by the id.
- The second approach is to relate them by the sentDatetime.
We say, if the relationship is not explicitly characterized using relatedTo, then we default to a temporal relationship where we pick the first communication after the result was sent and we will say that is the communication. This is not 100% accurate, but it is a communication that came from the same encounter so it is likely relevant. We are using this as a fallback in case the communication does not capture the relatedTo. Then, we expressed the two approaches as a combined result that said we are doing both at the same time by putting in this list of communications performed and either the communication does not specify the relationship so we need to relate it by a temporal relationship or it explicitly specifies a relationship and we need to use that. If the communication explicitly states that it is related to some lab test, then for those communications, we only want to consider the communications that are related to this lab test. When testing scenarios, we found that in some cases unrelated communication occurred before the related communication. The unrelated communication would override and the unrelated communication would be considered first because the sorting function used was sentDatetime. The not exists (C.relatedTo) function expands that solution to order by related first then sorted by the sentDatetime. This accounts for both related and unrelated communications, but gives priority to the communications that we know are related to this lab test.
Troponin Continuous Variable Measure - minutes between results and communication by id or timing: The Troponin Continuous Variable measure expression provided is looking for minutes between results and communication by id or timing. The expression related to timing makes sense, but the highlighted line says “not exists” so would not that untie the id?
define function "Minutes between results and communication by id or timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
(session 44 - 5/28/20)
define function "Minutes between results and communication by id or timing"(Encounter "Encounter, Performed"):
Min(
["Laboratory Test, Performed": "Cardiac Troponin"] T
let
Communication: First(
["Communication, Performed": "Lab Communications"] C
where C.sentDatetime during Encounter.relevantPeriod
and C.sentDatetime after T.resultDatetime
and (not exists (C.relatedTo) or C.relatedTo includes T.id)
sort by sentDatetime
)
return minutes between T.resultDatetime and Communication.sentDatetime
)
- The communication includes a relatedTo element, that is a list of strings so the communication is potentially related to multiple items. What this expression is saying is “if there is no relatedTo, then this expression is true.” We would not want to say C.relatedTo includes T.id because if relatedTo is empty, the statement would be false and this component of the expression would not be applied. If we have relatedTo, we should use it, but if we do not have relatedTo, then fall back to timing. The "not exists" (C.relatedTo) component is true if there are no relatedTo T.id elements present, in which case, we rely upon just the timing relationship to link the communication to the appropriate troponin test. If the electronic health record (EHR) is not capturing the explicit link between the order and the communication or does not have the information available, then we are falling back to timing in this approach because the function we’re defining in the example is looking for the minutes between results and communications by id or timing. By having (not exists (C.relatedTo) or C.relatedTo includes T.id) entirely in parenthesis, the entire statement in the parenthesis has to be true.
Unions in ED Encounter Expression: In the ED encounter observation expression, can the union be used with both option 1 (use of an Encounter code to model observation) and option 2 (use of an Encounter facility location to model observation)?
/*
O) As a different type of Encounter, separate from the Inpatient and Emergency Department encounters:
*/
define "Observation (Approach O)":
["Encounter, Performed": "Observation"]
/*
P) As a different location within the Inpatient Encounter:
*/
define "Observation (Approach P)":
["Encounter, Performed": "Encounter Inpatient"] E
return E.facilityLocations L where L.code in "Observation"
/*
NOTE: Observation beds are often scattered throughout the hospital and are not
necessarily designated as observation beds
(Session 27 - 7/26/18)
/*
O) As a different type of Encounter, separate from the Inpatient and Emergency Department encounters:
*/
define "Observation (Approach O)":
["Encounter, Performed": "Observation"]
/*
P) As a different location within the Inpatient Encounter:
*/
define "Observation (Approach P)":
["Encounter, Performed": "Encounter Inpatient"] E
return E.facilityLocations L where L.code in "Observation"
/*
NOTE: Observation beds are often scattered throughout the hospital and are not
necessarily designated as observation beds
- The “union” in this question is referring to the common use of unions to bring together encounters from different value sets in to a single expression for consideration. With option 1, because the observation status is determined by looking at a facility code, the encounter codes are still used in the way that they are now (i.e. to represent the type of encounter: inpatient, outpatient, etc.). With option 2, however, “Observation” is represented as another type of encounter, so you wouldn’t be able to distinguish other types of encounters within Observation.
Using a Subset of an Existing Value Set: When developing a new measure and I want to use an existing value set, how do I pull out specific codes, a subset of the existing value set, in a new value set? E.g. pulling one contraceptive from a list. (session 23 - 3/29/18)
- If you need to reference only a subset of the codes from an existing value set, this can be done two ways; 1) define another value set with only that code in it or, 2) reference a specific code. In general, if the use case clearly identifies a specific code to reference, you can use it as a direct reference code. However, if there are multiple codes necessary, a new value set is needed.
Note that although CQL does provide set operations that could be used to compute the subset of a value set using some expressive criteria, performing calculations involving value sets is not recommended for several reasons:
- Terminology operations, such as membership, are communicated in the machine-readable file in specific ways that enable implementations to make use of terminology servers. Performing calculations on value sets requires the use of expansion, which is not required for membership testing when that testing is done by the terminology server. As a result, using value sets in calculation would impose an additional implementation burden on vendors consuming the measure.
- The definition of terminology is by design a separate aspect of quality measure development. This separation of concerns has multiple benefits, including that the maintenance and governance of value sets can be performed independent of the maintenance and governance of the measures.
- Value set definition often involves operations that are specific to the terminologies involved, so there is a great deal of variability in the way that these expressions are represented.
Using choice types in Clinical Quality Language: When using choice types in Clinical Quality Language, why is a unit of '1' used instead of a Unified Code for Units of Measure (UCUM) annotation?
/*
@description: Returns the value of the given Quantity, interpreted as an Integer if it is safe to do so.
@comments: A Quantity value can be safely interpreted as an integer if it has no decimal component (i.e. zeros after the decimal),
and it has the default UCUM unit of '1'
*/
define function ToInteger(value Quantity):
case
when Abs(value.value - Truncate(value.value)) > 0.00000001 then
Message(null, true, 'ToInteger.InvalidArgument', ErrorSeverity, 'The quantity has a non-zero decimal component and cannot be safely interpreted as an Integer')
when value.unit != '1' then
Message(null, true, 'ToInteger.InvalidUnit', ErrorSeverity, 'The quantity has non-default units specified and cannot be safely interpreted as an Integer')
else
Truncate(value.value)
end
/*
(Session 68 - 10/27/22)
/*
@description: Returns the value of the given Quantity, interpreted as an Integer if it is safe to do so.
@comments: A Quantity value can be safely interpreted as an integer if it has no decimal component (i.e. zeros after the decimal),
and it has the default UCUM unit of '1'
*/
define function ToInteger(value Quantity):
case
when Abs(value.value - Truncate(value.value)) > 0.00000001 then
Message(null, true, 'ToInteger.InvalidArgument', ErrorSeverity, 'The quantity has a non-zero decimal component and cannot be safely interpreted as an Integer')
when value.unit != '1' then
Message(null, true, 'ToInteger.InvalidUnit', ErrorSeverity, 'The quantity has non-default units specified and cannot be safely interpreted as an Integer')
else
Truncate(value.value)
end
/*
- The number ‘1’ is the default UCUM unit as the identity unit in UCUM math. An annotation cannot be used at the current time, though that would be a good enhancement to the function.
Value set links in Clinical Quality Language: Value sets allow us to share concepts within electronic Clinical Quality Measures (eCQMs,) as well as reuse the value set definition within Clinical Quality Language (CQL.) The library example is a set of value set definitions. Where are the value set links pulled from?
library FHIRCommon version '4.0.1'
using FHIR version '4.0.1'
include FHIRHelpers version '4.0.1'
codesystem "LOINC": 'http://loinc.org'
codesystem "SNOMEDCT": 'http://snomed.info/sct'
codesystem "RoleCode": 'http://terminology.hl7.org/CodeSystem/v3-RoleCode'
codesystem "Diagnosis Role": 'http://terminology.hl7.org/CodeSystem/diagnosis-role'
codesystem "RequestIntent": 'http://terminology.hl7.org/CodeSystem/request-intent'
codesystem "MedicationRequestCategory": 'http://terminology.hl7.org/CodeSystem/medicationrequest-category'
codesystem "ConditionClinicalStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-clinical'
codesystem "ConditionVerificationStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-ver-status'
codesystem "AllergyIntoleranceClinicalStatusCodes": 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical'
codesystem "AllergyIntoleranceVerificationStatusCodes": 'http://terminology.hl7.org/CodeSystem/allergyintolerance-verification'
(Session 69 - 12/01/22)
library FHIRCommon version '4.0.1'
using FHIR version '4.0.1'
include FHIRHelpers version '4.0.1'
codesystem "LOINC": 'http://loinc.org'
codesystem "SNOMEDCT": 'http://snomed.info/sct'
codesystem "RoleCode": 'http://terminology.hl7.org/CodeSystem/v3-RoleCode'
codesystem "Diagnosis Role": 'http://terminology.hl7.org/CodeSystem/diagnosis-role'
codesystem "RequestIntent": 'http://terminology.hl7.org/CodeSystem/request-intent'
codesystem "MedicationRequestCategory": 'http://terminology.hl7.org/CodeSystem/medicationrequest-category'
codesystem "ConditionClinicalStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-clinical'
codesystem "ConditionVerificationStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-ver-status'
codesystem "AllergyIntoleranceClinicalStatusCodes": 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical'
codesystem "AllergyIntoleranceVerificationStatusCodes": 'http://terminology.hl7.org/CodeSystem/allergyintolerance-verification'
- The value set links are the canonical uniform resource locators (URLs) of the value sets in the Value Set Authority Center (VSAC.) While there are times when you can link an object identifier (OID) to a value set, similar to usage in QDM eCQMs, a Fast Healthcare Interoperability Resources® (FHIR®) measure or library uses a canonical URL. The canonical URL is a globally unique identifier for that value set, and it includes the base URL for the value set. If the publisher has made the value set available in a website or application programming interface (API,) clicking the canonical URL link will take the user to the definition of the value set. For ValueSets published in the HL7 terminology host 'terminology.hl7.org’, information on the value set is provided in narrative, JavaScript Object Notation (JSON,) and Extensible Markup Language (XML) forms. For VSAC value sets, the canonical URL will resolve to a FHIR API that returns the ValueSet content as JSON. Note that the use of this API, just like any access to the VSAC, requires a National Library of Medicine (NLM) account.
"Where" Clause: When the “where” clause is used in the example; does it mean that the ‘let…’ statement can only be assigned one line of code?
let procedure: (
“Total Hip Total Knee Procedure” (QualifyingEncounters) P
where P.ordinality in “Principal”
)
(Session 24 - 4/26/18)
let procedure: (
“Total Hip Total Knee Procedure” (QualifyingEncounters) P
where P.ordinality in “Principal”
)
- No, the “where” clause in a query will not change the result type of the query.
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