diff --git a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/qicore/v600/BaseTest.java b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/qicore/v600/BaseTest.java index 16981fdce..f92407c22 100644 --- a/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/qicore/v600/BaseTest.java +++ b/Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/qicore/v600/BaseTest.java @@ -1,14 +1,19 @@ package org.cqframework.cql.cql2elm.qicore.v600; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.cqframework.cql.cql2elm.CqlTranslator; import org.cqframework.cql.cql2elm.TestUtils; +import org.hl7.cql.model.ClassType; +import org.hl7.cql.model.ListType; import org.hl7.elm.r1.*; import org.junit.jupiter.api.Test; @@ -165,4 +170,332 @@ void qICore() throws IOException { assertThat(fr.getLibraryName(), is("FHIRHelpers")); assertThat(fr.getName(), is("ToValueSet")); } + + @Test + void testPCSBMIScreenAndFollowUpFHIR() throws IOException { + CqlTranslator translator = TestUtils.runSemanticTest("qicore/v600/PCSBMIScreenAndFollowUpFHIR.cql", 0); + + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + ExpressionDef def = defs.get("Initial Population"); + assertThat(def, notNullValue()); + } + + @Test + void testPCSDepressionScreenAdnFollowUpFHIR() throws IOException { + CqlTranslator translator = TestUtils.runSemanticTest("qicore/v600/PCSDepressionScreenAndFollowUpFHIR.cql", 0); + + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + ExpressionDef def = defs.get("Initial Population"); + assertThat(def, notNullValue()); + } + + @Test + void testStatinsforthePreventionandTreatmentofCardiovascularDiseaseFHIR() throws IOException { + CqlTranslator translator = TestUtils.runSemanticTest( + "qicore/v600/StatinsforthePreventionandTreatmentofCardiovascularDiseaseFHIR.cql", 0); + + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + ExpressionDef def = defs.get("Initial Population 1"); + assertThat(def, notNullValue()); + } + + // TODO: This content isn't available from measure developers for QICOre 6 yet. Update when available + // @Test + void adultOutpatientEncounters() throws IOException { + CqlTranslator translator = + TestUtils.runSemanticTest("qicore/v600/AdultOutpatientEncounters_QICore4-2.0.000.cql", 0); + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + /* + ExpressionDef + expression: Query + where: And + operand[0]: IncludedIn + operand[0]: FunctionRef + name: ToInterval + libraryName: FHIRHelpers + + */ + + ExpressionDef def = defs.get("Qualifying Encounters"); + assertThat(def, notNullValue()); + assertThat(def.getExpression(), instanceOf(Query.class)); + Query query = (Query) def.getExpression(); + assertThat(query.getWhere(), instanceOf(And.class)); + And and = (And) query.getWhere(); + assertThat(and.getOperand().size(), equalTo(2)); + assertThat(and.getOperand().get(0), instanceOf(IncludedIn.class)); + IncludedIn includedIn = (IncludedIn) and.getOperand().get(0); + assertThat(includedIn.getOperand().size(), equalTo(2)); + assertThat(includedIn.getOperand().get(0), instanceOf(FunctionRef.class)); + FunctionRef functionRef = (FunctionRef) includedIn.getOperand().get(0); + assertThat(functionRef.getName(), equalTo("ToInterval")); + assertThat(functionRef.getLibraryName(), equalTo("FHIRHelpers")); + } + + // TODO: Update when this content is available from measure developers for QICore 6 + // @Test + void papTestWithResults() throws IOException { + CqlTranslator translator = TestUtils.runSemanticTest("qicore/v600/EXM124_QICore4-8.2.000.cql", 0); + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + ExpressionDef def = defs.get("Pap Test with Results"); + assertThat(def, notNullValue()); + + /* + ExpressionDef: Pap Test with Results + Query + Source + Where: And + Operand[0]: Not + Operand[0]: IsNull + Operand[0]: FunctionRef: FHIRHelpers.ToValue + */ + assertThat(def.getExpression(), instanceOf(Query.class)); + Query q = (Query) def.getExpression(); + assertThat(q.getWhere(), instanceOf(And.class)); + And a = (And) q.getWhere(); + assertThat(a.getOperand().get(0), instanceOf(Not.class)); + Not n = (Not) a.getOperand().get(0); + assertThat(n.getOperand(), instanceOf(IsNull.class)); + IsNull i = (IsNull) n.getOperand(); + assertThat(i.getOperand(), instanceOf(FunctionRef.class)); + FunctionRef fr = (FunctionRef) i.getOperand(); + assertThat(fr.getLibraryName(), equalTo("FHIRHelpers")); + assertThat(fr.getName(), equalTo("ToValue")); + assertThat(fr.getOperand().get(0), instanceOf(Property.class)); + Property p = (Property) fr.getOperand().get(0); + assertThat(p.getPath(), equalTo("value")); + assertThat(p.getScope(), equalTo("PapTest")); + } + + @Test + void medicationRequest() throws IOException { + CqlTranslator translator = TestUtils.runSemanticTest("qicore/v600/TestMedicationRequest.cql", 0); + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + ExpressionDef def = defs.get("Antithrombotic Therapy at Discharge"); + assertThat(def, notNullValue()); + assertThat(def.getExpression(), instanceOf(Query.class)); + Query q = (Query) def.getExpression(); + assertThat(q.getSource().size(), is(1)); + assertThat(q.getSource().get(0).getExpression(), instanceOf(Retrieve.class)); + Retrieve r = (Retrieve) q.getSource().get(0).getExpression(); + assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest")); + assertThat(r.getCodeProperty(), is("medication")); + assertThat(r.getCodeComparator(), is("in")); + assertThat(r.getCodes(), instanceOf(ValueSetRef.class)); + ValueSetRef vsr = (ValueSetRef) r.getCodes(); + assertThat(vsr.getName(), is("Antithrombotic Therapy")); + + def = defs.get("Antithrombotic Therapy at Discharge (2)"); + assertThat(def, notNullValue()); + assertThat(def.getExpression(), instanceOf(Union.class)); + Union u = (Union) def.getExpression(); + assertThat(u.getOperand().size(), is(2)); + assertThat(u.getOperand().get(0), instanceOf(Retrieve.class)); + r = (Retrieve) u.getOperand().get(0); + assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest")); + assertThat(r.getCodeProperty(), is("medication")); + assertThat(r.getCodeComparator(), is("in")); + assertThat(r.getCodes(), instanceOf(ValueSetRef.class)); + vsr = (ValueSetRef) r.getCodes(); + assertThat(vsr.getName(), is("Antithrombotic Therapy")); + + assertThat(u.getOperand().get(1), instanceOf(Query.class)); + q = (Query) u.getOperand().get(1); + assertThat(q.getSource().size(), is(1)); + assertThat(q.getSource().get(0).getExpression(), instanceOf(Retrieve.class)); + r = (Retrieve) q.getSource().get(0).getExpression(); + assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest")); + assertThat(r.getCodeProperty() == null, is(true)); + assertThat(r.getCodes() == null, is(true)); + assertThat(q.getRelationship(), notNullValue()); + assertThat(q.getRelationship().size(), is(1)); + assertThat(q.getRelationship().get(0), instanceOf(With.class)); + With w = (With) q.getRelationship().get(0); + assertThat(w.getExpression(), instanceOf(Retrieve.class)); + r = (Retrieve) w.getExpression(); + assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication")); + assertThat(r.getCodeProperty() == null, is(true)); + assertThat(r.getCodes() == null, is(true)); + assertThat(r.getResultType(), instanceOf(ListType.class)); + assertThat(((ListType) r.getResultType()).getElementType(), instanceOf(ClassType.class)); + assertThat(((ClassType) ((ListType) r.getResultType()).getElementType()).getName(), is("QICore.Medication")); + assertThat(w.getSuchThat(), instanceOf(And.class)); + And a = (And) w.getSuchThat(); + assertThat(a.getOperand().get(0), instanceOf(Equal.class)); + Equal eq = (Equal) a.getOperand().get(0); + assertThat(eq.getOperand().get(0), instanceOf(Property.class)); + Property p = (Property) eq.getOperand().get(0); + assertThat(p.getScope(), is("M")); + assertThat(p.getPath(), is("id.value")); + assertThat(eq.getOperand().get(1), instanceOf(Last.class)); + Last l = (Last) eq.getOperand().get(1); + assertThat(l.getSource(), instanceOf(Split.class)); + Split s = (Split) l.getSource(); + assertThat(s.getStringToSplit(), instanceOf(Property.class)); + p = (Property) s.getStringToSplit(); + assertThat(p.getScope(), is("MR")); + assertThat(p.getPath(), is("medication.reference.value")); + // assertThat(s.getSeparator(), is("/")); + assertThat(a.getOperand().get(1), instanceOf(InValueSet.class)); + InValueSet ivs = (InValueSet) a.getOperand().get(1); + assertThat(ivs.getValueset().getName(), is("Antithrombotic Therapy")); + assertThat(ivs.getCode(), instanceOf(FunctionRef.class)); + FunctionRef fr = (FunctionRef) ivs.getCode(); + assertThat(fr.getLibraryName(), is("FHIRHelpers")); + assertThat(fr.getName(), is("ToConcept")); + assertThat(fr.getOperand().size(), is(1)); + assertThat(fr.getOperand().get(0), instanceOf(Property.class)); + p = (Property) fr.getOperand().get(0); + assertThat(p.getScope(), is("M")); + assertThat(p.getPath(), is("code")); + } + + @Test + void choiceUnion() throws IOException { + CqlTranslator translator = TestUtils.runSemanticTest("qicore/v600/TestChoiceUnion.cql", 0); + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + ExpressionDef def = defs.get("Union of Different Types"); + assertThat(def, notNullValue()); + assertThat(def.getExpression(), instanceOf(Query.class)); + Query q = (Query) def.getExpression(); + assertThat(q.getReturn(), notNullValue()); + assertThat(q.getReturn().getExpression(), instanceOf(Tuple.class)); + Tuple t = (Tuple) q.getReturn().getExpression(); + assertThat(t.getElement(), notNullValue()); + assertThat(t.getElement().size(), is(2)); + TupleElement t0 = t.getElement().get(0); + TupleElement t1 = t.getElement().get(1); + assertThat(t0.getName(), is("performed")); + assertThat(t0.getValue(), instanceOf(FunctionRef.class)); + FunctionRef fr = (FunctionRef) t0.getValue(); + assertThat(fr.getName(), is("ToValue")); + assertThat(fr.getOperand().size(), is(1)); + assertThat(fr.getOperand().get(0), instanceOf(Property.class)); + Property p = (Property) fr.getOperand().get(0); + assertThat(p.getPath(), is("performed")); + assertThat(p.getScope(), is("R")); + assertThat(t1.getName(), is("authoredOn")); + assertThat(t1.getValue(), instanceOf(Property.class)); + p = (Property) t1.getValue(); + assertThat(p.getPath(), is("value")); + assertThat(p.getSource(), instanceOf(Property.class)); + p = (Property) p.getSource(); + assertThat(p.getPath(), is("authoredOn")); + assertThat(p.getScope(), is("R")); + } + + // TODO: Apparently (enabled=false) doesn't work on the CI server? + // @Test(enabled = false, description = "Signature overloads not yet working for derived models") + public void testSignatureOnInterval() throws IOException { + CqlTranslator translator = TestUtils.runSemanticTest("qicore/v600/SupplementalDataElements-4.0.0000.cql", 0); + + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + var payer = defs.get("SDE Payer"); + + assertNotNull(payer); + + var query = (Query) payer.getExpression(); + var t = (Tuple) query.getReturn().getExpression(); + var toInterval = (FunctionRef) t.getElement().get(1).getValue(); + + assertNotNull(toInterval.getSignature()); + assertThat(toInterval.getSignature().size(), is(1)); + } + + @Test + public void testMedicationNotRequested() throws IOException { + CqlTranslator translator = TestUtils.runSemanticTest("qicore/v600/FHIRHelpersToConceptError.cql", 0); + + Library library = translator.toELM(); + Map defs = new HashMap<>(); + + if (library.getStatements() != null) { + for (ExpressionDef def : library.getStatements().getDef()) { + defs.put(def.getName(), def); + } + } + + var minimalRepro = defs.get("Minimal Repro"); + assertNotNull(minimalRepro); + assertThat(minimalRepro.getExpression(), instanceOf(Query.class)); + var query = (Query) minimalRepro.getExpression(); + var returnClause = query.getReturn(); + assertThat(returnClause.getExpression(), instanceOf(Query.class)); + query = (Query) returnClause.getExpression(); + assertThat(query.getSource().size(), equalTo(1)); + var source = query.getSource().get(0); + assertThat(source.getExpression(), instanceOf(Property.class)); + var property = (Property) source.getExpression(); + assertThat(property.getPath(), equalTo("reasonCode")); + assertThat(property.getScope(), equalTo("NoStatinTherapyOrdered")); + returnClause = query.getReturn(); + assertThat(returnClause.getExpression(), instanceOf(FunctionRef.class)); + var functionRef = (FunctionRef) returnClause.getExpression(); + assertThat(functionRef.getLibraryName(), equalTo("FHIRHelpers")); + assertThat(functionRef.getName(), equalTo("ToConcept")); + } } diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/CQMCommon-3.0.000.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/CQMCommon-3.0.000.cql new file mode 100644 index 000000000..3f6dd8904 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/CQMCommon-3.0.000.cql @@ -0,0 +1,433 @@ +/* +@update: JKR 2024-04-02 -> +Increment QICoreCommon and FHIRHelpers version +Update principalDiagnosis function to correctly reference the 'use' element https://oncprojectracking.healthit.gov/support/browse/CQLIT-416 +*/ +library CQMCommon version '3.0.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers +include QICoreCommon version '3.0.000' called QICoreCommon + +codesystem DiagnosisRole: 'http://terminology.hl7.org/CodeSystem/diagnosis-role' +codesystem "Present On Admission Indicators": 'https://www.cms.gov/Medicare/Medicare-Fee-for-Service-Payment/HospitalAcqCond/Coding' + +valueset "Emergency Department Visit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.292' +valueset "Encounter Inpatient": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307' +valueset "Intensive Care Unit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1029.206' +valueset "Observation Services": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1111.143' +valueset "Outpatient Surgery Service": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1110.38' +valueset "Present on Admission or Clinically Undetermined": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1147.197' + +code "Billing": 'billing' from DiagnosisRole display 'Billing' +code "POA-Y": 'Y' from "Present On Admission Indicators" display 'POA-Y' +code "POA-N": 'N' from "Present On Admission Indicators" display 'POA-N' +code "POA-W": 'W' from "Present On Admission Indicators" display 'POA-W' +code "POA-1": '1' from "Present On Admission Indicators" display 'POA-1' + +parameter "Measurement Period" Interval + +context Patient + +define "Inpatient Encounter": + [Encounter: "Encounter Inpatient"] EncounterInpatient + where EncounterInpatient.status = 'finished' + and EncounterInpatient.period ends during day of "Measurement Period" + +/* +@description: Returns an interval of date values extracted from the input interval of date-time values +@comment: This function returns an interval constructed using the `date from` extractor on the start +and end values of the input date-time interval. Note that using a precision specifier such as `day of` +as part of a timing phrase is preferred to communicate intent to perform day-level comparison, as well +as for general readability. +*/ +define function "ToDateInterval"(period Interval): + Interval[date from start of period, date from end of period] + +/* +@description: Calculates the difference in calendar days between the start and end of the given interval. +@deprecated: This function in deprecated. Use the fluent function `lengthInDays()` instead. +*/ +define function "LengthInDays"(Value Interval ): + difference in days between start of Value and end of Value + +/* +@description: Calculates the difference in calendar days between the start and end of the given interval. +*/ +define fluent function lengthInDays(Value Interval ): + difference in days between start of Value and end of Value + +/* +@description: Returns the most recent emergency department visit, if any, that occurs 1 hour or less prior to the given encounter. +@deprecated: This function is deprecated. Use the fluent function `edVisit()` instead. +*/ +define function "ED Visit"(TheEncounter Encounter ): + Last( + [Encounter: "Emergency Department Visit"] EDVisit + where EDVisit.status = 'finished' + and EDVisit.period ends 1 hour or less on or before start of TheEncounter.period + sort by end of period + ) + +/* +@description: Returns the most recent emergency department visit, if any, that occurs 1 hour or less prior to the given encounter. +*/ +define fluent function edVisit(TheEncounter Encounter ): + Last( + [Encounter: "Emergency Department Visit"] EDVisit + where EDVisit.status = 'finished' + and EDVisit.period ends 1 hour or less on or before start of TheEncounter.period + sort by end of period + ) + +/* +@description: Hospitalization returns the total interval for admission to discharge for the given encounter, or for the admission of any immediately prior emergency department visit to the discharge of the given encounter. +@deprecated: This function is deprecated. Use the fluent function `hospitalization()` instead. +*/ +define function "Hospitalization"(TheEncounter Encounter ): + ( "ED Visit"(TheEncounter) ) X + return + if X is null then TheEncounter.period + else Interval[start of X.period, end of TheEncounter.period) + +/* +@description: Hospitalization returns the total interval for admission to discharge for the given encounter, or for the admission of any immediately prior emergency department visit to the discharge of the given encounter. +*/ +define fluent function hospitalization(TheEncounter Encounter ): + (TheEncounter."edVisit"()) X + return + if X is null then TheEncounter.period + else Interval[start of X.period, end of TheEncounter.period] + +/* +@description: Returns list of all locations within an encounter, including locations for immediately prior ED visit. +@deprecated: This function is deprecated. Use the fluent function `hospitalizationLocations()` instead. +*/ +define function "Hospitalization Locations"(TheEncounter Encounter ): + ( "ED Visit"(TheEncounter) ) EDEncounter + return + if EDEncounter is null then TheEncounter.location + else flatten { EDEncounter.location, TheEncounter.location } + +/* +@description: Returns list of all locations within an encounter, including locations for immediately prior ED visit. +*/ +define fluent function hospitalizationLocations(TheEncounter Encounter ): + ( TheEncounter."edVisit"()) EDEncounter + return + if EDEncounter is null then TheEncounter.location + else flatten { EDEncounter.location, TheEncounter.location } + +/* +@description: Returns the length of stay in days (i.e. the number of days between admission and discharge) for the given encounter, or from the admission of any immediately prior emergency department visit to the discharge of the encounter +@deprecated: This function is deprecated. Use the fluent function `hospitalizationLengthOfStay()` instead. +*/ +define function "Hospitalization Length of Stay"(TheEncounter Encounter ): + LengthInDays("Hospitalization"(TheEncounter)) + +/* +@description: Returns the length of stay in days (i.e. the number of days between admission and discharge) for the given encounter, or from the admission of any immediately prior emergency department visit to the discharge of the encounter +*/ +define fluent function hospitalizationLengthOfStay(TheEncounter Encounter ): + TheEncounter."hospitalization"()."lengthInDays"() + +/* +@description: Returns admission time for an encounter or for immediately prior emergency department visit. +@deprecated: This function is deprecated. Use the fluent function `hospitalAdmissionTime()` instead. +*/ +define function "Hospital Admission Time"(TheEncounter Encounter ): + start of "Hospitalization"(TheEncounter) + +/* +@description: Returns admission time for an encounter or for immediately prior emergency department visit. +*/ +define fluent function hospitalAdmissionTime(TheEncounter Encounter ): + start of (TheEncounter."hospitalization"()) + +/* +@description: Hospital Discharge Time returns the discharge time for an encounter +@deprecated: This function is deprecated. Use the fluent function `hospitalDischargeTime()` instead. +*/ +define function "Hospital Discharge Time"(TheEncounter Encounter ): + end of TheEncounter.period + +/* +@description: Hospital Discharge Time returns the discharge time for an encounter +*/ +define fluent function hospitalDischargeTime(TheEncounter Encounter ): + end of TheEncounter.period + +/* +@description: Returns earliest arrival time for an encounter including any prior ED visit. +@deprecated: This function is deprecated. Use the fluent function `hospitalArrivalTime()` instead. +*/ +define function "Hospital Arrival Time"(TheEncounter Encounter ): + start of First( + ("Hospitalization Locations"(TheEncounter) ) HospitalLocation + sort by start of period + ).period + +/* +@description: Returns earliest arrival time for an encounter including any prior ED visit. +*/ +define fluent function hospitalArrivalTime(TheEncounter Encounter ): + start of First( + (TheEncounter."hospitalizationLocations"()) HospitalLocation + sort by start of period + ).period + +/* +@description: Returns the latest departure time for encounter including any prior ED visit. +@deprecated: This function is deprecated. Use the fluent function `hospitalDepartureTime()` instead. +*/ +define function "Hospital Departure Time"(TheEncounter Encounter): + end of Last( + ( "Hospitalization Locations"(TheEncounter) ) HospitalLocation + sort by start of period + ).period + +/* +@description: Returns the latest departure time for encounter including any prior ED visit. +*/ +define fluent function hospitalDepartureTime(TheEncounter Encounter): + end of Last( + (TheEncounter."hospitalizationLocations"()) HospitalLocation + sort by start of period + ).period + +/* +@description: Returns the emergency department arrival time for the encounter. +@deprecated. This function is deprecated. Use the fluent function `emergencyDepartmentArrivalTime()` instead. +*/ +define function "Emergency Department Arrival Time"(TheEncounter Encounter): + start of ( + singleton from ( + ( "Hospitalization Locations"(TheEncounter) ) HospitalLocation + where GetLocation(HospitalLocation.location).type in "Emergency Department Visit" + ) + ).period + +/* +@description: Returns the emergency department arrival time for the encounter. +*/ +define fluent function emergencyDepartmentArrivalTime(TheEncounter Encounter): + start of ( + singleton from ( + ( (TheEncounter."hospitalizationLocations"()) ) HospitalLocation + where GetLocation(HospitalLocation.location).type in "Emergency Department Visit" + ) + ).period + +/* +@description: Hospitalization with Observation and Outpatient Surgery Service returns the total interval from the start of any immediately prior emergency department visit, outpatient surgery visit or observation visit to the discharge of the given encounter. +@deprecated: This function is deprecated. Use the fluent function `hospitalizationWithObservationAndOutpatientSurgeryService()` instead. +*/ +define function "HospitalizationWithObservationAndOutpatientSurgeryService"(TheEncounter "Encounter" ): + TheEncounter Visit + let ObsVisit: Last([Encounter: "Observation Services"] LastObs + where LastObs.status = 'finished' + and LastObs.period ends 1 hour or less on or before start of Visit.period + sort by end of period + ), + VisitStart: Coalesce(start of ObsVisit.period, start of Visit.period), + EDVisit: Last([Encounter: "Emergency Department Visit"] LastED + where LastED.status = 'finished' + and LastED.period ends 1 hour or less on or before VisitStart + sort by end of period + ), + VisitStartWithED: Coalesce(start of EDVisit.period, VisitStart), + OutpatientSurgeryVisit: Last([Encounter: "Outpatient Surgery Service"] LastSurgeryOP + where LastSurgeryOP.period ends 1 hour or less on or before VisitStartWithED + sort by end of period + ) + return Interval[Coalesce(start of OutpatientSurgeryVisit.period, VisitStartWithED), end of Visit.period] + +/* +@description: Hospitalization with Observation and Outpatient Surgery Service returns the total interval from the start of any immediately prior emergency department visit, outpatient surgery visit or observation visit to the discharge of the given encounter. +*/ +define fluent function hospitalizationWithObservationAndOutpatientSurgeryService(TheEncounter "Encounter" ): + TheEncounter Visit + let ObsVisit: Last([Encounter: "Observation Services"] LastObs + where LastObs.status = 'finished' + and LastObs.period ends 1 hour or less on or before start of Visit.period + sort by end of period + ), + VisitStart: Coalesce(start of ObsVisit.period, start of Visit.period), + EDVisit: Last([Encounter: "Emergency Department Visit"] LastED + where LastED.status = 'finished' + and LastED.period ends 1 hour or less on or before VisitStart + sort by end of period + ), + VisitStartWithED: Coalesce(start of EDVisit.period, VisitStart), + OutpatientSurgeryVisit: Last([Encounter: "Outpatient Surgery Service"] LastSurgeryOP + where LastSurgeryOP.period ends 1 hour or less on or before VisitStartWithED + sort by end of period + ) + return Interval[Coalesce(start of OutpatientSurgeryVisit.period, VisitStartWithED), end of Visit.period] + +/* +@description: Hospitalization with Observation returns the total interval from the start of any immediately prior emergency department visit through the observation visit to the discharge of the given encounter +@deprecated: This function is deprecated. Use the fluent function `hospitalizationWithObservation()` instead. +*/ +define function "HospitalizationWithObservation"(TheEncounter Encounter ): + TheEncounter Visit + let ObsVisit: Last([Encounter: "Observation Services"] LastObs + where LastObs.status = 'finished' + and LastObs.period ends 1 hour or less on or before start of Visit.period + sort by end of period + ), + VisitStart: Coalesce(start of ObsVisit.period, start of Visit.period), + EDVisit: Last([Encounter: "Emergency Department Visit"] LastED + where LastED.status = 'finished' + and LastED.period ends 1 hour or less on or before VisitStart + sort by end of period + ) + return Interval[Coalesce(start of EDVisit.period, VisitStart), end of Visit.period] + +/* +@description: Hospitalization with Observation returns the total interval from the start of any immediately prior emergency department visit through the observation visit to the discharge of the given encounter +*/ +define fluent function hospitalizationWithObservation(TheEncounter Encounter ): + TheEncounter Visit + let ObsVisit: Last([Encounter: "Observation Services"] LastObs + where LastObs.status = 'finished' + and LastObs.period ends 1 hour or less on or before start of Visit.period + sort by end of period + ), + VisitStart: Coalesce(start of ObsVisit.period, start of Visit.period), + EDVisit: Last([Encounter: "Emergency Department Visit"] LastED + where LastED.status = 'finished' + and LastED.period ends 1 hour or less on or before VisitStart + sort by end of period + ) + return Interval[Coalesce(start of EDVisit.period, VisitStart), end of Visit.period] + +/* +@description: Hospitalization with Observation Length of Stay returns the length in days from the start of any immediately prior emergency department visit through the observation visit to the discharge of the given encounter +@deprecated: This function is deprecated. Use the fluent function `hospitalizationWithObservationLengthofStay()` instead. +*/ +define function "HospitalizationWithObservationLengthofStay"(TheEncounter "Encounter" ): + "LengthInDays"("HospitalizationWithObservation"(TheEncounter)) + +/* +@description: Hospitalization with Observation Length of Stay returns the length in days from the start of any immediately prior emergency department visit through the observation visit to the discharge of the given encounter +*/ +define fluent function hospitalizationWithObservationLengthofStay(TheEncounter "Encounter" ): + ((TheEncounter."hospitalizationWithObservation"()."lengthInDays"())) + +/* +@description: First Inpatient Intensive Care Unit returns the first intensive care unit for the given encounter, without considering any immediately prior emergency department visit. +@deprecated: This function is deprecated. Use the fluent function `firstInpatientIntensiveCareUnit()` instead. +*/ +define function "FirstInpatientIntensiveCareUnit"(Encounter Encounter ): + First((Encounter.location)HospitalLocation + where GetLocation(HospitalLocation.location).type in "Intensive Care Unit" + and HospitalLocation.period during Encounter.period + sort by start of period + ) + +/* +@description: First Inpatient Intensive Care Unit returns the first intensive care unit for the given encounter, without considering any immediately prior emergency department visit. +*/ +define fluent function firstInpatientIntensiveCareUnit(Encounter Encounter ): + First((Encounter.location)HospitalLocation + where GetLocation(HospitalLocation.location).type in "Intensive Care Unit" + and HospitalLocation.period during Encounter.period + sort by start of period + ) + +/* +@description: Returns the Condition resource referenced by the `diagnosis.condition` element of the Encounter +@deprecated: This function is deprecated. Use the fluent function `encounterDiagnosis()` instead. +*/ +define function "EncounterDiagnosis"(Encounter Encounter ): + Encounter.diagnosis D + return singleton from (([ConditionEncounterDiagnosis] union [ConditionProblemsHealthConcerns]) C where D.condition.references(C.id)) + +/* +@description: Returns the Condition resources referenced by the diagnosis element of the Encounter +*/ +define fluent function encounterDiagnosis(Encounter Encounter ): + Encounter.diagnosis D + return singleton from (([ConditionEncounterDiagnosis] union [ConditionProblemsHealthConcerns]) C where D.condition.references(C.id)) + +/* +@description: Returns the Condition resource for the given reference +@deprecated: This function is deprecated. Use the fluent function `getCondition()` instead +*/ +define function "GetCondition"(reference Reference): + singleton from (([ConditionEncounterDiagnosis] union [ConditionProblemsHealthConcerns]) C where reference.references(C.id)) + +/* +@description: Returns the Condition resource for the given reference +*/ +define fluent function getCondition(reference Reference): + singleton from (([ConditionEncounterDiagnosis] union [ConditionProblemsHealthConcerns]) C where reference.references(C.id)) + +/* +@description: Returns the condition that is specified as the principal diagnosis for the encounter +@deprecated: This function is deprecated. Use the fluent function `principalDiagnosis()` instead. +*/ +define function "PrincipalDiagnosis"(Encounter Encounter): + singleton from ( + (Encounter.diagnosis D where D.rank = 1 and D.use ~ "Billing") PD + return singleton from (([ConditionEncounterDiagnosis] union [ConditionProblemsHealthConcerns]) C where PD.condition.references(C.id)) + ) + +/* +@description: Returns the condition that is specified as the principal diagnosis for the encounter +@deprecated: This function is deprecated. Use the claimDiagnosis function for principal diagnosis and present on admission use cases. See +[ClaimDiagnoses](https://github.com/cqframework/CQL-Formatting-and-Usage-Wiki/blob/master/Source/Cooking%20With%20CQL/88/ClaimDiagnoses.md) +discussion for more information. +*/ +define fluent function principalDiagnosis(Encounter Encounter): + singleton from ((Encounter.diagnosis D where D.rank = 1 and D.use ~ "Billing") PD + return singleton from (([ConditionEncounterDiagnosis] union [ConditionProblemsHealthConcerns]) C where PD.condition.references(C.id)) + ) + +/* +@description: Returns the claim diagnosis elements for the given encounter +*/ +define fluent function claimDiagnosis(encounter Encounter): + encounter E + let + claim: ([Claim] C where C.status = 'active' and C.use = 'claim' and exists (C.item I where I.encounter.references(E))), + claimItem: (claim.item I where I.encounter.references(E)) + return claim.diagnosis D where D.sequence in claimItem.diagnosisSequence + +/* +@description: Returns the Location resource specified by the given reference +@deprecated: This function is deprecated. Use the fluent function `getLocation()` instead. +*/ +define function "GetLocation"(reference Reference): + singleton from ( + [Location] L where reference.references(L) + ) + +/* +@description: Returns the Location resource specified by the given reference. +*/ +define fluent function getLocation(reference Reference): + singleton from ( + [Location] L where reference.references(L) + ) + +/* +@description: Returns the medication code for the given MedicationRequest +@deprecated: This function is deprecated. Use the fluent function `getMedicationCode()` instead. +*/ +define function "GetMedicationCode"(request MedicationRequest): + if request.medication is Concept then + request.medication as Concept + else + (singleton from ([Medication] M where request.medication.references(M))).code + +/* +@description: Returns the medication code for the given MedicationRequest +*/ +define fluent function getMedicationCode(request MedicationRequest): + if request.medication is Concept then + request.medication as Concept + else + (singleton from ([Medication] M where request.medication.references(M))).code \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/FHIRHelpersToConceptError.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/FHIRHelpersToConceptError.cql new file mode 100644 index 000000000..59c5dd5c4 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/FHIRHelpersToConceptError.cql @@ -0,0 +1,13 @@ +library FHIRHelpersToConceptError version '0.2.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers + +parameter "Measurement Period" Interval default Interval[@2025-01-01T00:00:00Z, @2026-01-01T00:00:00Z) + +context Patient + +define "Minimal Repro": + [MedicationNotRequested] NoStatinTherapyOrdered + return NoStatinTherapyOrdered.reasonCode \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/FHIRHelpersToConceptError.xml b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/FHIRHelpersToConceptError.xml new file mode 100644 index 000000000..e3e610281 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/FHIRHelpersToConceptError.xml @@ -0,0 +1,144 @@ + + + + + + library FHIRHelpersToConceptError version '0.2.000' + + + + + + + + + + using + + QICore + + version '6.0.0' + + + + + + + + + include + + FHIRHelpers + + version '4.0.1' called FHIRHelpers + + + + + + + + + parameter "Measurement Period" + + Interval< + + DateTime + + > + + default + + Interval[@2025-01-01T00:00:00Z, @2026-01-01T00:00:00Z) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + define "Minimal Repro": + + + + + + + [MedicationNotRequested] + + + NoStatinTherapyOrdered + + + + + + return + + + NoStatinTherapyOrdered + + . + + reasonCode + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/Hospice-7.0.000.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/Hospice-7.0.000.cql new file mode 100644 index 000000000..12e9be3bc --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/Hospice-7.0.000.cql @@ -0,0 +1,54 @@ +/* +NOTE: Preliminary QICore 6.0.0 uplift to support connectathon 38 testing +*/ +library Hospice version '7.0.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers +include QICoreCommon version '3.0.000' called QICoreCommon +include Status version '2.0.000' called Status + +codesystem "LOINC": 'http://loinc.org' +codesystem "SNOMEDCT": 'http://snomed.info/sct' + +valueset "Encounter Inpatient": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307' +valueset "Hospice Care Ambulatory": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1584' +valueset "Hospice Encounter": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.1003' +valueset "Hospice Diagnosis": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.1165' + +code "Discharge to healthcare facility for hospice care (procedure)": '428371000124100' from "SNOMEDCT" display 'Discharge to healthcare facility for hospice care (procedure)' +code "Discharge to home for hospice care (procedure)": '428361000124107' from "SNOMEDCT" display 'Discharge to home for hospice care (procedure)' +code "Hospice care [Minimum Data Set]": '45755-6' from "LOINC" display 'Hospice care [Minimum Data Set]' +code "Yes (qualifier value)": '373066001' from "SNOMEDCT" display 'Yes (qualifier value)' + +parameter "Measurement Period" Interval + +context Patient + +define "Has Hospice Services": + exists ((([Encounter: type in "Encounter Inpatient"]).isEncounterPerformed()) InpatientEncounter + where (InpatientEncounter.hospitalization.dischargeDisposition ~ "Discharge to home for hospice care (procedure)" + or InpatientEncounter.hospitalization.dischargeDisposition ~ "Discharge to healthcare facility for hospice care (procedure)" + ) + and InpatientEncounter.period.toInterval() ends during day of "Measurement Period" + ) + or exists ((([Encounter: type in "Hospice Encounter"]).isEncounterPerformed()) HospiceEncounter + where HospiceEncounter.period.toInterval() overlaps day of "Measurement Period" + ) + or exists ((([ObservationScreeningAssessment: "Hospice care [Minimum Data Set]"]).isAssessmentPerformed()) HospiceAssessment + where (HospiceAssessment as ObservationScreeningAssessment).value ~ "Yes (qualifier value)" + and HospiceAssessment.effective.toInterval() overlaps day of "Measurement Period" + ) + or exists ((([ServiceRequest: "Hospice Care Ambulatory"]).isInterventionOrder()) HospiceOrder + where HospiceOrder.authoredOn.toInterval() during day of "Measurement Period" + // and HospiceOrder.doNotPerform is not true + // https://oncprojectracking.healthit.gov/support/browse/CQLIT-447 + ) + or exists ((([Procedure: "Hospice Care Ambulatory"]).isInterventionPerformed()) HospicePerformed + where HospicePerformed.performed.toInterval() overlaps day of "Measurement Period" + ) + or exists ((([ConditionProblemsHealthConcerns: "Hospice Diagnosis"]) + union ([ConditionEncounterDiagnosis: "Hospice Diagnosis"])) HospiceCareDiagnosis + where HospiceCareDiagnosis.prevalenceInterval() overlaps day of "Measurement Period" + ) \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PCSBMIScreenAndFollowUpFHIR.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PCSBMIScreenAndFollowUpFHIR.cql new file mode 100644 index 000000000..8ae6590ff --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PCSBMIScreenAndFollowUpFHIR.cql @@ -0,0 +1,203 @@ +/* +NOTE: Preliminary QICore 6.0.0 uplift to support connectathon 38 testing +*/ +library PCSBMIScreenAndFollowUpFHIR version '0.2.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers +include QICoreCommon version '3.0.000' called QICoreCommon +include SupplementalDataElements version '4.0.000' called SDE +include Hospice version '7.0.000' called Hospice +include PalliativeCare version '2.0.000' called PalliativeCare + +codesystem "ActCode": 'http://terminology.hl7.org/CodeSystem/v3-ActCode' +codesystem "ICD10CM": 'http://hl7.org/fhir/sid/icd-10-cm' +codesystem "LOINC": 'http://loinc.org' +codesystem "ObservationCategoryCodes": 'http://terminology.hl7.org/CodeSystem/observation-category' + +valueset "Encounter to Evaluate BMI": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1751' +valueset "Follow Up for Above Normal BMI": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1525' +valueset "Follow Up for Below Normal BMI": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1528' +valueset "Medical Reason": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1007' +valueset "Medications for Above Normal BMI": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1561' +valueset "Medications for Below Normal BMI": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1562' +valueset "Overweight or Obese": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.502' +valueset "Patient Declined": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1582' +valueset "Pregnancy or Other Related Diagnoses": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1623' +valueset "Referrals Where Weight Assessment May Occur": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1527' +valueset "Underweight": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1563' + +code "Body mass index (BMI) [Ratio]": '39156-5' from "LOINC" display 'Body mass index (BMI) [Ratio]' +code "Encounter for palliative care": 'Z51.5' from "ICD10CM" display 'Encounter for palliative care' +code "Functional Assessment of Chronic Illness Therapy - Palliative Care Questionnaire (FACIT-Pal)": '71007-9' from "LOINC" display 'Functional Assessment of Chronic Illness Therapy - Palliative Care Questionnaire (FACIT-Pal)' +code "virtual": 'VR' from "ActCode" display 'virtual' + +parameter "Measurement Period" Interval default Interval[@2025-01-01T00:00:00Z, @2026-01-01T00:00:00Z) + +context Patient + +define "Initial Population": + exists "Qualifying Encounter during Day of Measurement Period" QualifyingEncounter + where "AgeInYearsAt"(date from start of QualifyingEncounter.period) >= 18 + +define "Denominator": + "Initial Population" + +define "Denominator Exclusions": + Hospice."Has Hospice Services" + or PalliativeCare."Has Palliative Care in the Measurement Period" + or exists "Is Pregnant during Day of Measurement Period" + +define "Numerator": + exists "High BMI and Follow up Provided" + or exists "Low BMI and Follow up Provided" + or "Has Normal BMI" + +define "Denominator Exceptions": + exists "Medical Reason for Not Documenting a Follow up Plan for Low or High BMI" + or exists "Medical Reason or Patient Reason for Not Performing BMI Exam" + +define "BMI during Measurement Period": + [USCoreBMIProfile] BMI + where BMI.value > 0 'kg/m2' + and BMI.status in { 'final', 'amended', 'corrected' } + and BMI.effective.toInterval ( ) during day of "Measurement Period" + +define "Documented Low BMI during Measurement Period": + "BMI during Measurement Period" BMI + where BMI.effective.toInterval ( ) during day of "Measurement Period" + and BMI.value < 18.5 'kg/m2' + +define "Documented High BMI during Measurement Period": + "BMI during Measurement Period" BMI + where BMI.effective.toInterval ( ) during day of "Measurement Period" + and BMI.value >= 25 'kg/m2' + +define "Has Normal BMI": + exists ( "BMI during Measurement Period" BMI + where BMI.value >= 18.5 'kg/m2' + and BMI.value < 25 'kg/m2' + ) + and not ( exists "Documented High BMI during Measurement Period" + or exists "Documented Low BMI during Measurement Period" + ) + +define "High BMI and Follow up Provided": + "Documented High BMI during Measurement Period" HighBMI + with ( "High BMI Interventions Ordered" + union "High BMI Interventions Performed" ) HighBMIInterventions + such that Coalesce(HighBMIInterventions.performed.toInterval(), HighBMIInterventions.authoredOn.toInterval()) starts during day of "Measurement Period" + +define "High BMI Interventions Ordered": + ( ( [ServiceRequest: "Follow Up for Above Normal BMI"] + union [ServiceRequest: "Referrals Where Weight Assessment May Occur"] + union [MedicationRequest: "Medications for Above Normal BMI"] ) HighInterventionsOrdered + where HighInterventionsOrdered.reasonCode in "Overweight or Obese" + or ( exists [ConditionProblemsHealthConcerns: "Overweight or Obese"] OverweightObese + where ( OverweightObese.isProblemListItem ( ) + or OverweightObese.isHealthConcern ( ) + ) + and OverweightObese.isActive ( ) + and OverweightObese.prevalenceInterval ( ) starts before or on day of HighInterventionsOrdered.authoredOn + ) + ) + +define "High BMI Interventions Performed": + ( [Procedure: "Follow Up for Above Normal BMI"] HighInterventionsPerformed + where HighInterventionsPerformed.reasonCode in "Overweight or Obese" + or ( exists [ConditionProblemsHealthConcerns: "Overweight or Obese"] OverweightObese + where OverweightObese.prevalenceInterval ( ) starts before or on day of HighInterventionsPerformed.performed + and not ( OverweightObese.prevalenceInterval ( ) ends before day of HighInterventionsPerformed.performed ) + ) + ) + +define "Medical Reason or Patient Reason for Not Performing BMI Exam": + [ObservationCancelled: code = "Body mass index (BMI) [Ratio]"] NoBMI + with "Qualifying Encounter during Day of Measurement Period" QualifyingEncounter + such that NoBMI.effective.toInterval ( ) ends same day as start of QualifyingEncounter.period + where NoBMI.status = 'cancelled' + and exists(NoBMI.extension E + where E.url = 'http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-notDoneReason' + and (E.value as Code in "Patient Declined" + or E.value as Code in "Medical Reason" + ) + ) + +define "Low BMI Interventions Ordered": + ( ( [ServiceRequest: "Follow Up for Below Normal BMI"] + union [ServiceRequest: "Referrals Where Weight Assessment May Occur"] + union [MedicationRequest: "Medications for Below Normal BMI"] ) LowInterventionsOrdered + where LowInterventionsOrdered.reasonCode in "Underweight" + or ( exists [ConditionProblemsHealthConcerns: "Underweight"] Under + where ( Under.isHealthConcern ( ) ) + and Under.isActive ( ) + and Under.prevalenceInterval ( ) starts before or on day of LowInterventionsOrdered.authoredOn + and LowInterventionsOrdered.authoredOn during day of "Measurement Period" + ) + ) + +define "Low BMI Interventions Performed": + ( [Procedure: "Follow Up for Below Normal BMI"] LowInterventionsPerformed + where LowInterventionsPerformed.reasonCode in "Underweight" + and LowInterventionsPerformed.status = 'completed' + or ( exists [ConditionProblemsHealthConcerns: "Underweight"] Under + where ( Under.isHealthConcern ( ) ) + and Under.isActive ( ) + and Under.prevalenceInterval ( ) starts before or on day of LowInterventionsPerformed.performed.toInterval ( ) + and LowInterventionsPerformed.performed.toInterval ( ) during day of "Measurement Period" + and not ( Under.prevalenceInterval ( ) ends before day of LowInterventionsPerformed.performed.toInterval ( ) ) + ) + ) + +define "Low BMI and Follow up Provided": + ( "Documented Low BMI during Measurement Period" LowBMI + with ( "Low BMI Interventions Ordered" + union "Low BMI Interventions Performed" ) LowBMIInterventions + such that Coalesce(LowBMIInterventions.performed.toInterval(), LowBMIInterventions.authoredOn.toInterval()) starts during day of "Measurement Period" + ) + +define "Medical Reason for Not Documenting a Follow up Plan for Low or High BMI": + ( ( [ServiceNotRequested: "Referrals Where Weight Assessment May Occur"] + union [ServiceNotRequested: "Follow Up for Above Normal BMI"] + union [ServiceNotRequested: "Follow Up for Below Normal BMI"] ) NoBMIFollowUp + with "Qualifying Encounter during Day of Measurement Period" QualifyingEncounter + such that NoBMIFollowUp.authoredOn same day as start of QualifyingEncounter.period + where NoBMIFollowUp.status ~ 'completed' + and exists(NoBMIFollowUp.extension E + where E.url = 'http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-doNotPerformReason' + and E.value as Code in "Medical Reason" + ) + ) + union ( ( [MedicationNotRequested: "Medications for Above Normal BMI"] + union [MedicationNotRequested: "Medications for Below Normal BMI"] ) NoBMIFollowUp + with "Qualifying Encounter during Day of Measurement Period" QualifyingEncounter + such that NoBMIFollowUp.authoredOn same day as start of QualifyingEncounter.period + where NoBMIFollowUp.status ~ 'completed' + and NoBMIFollowUp.reasonCode in "Medical Reason" + ) + +define "Is Pregnant during Day of Measurement Period": + ([ConditionProblemsHealthConcerns: "Pregnancy or Other Related Diagnoses"] + union [ConditionEncounterDiagnosis: "Pregnancy or Other Related Diagnoses"]) PregnancyDiagnosis + with "Qualifying Encounter during Day of Measurement Period" QualifyingEncounter + such that PregnancyDiagnosis.clinicalStatus ~ QICoreCommon."active" + and PregnancyDiagnosis.prevalenceInterval ( ) overlaps day of "Measurement Period" + +define "Qualifying Encounter during Day of Measurement Period": + [Encounter: type in "Encounter to Evaluate BMI"] BMIEncounter + where BMIEncounter.period during day of "Measurement Period" + and BMIEncounter.class !~ "virtual" + and BMIEncounter.status = 'finished' + +define "SDE Ethnicity": + SDE."SDE Ethnicity" + +define "SDE Payer": + SDE."SDE Payer" + +define "SDE Race": + SDE."SDE Race" + +define "SDE Sex": + SDE."SDE Sex" \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PCSDepressionScreenAndFollowUpFHIR.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PCSDepressionScreenAndFollowUpFHIR.cql new file mode 100644 index 000000000..2977adf36 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PCSDepressionScreenAndFollowUpFHIR.cql @@ -0,0 +1,220 @@ +/* +NOTE: Preliminary QICore 6.0.0 uplift to support connectathon 38 testing +*/ +library PCSDepressionScreenAndFollowUpFHIR version '0.2.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers +include QICoreCommon version '3.0.000' called QICoreCommon +include SupplementalDataElements version '4.0.000' called SDE + +codesystem "LOINC": 'http://loinc.org' +codesystem "SNOMEDCT": 'http://snomed.info/sct' + +valueset "Adolescent Depression Medications": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1567' +valueset "Adult Depression Medications": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1566' +valueset "Bipolar Disorder": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.67.1.101.1.128' +valueset "Encounter to Screen for Depression": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1916' +valueset "Follow Up for Adolescent Depression": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1569' +valueset "Follow Up for Adult Depression": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1568' +valueset "Medical Reason": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1007' +valueset "Physical Therapy Evaluation": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1022' +valueset "Referral for Adolescent Depression": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1570' +valueset "Referral for Adult Depression": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1571' +valueset "Telephone Visits": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1080' + +code "Adolescent depression screening assessment": '73831-0' from "LOINC" display 'Adolescent depression screening assessment' +code "Adult depression screening assessment": '73832-8' from "LOINC" display 'Adult depression screening assessment' +code "Depression screening declined (situation)": '720834000' from "SNOMEDCT" display 'Depression screening declined (situation)' +code "Depression screening negative (finding)": '428171000124102' from "SNOMEDCT" display 'Depression screening negative (finding)' +code "Depression screening positive (finding)": '428181000124104' from "SNOMEDCT" display 'Depression screening positive (finding)' + +parameter "Measurement Period" Interval default Interval[@2025-01-01T00:00:00Z, @2026-01-01T00:00:00Z) + +context Patient + +define "Initial Population": + "Patient Age 12 Years or Older at Start of Measurement Period" + and exists ( "Qualifying Encounter During Measurement Period" ) + +define "Denominator": + "Initial Population" + +define "Denominator Exclusions": + exists "History of Bipolar Diagnosis Before Qualifying Encounter" + +define "Numerator": + ( "Patient Age 12 to 16 Years at Start of Measurement Period" + and ( "Has Most Recent Adolescent Screening Negative" + or exists "Most Recent Adolescent Depression Screening Positive and Follow Up Provided" + ) + ) + or ( "Patient Age 17 Years at Start of Measurement Period" + and ( "Has Most Recent Adolescent Screening Negative" + or exists "Most Recent Adolescent Depression Screening Positive and Follow Up Provided" + or "Has Most Recent Adult Screening Negative" + or exists "Most Recent Adult Depression Screening Positive and Follow Up Provided" + ) + ) + or ( "Patient Age 18 Years or Older at Start of Measurement Period" + and ( "Has Most Recent Adult Screening Negative" + or exists "Most Recent Adult Depression Screening Positive and Follow Up Provided" + ) + ) + +define "Denominator Exceptions": + ( exists "Medical or Patient Reason for Not Screening Adolescent for Depression" + and not "Has Adolescent Depression Screening" + ) + or ( exists "Medical or Patient Reason for Not Screening Adult for Depression" + and not "Has Adult Depression Screening" + ) + +define "Follow Up Intervention for Positive Adolescent Depression Screening": + ( ( [MedicationRequest: "Adolescent Depression Medications"] AdolescentMed + where AdolescentMed.status in { 'active', 'completed' } + and AdolescentMed.intent = 'order' + ) + union ( [ServiceRequest: "Referral for Adolescent Depression"] AdolescentReferral + where AdolescentReferral.status in { 'active', 'completed' } + ) + union ( [Procedure: "Follow Up for Adolescent Depression"] AdolescentFollowUp + where AdolescentFollowUp.status = 'completed' + ) + ) + +define "Follow Up Intervention for Positive Adult Depression Screening": + ( ( [MedicationRequest: "Adult Depression Medications"] AdultMed + where AdultMed.status in { 'active', 'completed' } + and AdultMed.intent = 'order' + ) + union ( [ServiceRequest: "Referral for Adult Depression"] AdultReferral + where AdultReferral.status in { 'active', 'completed' } + ) + union ( [Procedure: "Follow Up for Adult Depression"] AdultFollowUp + where AdultFollowUp.status = 'completed' + ) + ) + +define "Has Adolescent Depression Screening": + exists ( [ObservationScreeningAssessment: "Adolescent depression screening assessment"] AdolescentScreening + with "Qualifying Encounter During Measurement Period" QualifyingEncounter + such that AdolescentScreening.effective.toInterval() 14 days or less on or before day of start of QualifyingEncounter.period + and AdolescentScreening.value is not null + and AdolescentScreening.status = 'final' + ) + +define "Has Adult Depression Screening": + exists ( [ObservationScreeningAssessment: "Adult depression screening assessment"] AdultScreening + with "Qualifying Encounter During Measurement Period" QualifyingEncounter + such that AdultScreening.effective.toInterval() 14 days or less on or before day of start of QualifyingEncounter.period + and AdultScreening.value is not null + and AdultScreening.status = 'final' + ) + +define "Has Most Recent Adolescent Screening Negative": + ( "Most Recent Adolescent Depression Screening" AdolescentScreen + where AdolescentScreen.value ~ "Depression screening negative (finding)" + ) is not null + +define "Has Most Recent Adult Screening Negative": + ( "Most Recent Adult Depression Screening" AdultScreen + where AdultScreen.value ~ "Depression screening negative (finding)" + ) is not null + +define "History of Bipolar Diagnosis Before Qualifying Encounter": + [ConditionProblemsHealthConcerns: "Bipolar Disorder"] BipolarDiagnosis + with "Qualifying Encounter During Measurement Period" QualifyingEncounter + such that BipolarDiagnosis.prevalenceInterval() starts before QualifyingEncounter.period + +define "Medical or Patient Reason for Not Screening Adolescent for Depression": + [ObservationCancelled: code ~ "Adolescent depression screening assessment"] NoAdolescentScreen + with "Qualifying Encounter During Measurement Period" QualifyingEncounter + such that NoAdolescentScreen.issued during QualifyingEncounter.period + where ( NoAdolescentScreen.notDoneReason ~ "Depression screening declined (situation)" + or NoAdolescentScreen.notDoneReason in "Medical Reason" + ) + and NoAdolescentScreen.status = 'cancelled' + +define "Medical or Patient Reason for Not Screening Adult for Depression": + [ObservationCancelled: code ~ "Adult depression screening assessment"] NoAdultScreen + with "Qualifying Encounter During Measurement Period" QualifyingEncounter + such that NoAdultScreen.issued during QualifyingEncounter.period + where ( NoAdultScreen.notDoneReason ~ "Depression screening declined (situation)" + or NoAdultScreen.notDoneReason in "Medical Reason" + ) + and NoAdultScreen.status = 'cancelled' + +define "Most Recent Adolescent Depression Screening": + Last([ObservationScreeningAssessment: "Adolescent depression screening assessment"] AdolescentDepressionScreening + with "Qualifying Encounter During Measurement Period" QualifyingEncounter + such that AdolescentDepressionScreening.effective.toInterval() 14 days or less on or before day of start of QualifyingEncounter.period + and AdolescentDepressionScreening.value is not null + and AdolescentDepressionScreening.status = 'final' + sort by start of effective.toInterval() + ) + +define "Most Recent Adult Depression Screening": + Last([ObservationScreeningAssessment: "Adult depression screening assessment"] AdultDepressionScreening + with "Qualifying Encounter During Measurement Period" QualifyingEncounter + such that AdultDepressionScreening.effective.toInterval() 14 days or less on or before day of start of QualifyingEncounter.period + and AdultDepressionScreening.value is not null + and AdultDepressionScreening.status = 'final' + sort by start of effective.toInterval() + ) + +define "Most Recent Adolescent Depression Screening Positive and Follow Up Provided": + from + "Most Recent Adolescent Depression Screening" LastAdolescentScreen, + "Follow Up Intervention for Positive Adolescent Depression Screening" FollowUpPositiveAdolescentScreen, + "Qualifying Encounter During Measurement Period" QualifyingEncounter + where LastAdolescentScreen.effective.toInterval ( ) starts 14 days or less on or before day of start of QualifyingEncounter.period + and LastAdolescentScreen.value ~ "Depression screening positive (finding)" + and ( start of FollowUpPositiveAdolescentScreen.performed.toInterval ( ) during QualifyingEncounter.period + or FollowUpPositiveAdolescentScreen.authoredOn 2 days or less on or after day of end of QualifyingEncounter.period + ) + and ( Coalesce(start of FollowUpPositiveAdolescentScreen.performed.toInterval(), FollowUpPositiveAdolescentScreen.authoredOn) during day of "Measurement Period" ) + +define "Most Recent Adult Depression Screening Positive and Follow Up Provided": + from + "Most Recent Adult Depression Screening" LastAdultScreen, + "Follow Up Intervention for Positive Adult Depression Screening" FollowUpPositiveAdultScreen, + "Qualifying Encounter During Measurement Period" QualifyingEncounter + where LastAdultScreen.effective.toInterval() starts 14 days or less on or before day of start of QualifyingEncounter.period + and LastAdultScreen.value ~ "Depression screening positive (finding)" + and ( start of FollowUpPositiveAdultScreen.performed.toInterval() during QualifyingEncounter.period + or FollowUpPositiveAdultScreen.authoredOn 2 days or less on or after day of end of QualifyingEncounter.period + ) + and ( Coalesce(start of FollowUpPositiveAdultScreen.performed.toInterval(), FollowUpPositiveAdultScreen.authoredOn) during day of "Measurement Period" ) + +define "Patient Age 12 to 16 Years at Start of Measurement Period": + AgeInYearsAt(date from start of "Measurement Period") in Interval[12, 16] + +define "Patient Age 12 Years or Older at Start of Measurement Period": + AgeInYearsAt(date from start of "Measurement Period") >= 12 + +define "Patient Age 17 Years at Start of Measurement Period": + AgeInYearsAt(date from start of "Measurement Period") = 17 + +define "Patient Age 18 Years or Older at Start of Measurement Period": + AgeInYearsAt(date from start of "Measurement Period") >= 18 + +define "Qualifying Encounter During Measurement Period": + ( [Encounter: type in "Encounter to Screen for Depression"] + union [Encounter: type in "Physical Therapy Evaluation"] + union [Encounter: type in "Telephone Visits"] ) QualifyingEncounter + where QualifyingEncounter.period during day of "Measurement Period" + and QualifyingEncounter.status = 'finished' + +define "SDE Ethnicity": + SDE."SDE Ethnicity" + +define "SDE Payer": + SDE."SDE Payer" + +define "SDE Race": + SDE."SDE Race" + +define "SDE Sex": + SDE."SDE Sex" \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PalliativeCare-2.0.000.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PalliativeCare-2.0.000.cql new file mode 100644 index 000000000..096a80745 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/PalliativeCare-2.0.000.cql @@ -0,0 +1,36 @@ +/* +NOTE: Preliminary QICore 6.0.0 uplift to support connectathon 38 testing +*/ +library PalliativeCare version '2.0.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers +include QICoreCommon version '3.0.000' called QICoreCommon +include Status version '2.0.000' called Status + +codesystem "LOINC": 'http://loinc.org' + +valueset "Palliative Care Encounter": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1090' +valueset "Palliative Care Intervention": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1135' +valueset "Palliative Care Diagnosis": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.1167' + +code "Functional Assessment of Chronic Illness Therapy - Palliative Care Questionnaire (FACIT-Pal)": '71007-9' from "LOINC" display 'Functional Assessment of Chronic Illness Therapy - Palliative Care Questionnaire (FACIT-Pal)' + +parameter "Measurement Period" Interval + +context Patient + +define "Has Palliative Care in the Measurement Period": + exists ((([ObservationScreeningAssessment: "Functional Assessment of Chronic Illness Therapy - Palliative Care Questionnaire (FACIT-Pal)"]).isAssessmentPerformed()) PalliativeAssessment + where PalliativeAssessment.effective.toInterval() overlaps day of "Measurement Period" + ) + or exists (([ConditionProblemsHealthConcerns: "Palliative Care Diagnosis"]) PalliativeDiagnosis + where PalliativeDiagnosis.prevalenceInterval() overlaps day of "Measurement Period" + ) + or exists ((([Encounter: type in "Palliative Care Encounter"]).isEncounterPerformed()) PalliativeEncounter + where PalliativeEncounter.period.toInterval() overlaps day of "Measurement Period" + ) + or exists ((([Procedure: "Palliative Care Intervention"]).isInterventionPerformed()) PalliativeIntervention + where PalliativeIntervention.performed.toInterval() overlaps day of "Measurement Period" + ) \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/QICoreCommon-3.0.000.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/QICoreCommon-3.0.000.cql new file mode 100644 index 000000000..3adabe2f6 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/QICoreCommon-3.0.000.cql @@ -0,0 +1,829 @@ +/* +@update: JKR 2024-04-02 -> +Check display code accuracy against source system +Ensure all fluent functions are not referencing any deprecated functions. +https://oncprojectracking.healthit.gov/support/browse/CQLIT-414 +*/ + +library QICoreCommon version '3.0.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' + +codesystem "LOINC": 'http://loinc.org' +codesystem "SNOMEDCT": 'http://snomed.info/sct' +codesystem "ActCode": 'http://terminology.hl7.org/CodeSystem/v3-ActCode' +codesystem "RoleCode": 'http://terminology.hl7.org/CodeSystem/v3-RoleCode' +codesystem "Diagnosis Role": 'http://terminology.hl7.org/CodeSystem/diagnosis-role' +codesystem "RequestIntent": 'http://hl7.org/fhir/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' +codesystem "ObservationCategoryCodes": 'http://terminology.hl7.org/CodeSystem/observation-category' +codesystem "USCoreObservationCategoryExtensionCodes": 'http://hl7.org/fhir/us/core/CodeSystem/us-core-observation-category' +codesystem "ConditionCategory": 'http://terminology.hl7.org/CodeSystem/condition-category' +codesystem "USCoreConditionCategoryExtensionCodes": 'http://hl7.org/fhir/us/core/CodeSystem/condition-category' + +code "Birthdate": '21112-8' from "LOINC" display 'Birth date' +code "Dead": '419099009' from "SNOMEDCT" display 'Dead' +code "ER": 'ER' from "RoleCode" display 'Emergency room' +code "ICU": 'ICU' from "RoleCode" display 'Intensive care unit' +code "Billing": 'billing' from "Diagnosis Role" display 'Billing' + +// Encounter Class Codes +code "ambulatory": 'AMB' from ActCode display 'ambulatory' +code "emergency": 'EMER' from ActCode display 'emergency' +code "field": 'FLD' from ActCode display 'field' +code "home health": 'HH' from ActCode display 'home health' +code "inpatient encounter": 'IMP' from ActCode display 'inpatient encounter' +code "inpatient acute": 'ACUTE' from ActCode display 'inpatient acute' +code "inpatient non-acute": 'NONAC' from ActCode display 'inpatient non-acute' +code "observation encounter": 'OBSENC' from ActCode display 'observation encounter' +code "pre-admission": 'PRENC' from ActCode display 'pre-admission' +code "short stay": 'SS' from ActCode display 'short stay' +code "virtual": 'VR' from ActCode display 'Virtual' + +// Condition Category Codes +code "problem-list-item": 'problem-list-item' from "ConditionCategory" display 'Problem List Item' +code "encounter-diagnosis": 'encounter-diagnosis' from "ConditionCategory" display 'Encounter Diagnosis' +code "health-concern": 'health-concern' from "USCoreConditionCategoryExtensionCodes" display 'Health Concern' + +// Condition Clinical Status Codes - Consider value sets for these +code "active": 'active' from "ConditionClinicalStatusCodes" display 'Active' +code "recurrence": 'recurrence' from "ConditionClinicalStatusCodes" display 'Recurrence' +code "relapse": 'relapse' from "ConditionClinicalStatusCodes" display 'Relapse' +code "inactive": 'inactive' from "ConditionClinicalStatusCodes" display 'Inactive' +code "remission": 'remission' from "ConditionClinicalStatusCodes" display 'Remission' +code "resolved": 'resolved' from "ConditionClinicalStatusCodes" display 'Resolved' + +// Condition Verification Status Codes - Consider value sets for these +code "unconfirmed": 'unconfirmed' from ConditionVerificationStatusCodes display 'Unconfirmed' +code "provisional": 'provisional' from ConditionVerificationStatusCodes display 'Provisional' +code "differential": 'differential' from ConditionVerificationStatusCodes display 'Differential' +code "confirmed": 'confirmed' from ConditionVerificationStatusCodes display 'Confirmed' +code "refuted": 'refuted' from ConditionVerificationStatusCodes display 'Refuted' +code "entered-in-error": 'entered-in-error' from ConditionVerificationStatusCodes display 'Entered in Error' + +code "allergy-active": 'active' from "AllergyIntoleranceClinicalStatusCodes" display 'Active' +code "allergy-inactive": 'inactive' from "AllergyIntoleranceClinicalStatusCodes" display 'Inactive' +code "allergy-resolved": 'resolved' from "AllergyIntoleranceClinicalStatusCodes" display 'Resolved' + +// Allergy/Intolerance Verification Status Codes - Consider value sets for these +code "allergy-unconfirmed": 'unconfirmed' from AllergyIntoleranceVerificationStatusCodes display 'Unconfirmed' +code "allergy-confirmed": 'confirmed' from AllergyIntoleranceVerificationStatusCodes display 'Confirmed' +code "allergy-refuted": 'refuted' from AllergyIntoleranceVerificationStatusCodes display 'Refuted' + +// MedicationRequest Category Codes +code "Inpatient": 'inpatient' from "MedicationRequestCategory" display 'Inpatient' +code "Outpatient": 'outpatient' from "MedicationRequestCategory" display 'Outpatient' +code "Community": 'community' from "MedicationRequestCategory" display 'Community' +code "Discharge": 'discharge' from "MedicationRequestCategory" display 'Discharge' + +// Diagnosis Role Codes +code "AD": 'AD' from "Diagnosis Role" display 'Admission diagnosis' +code "DD": 'DD' from "Diagnosis Role" display 'Discharge diagnosis' +code "CC": 'CC' from "Diagnosis Role" display 'Chief complaint' +code "CM": 'CM' from "Diagnosis Role" display 'Comorbidity diagnosis' +code "pre-op": 'pre-op' from "Diagnosis Role" display 'pre-op diagnosis' +code "post-op": 'post-op' from "Diagnosis Role" display 'post-op diagnosis' +code "billing": 'billing' from "Diagnosis Role" display 'Billing' + +// Observation Category Codes +code "social-history": 'social-history' from "ObservationCategoryCodes" display 'Social History' +code "vital-signs": 'vital-signs' from "ObservationCategoryCodes" display 'Vital Signs' +code "imaging": 'imaging' from "ObservationCategoryCodes" display 'Imaging' +code "laboratory": 'laboratory' from "ObservationCategoryCodes" display 'Laboratory' +code "procedure": 'procedure' from "ObservationCategoryCodes" display 'Procedure' +code "survey": 'survey' from "ObservationCategoryCodes" display 'Survey' +code "exam": 'exam' from "ObservationCategoryCodes" display 'Exam' +code "therapy": 'therapy' from "ObservationCategoryCodes" display 'Therapy' +code "activity": 'activity' from "ObservationCategoryCodes" display 'Activity' + +context Patient + +/* Candidates for FHIRCommon */ + +/* +@description: Returns true if the given condition has a clinical status of active, recurrence, or relapse +*/ +define fluent function isActive(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">): + condition.clinicalStatus ~ "active" + or condition.clinicalStatus ~ "recurrence" + or condition.clinicalStatus ~ "relapse" + +/* +@description: Returns true if the given condition has the given category +*/ +define fluent function hasCategory(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">, category Code): + exists (condition.category C + where C ~ category + ) + +/* +@description: Returns true if the given condition is a problem list item. +*/ +define fluent function isProblemListItem(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">): + exists (condition.category C + where C ~ "problem-list-item" + ) + +/* +@description: Returns true if the given condition is an encounter diagnosis +*/ +define fluent function isEncounterDiagnosis(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">): + exists (condition.category C + where C ~ "encounter-diagnosis" + ) + +/* +@description: Returns true if the given condition is a health concern +*/ +define fluent function isHealthConcern(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">): + exists (condition.category C + where C ~ "health-concern" + ) + +/* +@description: Returns true if the given observation has the given category +*/ +define fluent function hasCategory(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">, +category Code): + exists (observation.category C + where C ~ category + ) + +/* +@description: Returns true if the given observation is a social history observation +*/ +define fluent function isSocialHistory(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "social-history" + ) + +/* +@description: Returns true if the given observation is a vital sign +*/ +define fluent function isVitalSign(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "vital-signs" + ) + +/* +@description: Returns true if the given observation is an imaging observation +*/ +define fluent function isImaging(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "imaging" + ) + +/* +@description: Returns true if the given observation is a laboratory observation +*/ +define fluent function isLaboratory(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "laboratory" + ) + +/* +@description: Returns true if the given observation is a procedure observation +*/ +define fluent function isProcedure(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "procedure" + ) + +/* +@description: Returns true if the given observation is a survey observation +*/ +define fluent function isSurvey(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "survey" + ) + +/* +@description: Returns true if the given observation is an exam observation +*/ +define fluent function isExam(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "exam" + ) + +/* +@description: Returns true if the given observation is a therapy observation +*/ +define fluent function isTherapy(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "therapy" + ) + +/* +@description: Returns true if the given observation is an activity observation +*/ +define fluent function isActivity(observation Choice<"SimpleObservation", +"ObservationCancelled", +"NonPatientObservation", +"LaboratoryResultObservation", +"ObservationClinicalResult", +"ObservationScreeningAssessment", +"USCoreVitalSignsProfile", +"USCoreBloodPressureProfile", +"USCoreBMIProfile", +"USCoreBodyWeightProfile", +"USCoreBodyHeightProfile", +"USCoreBodyTemperatureProfile", +"USCoreHeadCircumferenceProfile", +"USCoreHeartRateProfile", +"USCorePediatricBMIforAgeObservationProfile", +"USCorePediatricHeadOccipitalFrontalCircumferencePercentileProfile", +"USCorePediatricWeightForHeightObservationProfile", +"USCorePulseOximetryProfile", +"USCoreRespiratoryRateProfile", +"USCoreSmokingStatusProfile", +"USCoreObservationOccupationProfile", +"USCoreObservationPregnancyIntentProfile", +"USCoreObservationPregnancyStatusProfile", +"USCoreObservationSexualOrientationProfile">): + exists (observation.category C + where C ~ "activity" + ) + +/* +@description: Returns true if the given MedicationRequest has a category of Community +*/ +define fluent function isCommunity(medicationRequest Choice): + exists (medicationRequest.category C + where C ~ Community + ) + +/* +@description: Returns true if the given MedicationRequest has a category of Discharge +*/ +define fluent function isDischarge(medicationRequest Choice): + exists (medicationRequest.category C + where C ~ Discharge + ) + +/* +@description: Returns true if the given DeviceRequest is a negation (i.e. do not perform this order) +@deprecated: This function is no longer required in QICore 6.0.0 and above +*/ +define fluent function doNotPerform(deviceRequest DeviceRequest): + // KNOWN ISSUE: The translator reports this as an error but the execution still works, this is under investigation + deviceRequest.doNotPerform + +/* +@description: Normalizes a value that is a choice of timing-valued types to an equivalent interval +@comment: Normalizes a choice type of DateTime, Quanitty, Interval, or Interval types +to an equivalent interval. This selection of choice types is a superset of the majority of choice types that are used as possible +representations for timing-valued elements in QICore, allowing this function to be used across any resource. +The input can be provided as a DateTime, Quantity, Interval or Interval. +The intent of this function is to provide a clear and concise mechanism to treat single +elements that have multiple possible representations as intervals so that logic doesn't have to account +for the variability. More complex calculations (such as medication request period or dispense period +calculation) need specific guidance and consideration. That guidance may make use of this function, but +the focus of this function is on single element calculations where the semantics are unambiguous. +If the input is a DateTime, the result a DateTime Interval beginning and ending on that DateTime. +If the input is a Quantity, the quantity is expected to be a calendar-duration interpreted as an Age, +and the result is a DateTime Interval beginning on the Date the patient turned that age and ending immediately before one year later. +If the input is a DateTime Interval, the result is the input. +If the input is a Quantity Interval, the quantities are expected to be calendar-durations interpreted as an Age, and the result +is a DateTime Interval beginning on the date the patient turned the age given as the start of the quantity interval, and ending +immediately before one year later than the date the patient turned the age given as the end of the quantity interval. +If the input is a Timing, an error will be thrown indicating that Timing calculations are not implemented. Any other input will reslt in a null DateTime Interval +@deprecated: This function is deprecated. Use the fluent function `toInterval()` instead +*/ +define function ToInterval(choice Choice, Interval, Timing>): + case + when choice is DateTime then + Interval[choice as DateTime, choice as DateTime] + when choice is Interval then + choice as Interval + when choice is Quantity then + Interval[Patient.birthDate + (choice as Quantity), + Patient.birthDate + (choice as Quantity) + 1 year) + when choice is Interval then + Interval[Patient.birthDate + (choice.low as Quantity), + Patient.birthDate + (choice.high as Quantity) + 1 year) + when choice is Timing then + Message(null, true, 'NOT_IMPLEMENTED', 'Error', 'Calculation of an interval from a Timing value is not supported') as Interval + else + null as Interval + end + +/* +@description: Normalizes a value that is a choice of timing-valued types to an equivalent interval +@comment: Normalizes a choice type of DateTime, Quanitty, Interval, or Interval types +to an equivalent interval. This selection of choice types is a superset of the majority of choice types that are used as possible +representations for timing-valued elements in QICore, allowing this function to be used across any resource. +The input can be provided as a DateTime, Quantity, Interval or Interval. +The intent of this function is to provide a clear and concise mechanism to treat single +elements that have multiple possible representations as intervals so that logic doesn't have to account +for the variability. More complex calculations (such as medication request period or dispense period +calculation) need specific guidance and consideration. That guidance may make use of this function, but +the focus of this function is on single element calculations where the semantics are unambiguous. +If the input is a DateTime, the result a DateTime Interval beginning and ending on that DateTime. +If the input is a Quantity, the quantity is expected to be a calendar-duration interpreted as an Age, +and the result is a DateTime Interval beginning on the Date the patient turned that age and ending immediately before one year later. +If the input is a DateTime Interval, the result is the input. +If the input is a Quantity Interval, the quantities are expected to be calendar-durations interpreted as an Age, and the result +is a DateTime Interval beginning on the date the patient turned the age given as the start of the quantity interval, and ending +immediately before one year later than the date the patient turned the age given as the end of the quantity interval. +If the input is a Timing, an error will be thrown indicating that Timing calculations are not implemented. Any other input will reslt in a null DateTime Interval +*/ +define fluent function toInterval(choice Choice, Interval, Timing>): + case + when choice is DateTime then + Interval[choice as DateTime, choice as DateTime] + when choice is Interval then + choice as Interval + when choice is Quantity then + Interval[Patient.birthDate + (choice as Quantity), + Patient.birthDate + (choice as Quantity) + 1 year) + when choice is Interval then + Interval[Patient.birthDate + (choice.low as Quantity), + Patient.birthDate + (choice.high as Quantity) + 1 year) + when choice is Timing then + Message(null, true, 'NOT_IMPLEMENTED', 'Error', 'Calculation of an interval from a Timing value is not supported') as Interval + else + null as Interval + end + +/* +@description: Returns an interval representing the normalized abatement of a given Condition. +@comment: If the abatement element of the Condition is represented as a DateTime, the result +is an interval beginning and ending on that DateTime. +If the abatement is represented as a Quantity, the quantity is expected to be a calendar-duration and is interpreted as the age of the patient. The +result is an interval from the date the patient turned that age to immediately before one year later. +If the abatement is represented as a Quantity Interval, the quantities are expected to be calendar-durations and are interpreted as an age range during +which the abatement occurred. The result is an interval from the date the patient turned the starting age of the quantity interval, and ending immediately +before one year later than the date the patient turned the ending age of the quantity interval. +@deprecated: This function is deprecated. Use the fluent function `abatementInterval()` instead. +*/ +define function ToAbatementInterval(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">): + if condition.abatement is DateTime then + Interval[condition.abatement as DateTime, condition.abatement as DateTime] + else if condition.abatement is Quantity then + Interval[Patient.birthDate + (condition.abatement as Quantity), + Patient.birthDate + (condition.abatement as Quantity) + 1 year) + else if condition.abatement is Interval then + Interval[Patient.birthDate + (condition.abatement.low as Quantity), + Patient.birthDate + (condition.abatement.high as Quantity) + 1 year) + else if condition.abatement is Interval then + Interval[condition.abatement.low, condition.abatement.high) + else null as Interval + +/* +@description: Returns an interval representing the normalized abatement of a given Condition. +@comment: If the abatement element of the Condition is represented as a DateTime, the result +is an interval beginning and ending on that DateTime. +If the abatement is represented as a Quantity, the quantity is expected to be a calendar-duration and is interpreted as the age of the patient. The +result is an interval from the date the patient turned that age to immediately before one year later. +If the abatement is represented as a Quantity Interval, the quantities are expected to be calendar-durations and are interpreted as an age range during +which the abatement occurred. The result is an interval from the date the patient turned the starting age of the quantity interval, and ending immediately +before one year later than the date the patient turned the ending age of the quantity interval. +*/ +define fluent function abatementInterval(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">): + if condition.abatement is DateTime then + Interval[condition.abatement as DateTime, condition.abatement as DateTime] + else if condition.abatement is Quantity then + Interval[Patient.birthDate + (condition.abatement as Quantity), + Patient.birthDate + (condition.abatement as Quantity) + 1 year) + else if condition.abatement is Interval then + Interval[Patient.birthDate + (condition.abatement.low as Quantity), + Patient.birthDate + (condition.abatement.high as Quantity) + 1 year) + else if condition.abatement is Interval then + Interval[condition.abatement.low, condition.abatement.high) + else null as Interval + +/* +@description: Returns an interval representing the normalized prevalence period of a given Condition. +@comment: Uses the ToInterval and ToAbatementInterval functions to determine the widest potential interval from +onset to abatement as specified in the given Condition. If the condition is active, or has an abatement date the resulting +interval will have a closed ending boundary. Otherwise, the resulting interval will have an open ending boundary. +@deprecated: This function is deprecated. Use the `prevalenceInterval()` fluent function instead +*/ +define function ToPrevalenceInterval(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">): +if condition.clinicalStatus ~ "active" + or condition.clinicalStatus ~ "recurrence" + or condition.clinicalStatus ~ "relapse" then + Interval[start of ToInterval(condition.onset), end of ToAbatementInterval(condition)] +else + (end of ToAbatementInterval(condition)) abatementDate + return if abatementDate is null then + Interval[start of ToInterval(condition.onset), abatementDate) + else + Interval[start of ToInterval(condition.onset), abatementDate] + +/* +@description: Returns an interval representing the normalized prevalence period of a given Condition. +@comment: Uses the ToInterval and ToAbatementInterval functions to determine the widest potential interval from +onset to abatement as specified in the given Condition. If the condition is active, or has an abatement date the resulting +interval will have a closed ending boundary. Otherwise, the resulting interval will have an open ending boundary. +*/ +define fluent function prevalenceInterval(condition Choice<"ConditionEncounterDiagnosis", "ConditionProblemsHealthConcerns">): +if condition.clinicalStatus ~ "active" + or condition.clinicalStatus ~ "recurrence" + or condition.clinicalStatus ~ "relapse" then + Interval[start of condition.onset.toInterval(), end of condition.abatementInterval()] +else + (end of condition.abatementInterval()) abatementDate + return if abatementDate is null then + Interval[start of condition.onset.toInterval(), abatementDate) + else + Interval[start of condition.onset.toInterval(), abatementDate] + +/* +@description: Returns the tail of the given uri (i.e. everything after the last slash in the URI). +@comment: This function can be used to determine the logical id of a given resource. It can be used in +a single-server environment to trace references. However, this function does not attempt to resolve +or distinguish the base of the given url, and so cannot be used safely in multi-server environments. +@deprecated: This function is deprecated. Use the fluent function `getId()` instead +*/ +define function GetId(uri String): + Last(Split(uri, '/')) + +/* +@description: Returns the tail of the given uri (i.e. everything after the last slash in the URI). +@comment: This function can be used to determine the logical id of a given resource. It can be used in +a single-server environment to trace references. However, this function does not attempt to resolve +or distinguish the base of the given url, and so cannot be used safely in multi-server environments. +*/ +define fluent function getId(uri String): + Last(Split(uri, '/')) + +/* +@description: Returns true if the given reference is to the given resource +@comment: Returns true if the `id` element of the given resource exactly equals the tail of the given reference. +NOTE: This function assumes resources from the same source server. +*/ +define fluent function references(reference Reference, resource Resource): + resource.id = Last(Split(reference.reference, '/')) + +/* +@description: Returns true if the given reference is to the given resourceId +@comment: Returns true if the `resourceId` parameter exactly equals the tail of the given reference. +NOTE: This function assumes resources from the same source server. +*/ +define fluent function references(reference Reference, resourceId String): + resourceId = Last(Split(reference.reference, '/')) + +/* +@description: Returns true if any of the given references are to the given resource +@comment: Returns true if the `id` element of the given resource exactly equals the tail of any of the given references. +NOTE: This function assumes resources from the same source server. +*/ +define fluent function references(references List, resource Resource): + exists (references R where R.references(resource)) + +/* +@description: Returns true if any of the given references are to the given resourceId +@comment: Returns true if the `resourceId` parameter exactly equals the tail of any of the given references. +NOTE: This function assumes resources from the same source server. +*/ +define fluent function references(references List, resourceId String): + exists (references R where R.references(resourceId)) + +/* +@description: Returns true if the given code is in the given codeList +@comment: Returns true if the `code` is equivalent to any of the codes in the given `codeList`, false otherwise. +*/ +define fluent function includesCode(codeList List, code Code): + exists (codeList C where C ~ code) + +/* +@description: Given an interval, return true if the interval has a starting boundary specified +(i.e. the start of the interval is not null and not the minimum DateTime value) +@deprecated: This function is deprecated. Uee the fluent function `hasStart()` instead +*/ +define function "HasStart"(period Interval ): + not ( start of period is null + or start of period = minimum DateTime + ) + +/* +@description: Given an interval, return true if the interval has a starting boundary specified +(i.e. the start of the interval is not null and not the minimum DateTime value) +*/ +define fluent function hasStart(period Interval ): + not ( start of period is null + or start of period = minimum DateTime + ) + +/* +@description: Given an interval, returns true if the interval has an ending boundary specified +(i.e. the end of the interval is not null and not the maximum DateTime value) +@deprecated: This function is deprecated. Use the fluent function `hasEnd()` instead +*/ +define function "HasEnd"(period Interval ): + not ( + end of period is null + or end of period = maximum DateTime + ) + +/* +@description: Given an interval, returns true if the interval has an ending boundary specified +(i.e. the end of the interval is not null and not the maximum DateTime value) +*/ +define fluent function hasEnd(period Interval ): + not ( + end of period is null + or end of period = maximum DateTime + ) + +/* +@description: Given an interval, returns the ending point if the interval has an ending boundary specified, +otherwise, returns the starting point +@deprecated: This function is deprecated. Use the fluent function `latest()` instead +*/ +define function "Latest"(choice Choice, Interval> ): + (choice.toInterval()) period + return + if (HasEnd(period)) then end of period + else start of period + +/* +@description: Given an interval, returns the ending point if the interval has an ending boundary specified, +otherwise, returns the starting point +*/ +define fluent function latest(choice Choice, Interval> ): + (choice.toInterval()) period + return + if (period."hasEnd"()) then end of period + else start of period + +/* +@description: Given an interval, return the starting point if the interval has a starting boundary specified, +otherwise, return the ending point +@deprecated: This function is deprecated. Use the fluent function `earliest()` instead +*/ +define function "Earliest"(choice Choice, Interval> ): + (choice.toInterval()) period + return + if (HasStart(period)) then start of period + else end of period + +/* +@description: Given an interval, return the starting point if the interval has a starting boundary specified, +otherwise, return the ending point +*/ +define fluent function earliest(choice Choice, Interval> ): + (choice.toInterval()) period + return + if (period."hasStart"()) then start of period + else end of period + +/* +@description: Creates a list of integers from 1 to how many days are in the interval. Note, this wont create an index for +the final day if it is less than 24 hours. This also includes the first 24 hour period. +@deprecated: This function is deprecated. Use the fluent function `toDayNumbers()` instead +*/ +define function "Interval To Day Numbers"(Period Interval): + ( expand { Interval[1, duration in days between start of Period and end of Period]} ) DayNumber + return end of DayNumber + +/* +@description: Creates a list of integers from 1 to how many days are in the interval. Note, this wont create an index for +the final day if it is less than 24 hours. This also includes the first 24 hour period. +*/ +define fluent function toDayNumbers(Period Interval): + ( expand { Interval[1, duration in days between start of Period and end of Period]} ) DayNumber + return end of DayNumber + +/* +@description: Creates a list of 24 hour long intervals in an interval paired with the index (1 indexed) to which 24 hour interval it is. +Note that the result will include intervals that are closed at the beginning and open at the end +@deprecated: This function is deprecated. Use the fluent function `daysInPeriod()` instead +*/ +define function "Days In Period"(Period Interval): + ( "Interval To Day Numbers"(Period)) DayIndex + let startPeriod: start of Period + (24 hours * (DayIndex - 1)), + endPeriod: if (hours between startPeriod and end of Period < 24) then startPeriod + else start of Period + (24 hours * DayIndex) + return Tuple { + dayIndex: DayIndex, + dayPeriod: Interval[startPeriod, endPeriod) + } + +/* +@description: Creates a list of 24 hour long intervals in an interval paired with the index (1 indexed) to which 24 hour interval it is. +Note that the result will include intervals that are closed at the beginning and open at the end +*/ +define fluent function daysInPeriod(Period Interval): + ( "Interval To Day Numbers"(Period)) DayIndex + let startPeriod: start of Period + (24 hours * (DayIndex - 1)), + endPeriod: if (hours between startPeriod and end of Period < 24) then startPeriod + else start of Period + (24 hours * DayIndex) + return Tuple { + dayIndex: DayIndex, + dayPeriod: Interval[startPeriod, endPeriod) + } \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/StatinsforthePreventionandTreatmentofCardiovascularDiseaseFHIR.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/StatinsforthePreventionandTreatmentofCardiovascularDiseaseFHIR.cql new file mode 100644 index 000000000..a93f8bc15 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/StatinsforthePreventionandTreatmentofCardiovascularDiseaseFHIR.cql @@ -0,0 +1,278 @@ +/* +NOTE: Preliminary QICore 6.0.0 uplift to support connectathon 38 testing +*/ +library StatinsforthePreventionandTreatmentofCardiovascularDiseaseFHIR version '0.2.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers +include QICoreCommon version '3.0.000' called QICoreCommon +include SupplementalDataElements version '4.0.000' called SDE +include Hospice version '7.0.000' called Hospice +include PalliativeCare version '2.0.000' called PalliativeCare + +codesystem "CPT": 'http://www.ama-assn.org/go/cpt' +codesystem "ICD10CM": 'http://hl7.org/fhir/sid/icd-10-cm' +codesystem "LOINC": 'http://loinc.org' +codesystem "ObservationCategoryCodes": 'http://terminology.hl7.org/CodeSystem/observation-category' +codesystem "RxNorm": 'https://www.nlm.nih.gov/research/umls/rxnorm/index.html' +codesystem "SNOMEDCT": 'http://snomed.info/sct' + +valueset "Annual Wellness Visit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1240' +valueset "Atherosclerosis and Peripheral Arterial Disease": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.21' +valueset "Breastfeeding": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.73' +valueset "CABG Surgeries": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.694' +valueset "CABG or PCI Procedure": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1138.566' +valueset "Carotid Intervention": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.204' +valueset "Cerebrovascular Disease Stroke or TIA": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.44' +valueset "Diabetes": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.103.12.1001' +valueset "End Stage Renal Disease": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.353' +valueset "Familial Hypercholesterolemia": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.100' +valueset "Hepatitis A": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.110.12.1024' +valueset "Hepatitis B": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.67.1.101.1.269' +valueset "High Intensity Statin Therapy": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1572' +valueset "Ischemic Heart Disease or Other Related Diagnoses": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.46' +valueset "LDL Cholesterol": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1573' +valueset "Liver Disease": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.42' +valueset "Low Intensity Statin Therapy": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1574' +valueset "Medical Reason": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1007' +valueset "Moderate Intensity Statin Therapy": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1575' +valueset "Myocardial Infarction": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.403' +valueset "Office Visit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001' +valueset "Outpatient Consultation": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1008' +valueset "Outpatient Encounters for Preventive Care": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1576' +valueset "PCI": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1045.67' +valueset "Preventive Care Services Established Office Visit, 18 and Up": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1025' +valueset "Preventive Care Services Individual Counseling": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1026' +valueset "Preventive Care Services Initial Office Visit, 18 and Up": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1023' +valueset "Preventive Care Services Other": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.11.1150' +valueset "Rhabdomyolysis": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.102' +valueset "Stable and Unstable Angina": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.47' +valueset "Statin Allergen": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1110.42' +valueset "Statin Associated Muscle Symptoms": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.85' + +code "Cardiovascular disease 10Y risk [Likelihood]": '99055-6' from "LOINC" display 'Cardiovascular disease 10Y risk [Likelihood]' +code "Cardiovascular disease 10Y risk [Likelihood] ACC-AHA Pooled Cohort by Goff 2013": '79423-0' from "LOINC" display 'Cardiovascular disease 10Y risk [Likelihood] ACC-AHA Pooled Cohort by Goff 2013' +code "Subsequent non-ST elevation (NSTEMI) myocardial infarction": 'I22.2' from "ICD10CM" display 'Subsequent non-ST elevation (NSTEMI) myocardial infarction' + +parameter "Measurement Period" Interval default Interval[@2025-01-01T00:00:00Z, @2026-01-01T00:00:00Z) + +context Patient + +define "Initial Population 1": + exists ( "ASVCD Diagnosis or Procedure before End of Measurement Period" + union "Myocardial Infarction before End of Measurement Period" + ) + and exists "Qualifying Encounter during day of Measurement Period" + +define "Initial Population 2": + "Patients Aged 20 to 75 with LDL Cholesterol Result Greater than or Equal to 190 or Hypercholesterolemia without ASCVD" + and exists "Qualifying Encounter during day of Measurement Period" + +define "Initial Population 3": + "Patients Aged 40 to 75 Years with Diabetes without ASCVD or LDL Greater than 190 or Hypercholesterolemia" + and exists "Qualifying Encounter during day of Measurement Period" + +define "Initial Population 4": + "Patients Age 40 to 75 Years and have a 10 Year CVD Risk of High without ASCVD and High LDL and Diabetes" + and exists "Qualifying Encounter during day of Measurement Period" + +define "Denominator 1": + "Initial Population 1" + +define "Denominator 2": + "Initial Population 2" + +define "Denominator 3": + "Initial Population 3" + +define "Denominator 4": + "Initial Population 4" + +define "Denominator Exclusions": + exists ( ( [ConditionProblemsHealthConcerns: "Breastfeeding"] + union [ConditionProblemsHealthConcerns: "Rhabdomyolysis"] ) ExclusionDiagnosis + where ( ExclusionDiagnosis.isProblemListItem ( ) + or ExclusionDiagnosis.isHealthConcern ( ) + ) + and ExclusionDiagnosis.prevalenceInterval ( ) overlaps day of "Measurement Period" + ) + +define "Denominator Exceptions": + "Has Allergy to Statin" + or Hospice."Has Hospice Services" + or PalliativeCare."Has Palliative Care in the Measurement Period" + or "Has Hepatitis or Liver Disease Diagnosis" + or "Has Statin Associated Muscle Symptoms" + or "Has ESRD Diagnosis" + or "Has Adverse Reaction to Statin" + or "Has Medical Reason for Not Ordering Statin Therapy" + +define "Numerator": + exists "Statin Therapy Prescribed Anytime during day of Measurement Period" + +define "ASVCD Diagnosis or Procedure before End of Measurement Period": + ( ( [ConditionProblemsHealthConcerns: "Myocardial Infarction"] + union [ConditionProblemsHealthConcerns: "Cerebrovascular Disease Stroke or TIA"] + union [ConditionProblemsHealthConcerns: "Atherosclerosis and Peripheral Arterial Disease"] + union [ConditionProblemsHealthConcerns: "Ischemic Heart Disease or Other Related Diagnoses"] + union [ConditionProblemsHealthConcerns: "Stable and Unstable Angina"] ) ASCVDDiagnosis + where ( ASCVDDiagnosis.isProblemListItem ( ) + or ASCVDDiagnosis.isHealthConcern ( ) + ) + and ASCVDDiagnosis.prevalenceInterval ( ) starts on or before day of end of "Measurement Period" + ) + union ( ( [Procedure: "PCI"] + union [Procedure: "CABG Surgeries"] + union [Procedure: "Carotid Intervention"] + union [Procedure: "CABG or PCI Procedure"] ) ASCVDProcedure + where QICoreCommon.ToInterval(ASCVDProcedure.performed) starts on or before day of end of "Measurement Period" + and ASCVDProcedure.status = 'completed' + ) + +define "Myocardial Infarction before End of Measurement Period": + [ConditionProblemsHealthConcerns: "Subsequent non-ST elevation (NSTEMI) myocardial infarction"] SubsequentMI + where ( SubsequentMI.isProblemListItem ( ) + or SubsequentMI.isHealthConcern ( ) + ) + and SubsequentMI.prevalenceInterval ( ) starts on or before day of end of "Measurement Period" + +define "Has Adverse Reaction to Statin": + exists ( [AdverseEvent: event in "Statin Allergen"] StatinReaction + where StatinReaction.recordedDate during day of "Measurement Period" + ) + +define "Has Allergy to Statin": + exists ( [AllergyIntolerance: "Statin Allergen"] StatinAllergy + where QICoreCommon.ToInterval(StatinAllergy.onset) overlaps day of "Measurement Period" + and StatinAllergy.clinicalStatus ~ QICoreCommon."allergy-active" + ) + +define "Has Diabetes Diagnosis": + exists ( [ConditionProblemsHealthConcerns: "Diabetes"] D + where ( D.isProblemListItem ( ) + or D.isHealthConcern ( ) + ) + and D.prevalenceInterval ( ) overlaps day of "Measurement Period" + ) + +define "Has ESRD Diagnosis": + exists ( [ConditionProblemsHealthConcerns: "End Stage Renal Disease"] ESRD + where ( ESRD.isProblemListItem ( ) + or ESRD.isHealthConcern ( ) + ) + and ESRD.prevalenceInterval ( ) overlaps day of "Measurement Period" + ) + +define "Has Hepatitis or Liver Disease Diagnosis": + exists ( ( [ConditionProblemsHealthConcerns: "Hepatitis A"] + union [ConditionProblemsHealthConcerns: "Hepatitis B"] + union [ConditionProblemsHealthConcerns: "Liver Disease"] ) HepatitisLiverDisease + where ( HepatitisLiverDisease.isProblemListItem ( ) + or HepatitisLiverDisease.isHealthConcern ( ) + ) + and HepatitisLiverDisease.prevalenceInterval ( ) overlaps day of "Measurement Period" + ) + +define "Has Medical Reason for Not Ordering Statin Therapy": + exists ( ( [MedicationNotRequested: "Low Intensity Statin Therapy"] + union [MedicationNotRequested: "Moderate Intensity Statin Therapy"] + union [MedicationNotRequested: "High Intensity Statin Therapy"] ) NoStatinTherapyOrdered + with "Qualifying Encounter during day of Measurement Period" QualifyingEncounter + such that NoStatinTherapyOrdered.authoredOn during QualifyingEncounter.period + and NoStatinTherapyOrdered.status = 'completed' + and NoStatinTherapyOrdered.reasonCode in "Medical Reason" + ) + +define "Has Statin Associated Muscle Symptoms": + exists ( [ConditionProblemsHealthConcerns: "Statin Associated Muscle Symptoms"] StatinMuscleSymptom + where ( StatinMuscleSymptom.isProblemListItem ( ) + or StatinMuscleSymptom.isHealthConcern ( ) + ) + and StatinMuscleSymptom.prevalenceInterval ( ) overlaps day of "Measurement Period" + ) + +define "Hypercholesterolemia Diagnosis": + ( [ConditionProblemsHealthConcerns: "Familial Hypercholesterolemia"] Hypercholesterolemia + where ( Hypercholesterolemia.isProblemListItem ( ) + or Hypercholesterolemia.isHealthConcern ( ) + ) + and Hypercholesterolemia.prevalenceInterval ( ) starts on or before day of end of "Measurement Period" + ) + +define "LDL Result Greater Than or Equal To 190": + [LaboratoryResultObservation: "LDL Cholesterol"] LDL190 + where LDL190.value as Quantity >= 190 'mg/dL' + and QICoreCommon.ToInterval(LDL190.effective) starts on or before day of end of "Measurement Period" + and LDL190.status in { 'final', 'amended', 'corrected', 'appended' } + +define "Patients Aged 20 to 75 with LDL Cholesterol Result Greater than or Equal to 190 or Hypercholesterolemia without ASCVD": + "Patients Aged 20 to 75 at Start of Measurement Period" + and exists ( "LDL Result Greater Than or Equal To 190" + union "Hypercholesterolemia Diagnosis" + ) + and not exists ( "ASVCD Diagnosis or Procedure before End of Measurement Period" ) + +define "Patients Aged 20 to 75 at Start of Measurement Period": + AgeInYearsAt(date from start of "Measurement Period") in Interval[20, 75] + +define "Patients Aged 40 to 75 Years with Diabetes without ASCVD or LDL Greater than 190 or Hypercholesterolemia": + AgeInYearsAt(date from start of "Measurement Period") >= 40 + and AgeInYearsAt(date from start of "Measurement Period") <= 75 + and "Has Diabetes Diagnosis" + and ( not exists "ASVCD Diagnosis or Procedure before End of Measurement Period" + and not exists "LDL Result Greater Than or Equal To 190" + and not exists "Hypercholesterolemia Diagnosis" + ) + +define "Patients Age 40 to 75 Years and have a 10 Year CVD Risk of High without ASCVD and High LDL and Diabetes": + AgeInYearsAt(date from start of "Measurement Period") in Interval[40, 75] + and "Ten Year CVD Risk is High" + and not ( exists "ASVCD Diagnosis or Procedure before End of Measurement Period" + or exists "Hypercholesterolemia Diagnosis" + or exists "LDL Result Greater Than or Equal To 190" + or "Has Diabetes Diagnosis" + ) + +define "Statin Therapy Prescribed Anytime during day of Measurement Period": + ( [MedicationRequest: "Low Intensity Statin Therapy"] + union [MedicationRequest: "Moderate Intensity Statin Therapy"] + union [MedicationRequest: "High Intensity Statin Therapy"] ) StatinPrescribed + where StatinPrescribed.status in { 'active', 'completed' } + and StatinPrescribed.intent ~ 'order' + and ( StatinPrescribed.authoredOn during day of "Measurement Period" + or exists ( StatinPrescribed.dosageInstruction.timing T + where QICoreCommon.ToInterval(T.repeat.bounds) overlaps day of "Measurement Period" + ) + ) + +define "Qualifying Encounter during day of Measurement Period": + ( [Encounter: "Annual Wellness Visit"] + union [Encounter: "Office Visit"] + union [Encounter: "Outpatient Consultation"] + union [Encounter: "Outpatient Encounters for Preventive Care"] + union [Encounter: "Preventive Care Services Established Office Visit, 18 and Up"] + union [Encounter: "Preventive Care Services Individual Counseling"] + union [Encounter: "Preventive Care Services Initial Office Visit, 18 and Up"] + union [Encounter: "Preventive Care Services Other"] ) QualifyingEncounter + where QualifyingEncounter.period during day of "Measurement Period" + and QualifyingEncounter.status = 'finished' + +define "Ten Year CVD Risk is High": + exists ( ([ObservationClinicalResult: "Cardiovascular disease 10Y risk [Likelihood]"] + union [ObservationClinicalResult: "Cardiovascular disease 10Y risk [Likelihood] ACC-AHA Pooled Cohort by Goff 2013"]) AtRiskCVD + where AtRiskCVD.value as Quantity >= 20 '%' + and QICoreCommon.ToInterval(AtRiskCVD.effective) during day of "Measurement Period" + ) + +define "SDE Ethnicity": + SDE."SDE Ethnicity" + +define "SDE Payer": + SDE."SDE Payer" + +define "SDE Race": + SDE."SDE Race" + +define "SDE Sex": + SDE."SDE Sex" \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/Status-2.0.000.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/Status-2.0.000.cql new file mode 100644 index 000000000..2045df0be --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/Status-2.0.000.cql @@ -0,0 +1,191 @@ +/* +NOTE: Preliminary QICore 6.0.0 uplift to support connectathon 38 testing +*/ +library Status version '2.0.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers + +codesystem "ObservationCategoryCodes": 'http://terminology.hl7.org/CodeSystem/observation-category' +codesystem "ConditionClinicalStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-clinical' + +code "laboratory": 'laboratory' from "ObservationCategoryCodes" display 'laboratory' +code "exam": 'exam' from "ObservationCategoryCodes" display 'exam' +code "survey": 'survey' from "ObservationCategoryCodes" display 'survey' +code "vital-signs": 'vital-signs' from "ObservationCategoryCodes" display 'vital-signs' +code "clinicalStatusActive": 'active' from "ConditionClinicalStatusCodes" + +context Patient +//Outdated: "This library contains functions that are based on QDM 5.6 to QICore 4.1.1 March 2023 (https://github.com/cqframework/CQL-Formatting-and-Usage-Wiki/wiki/Authoring-Patterns---QICore-v4.1.1). The functions may appear similar to some QICoreCommon functions but different in that they have constraints that are relevant for measures authored by NCQA." + +//Assessment, Performed +define fluent function isAssessmentPerformed(Obs List>): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + and exists ( O.category ObservationCategory + where ( ObservationCategory ) ~ "survey" + ) + +//Encounter, Performed +define fluent function isEncounterPerformed(Enc List): + Enc E + where E.status in {'finished', 'arrived', 'triaged', 'in-progress', 'onleave'} + +//Intervention, Performed +define fluent function isInterventionPerformed(Proc List): + Proc P + where P.status ~ 'completed' + +//Intervention, Order: active and completed only +define fluent function isInterventionOrder(ServiceRequest List): + ServiceRequest S + where S.status in { 'active', 'completed' } + and S.intent = 'order' + +/* +//Diagnosis +define function "Active Condition"(Condition List>): + Condition C + where C.clinicalStatus ~ "clinicalStatusActive" + +//Similar but different from QICoreCommon.isActive, which has recurrence and relapse as additional constraints +define fluent function isActiveOnly(Condition List>): + Condition C + where C.clinicalStatus ~ "clinicalStatusActive" + +//Device, Order - Personal Use Devices: active and completed only +define function "Completed or Ongoing Device Request"(DeviceRequest List): + DeviceRequest.isDeviceOrder() + +define fluent function isDeviceOrder(DeviceRequest List): + DeviceRequest D + where D.status in { 'active', 'completed' } + and D.intent = 'order' + +//Diagnostic Study, Order: active and completed only +define function "Completed or Ongoing Service Request"(ServiceRequest List): + ServiceRequest.isDiagnosticStudyOrder() + +define fluent function isDiagnosticStudyOrder(ServiceRequest List): + ServiceRequest S + where S.status in { 'active', 'completed' } + and S.intent = 'order' + +//Laboratory Test, Order: active and completed only +define fluent function isLaboratoryTestOrder(ServiceRequest List): + ServiceRequest S + where S.status in { 'active', 'completed' } + and S.intent = 'order' + +//Diagnostic Study, Performed +define function "Final Observation"(Obs List>): + Obs.isDiagnosticStudyPerformed() + +define fluent function isDiagnosticStudyPerformed(Obs List>): + Obs O + where O.status in {'final', 'amended', 'corrected' } + +//Encounter, Performed +//General usage unless required otherwise by measure intent (e.g., follow-up encounters) +define function "Finished Encounter"(Enc List): + isEncounterPerformed() + +//Immunization, Administered: completed only +define function "Completed Immunization"(Immunization List): + isImmunizationAdministered() + +define fluent function isImmunizationAdministered(Immunization List): + Immunization I + where I.status ~ 'completed' + + +//Procedure, Performed +define function "Completed Procedure"(Proc List): + isProcedurePerformed() + +define fluent function isProcedurePerformed(Proc List): + Proc P + where P.status ~ 'completed' + +//Laboratory Test, Performed +define fluent function isLaboratoryTestPerformed(Obs List<"LaboratoryResultObservation">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + and exists ( O.category ObservationCategory + where ( ObservationCategory ) ~ "laboratory" + ) + +//Medication, Active +define function "Active Medication"(MedicationRequest List): + isMedicationActive() + +define fluent function isMedicationActive(MedicationRequest List): + MedicationRequest M + where M.status = 'active' + and M.intent = 'order' + +//Medication, Dispensed +define function "Dispensed Medication"(Med List): + isMedicationDispensed() + +define fluent function isMedicationDispensed(Med List): + Med M + where M.status in { 'completed', 'in-progress', 'on-hold' } + +//Medication, Order: active and completed only +define function "Active or Completed Medication Request"(MedicationRequest List): + isMedicationOrder() + +define fluent function isMedicationOrder(MedicationRequest List): + MedicationRequest M + where M.status in { 'active', 'completed' } + and M.intent = 'order' + +//Physical Exam, Performed +define fluent function isPhysicalExamPerformed(Obs List>): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + and exists ( O.category ObservationCategory + where ( ObservationCategory ) ~ "exam" + ) + +//Observation Vital Signs +define fluent function isObservationBP(Obs List<"USCoreBloodPressureProfile">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define fluent function isObservationBodyHeight(Obs List<"USCoreBodyHeightProfile">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define fluent function isObservationBodyWeight(Obs List<"USCoreBodyWeightProfile">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define fluent function isObservationBMI(Obs List<"USCoreBMIProfile">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +//Symptom +define function "Initial or Final Observation"(Obs List>): + Obs O + where O.status in { 'preliminary', 'final', 'amended', 'corrected' } + +define fluent function isSymptom(Obs List>): + Obs O + where O.status in { 'preliminary', 'final', 'amended', 'corrected' } +*/ \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/SupplementalDataElements-4.0.000.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/SupplementalDataElements-4.0.000.cql new file mode 100644 index 000000000..4eb47586e --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/SupplementalDataElements-4.0.000.cql @@ -0,0 +1,68 @@ +/* +@update: JSR 2024-04-04 -> +Updated FHIRHelpers version to 4.4.000. +Added Male and Female codes and related AdministrativeGender code system +Updated SDE Sex definition to use added codes from FHIR administrative-gender code system. +*/ +library SupplementalDataElements version '4.0.000' + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' called FHIRHelpers + +codesystem "AdministrativeGender": 'http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender' +codesystem "SNOMEDCT": 'http://snomed.info/sct' +codesystem "DataAbsentReason": 'http://terminology.hl7.org/CodeSystem/data-absent-reason' + +valueset "Ethnicity": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837' +valueset "ONC Administrative Sex": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1' +valueset "Sex": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1240.3' // USCore required binding for uscore-sex element +valueset "Patient Sex For Quality Measurement": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1021.120' +valueset "Payer Type": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591' +valueset "Race": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836' + +code "M": 'M' from "AdministrativeGender" display 'Male' +code "F": 'F' from "AdministrativeGender" display 'Female' + +code "Patient sex unknown": '184115007' from SNOMEDCT display 'Patient sex unknown (finding)' +code "Female": '248152002' from SNOMEDCT display 'Female (finding)' +code "Male": '248153007' from SNOMEDCT display 'Male (finding)' + +code "Asked But Declined": 'asked-declined' from DataAbsentReason + +context Patient + +define "SDE Ethnicity": + // KNOWN ISSUE: The translator reports this as an error but the execution still works, this is under investigation + Patient.ethnicity E + return Tuple { + codes: { E.ombCategory } union E.detailed, + display: E.text + } + +define "SDE Payer": + // KNOWN ISSUE: The translator reports this as an error but the execution still works, this is under investigation + [Coverage: type in "Payer Type"] Payer + return { + code: Payer.type, + period: Payer.period + } + +define "SDE Race": + // KNOWN ISSUE: The translator reports this as an error but the execution still works, this is under investigation + Patient.race R + return Tuple { + codes: R.ombCategory union R.detailed, + display: R.text + } + +define "SDE Sex": + case + when Patient.sex = '184115007' then "Patient sex unknown" + when Patient.sex = '248152002' then "Female" + when Patient.sex = '248153007' then "Male" + when Patient.sex = 'asked-declined' then "Asked But Declined" + when Patient.gender = 'male' then "M" + when Patient.gender = 'female' then "F" + else null + end \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/TestChoiceUnion.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/TestChoiceUnion.cql new file mode 100644 index 000000000..e48635fa6 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/TestChoiceUnion.cql @@ -0,0 +1,14 @@ +library TestChoiceUnion + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' + +context Patient + +define "Union of Different Types": + ([Procedure] union [ServiceRequest]) R + return { + performed: R.performed, + authoredOn: R.authoredOn + } \ No newline at end of file diff --git a/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/TestMedicationRequest.cql b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/TestMedicationRequest.cql new file mode 100644 index 000000000..53f0b5f38 --- /dev/null +++ b/Src/java/cql-to-elm/src/test/resources/org/cqframework/cql/cql2elm/qicore/v600/TestMedicationRequest.cql @@ -0,0 +1,15 @@ +library TestMedicationRequest + +using QICore version '6.0.0' + +include FHIRHelpers version '4.0.1' + +valueset "Antithrombotic Therapy": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1110.62' + +context Patient + +define "Antithrombotic Therapy at Discharge": + ["MedicationRequest": medication in "Antithrombotic Therapy"] Antithrombotic + +define "Antithrombotic Therapy at Discharge (2)": + ["MedicationRequest": "Antithrombotic Therapy"] diff --git a/Src/java/quick/src/main/resources/org/hl7/fhir/qicore-modelinfo-6.0.0.xml b/Src/java/quick/src/main/resources/org/hl7/fhir/qicore-modelinfo-6.0.0.xml index 5e8288214..ecc08aff5 100644 --- a/Src/java/quick/src/main/resources/org/hl7/fhir/qicore-modelinfo-6.0.0.xml +++ b/Src/java/quick/src/main/resources/org/hl7/fhir/qicore-modelinfo-6.0.0.xml @@ -3486,8 +3486,9 @@ - - + + +