diff --git a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.java b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.java index 3a13ad5dc..83459ae18 100755 --- a/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.java +++ b/Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.java @@ -3871,6 +3871,9 @@ public Expression resolveFunctionOrQualifiedFunction(String identifier, cqlParse return systemMethodResolver.resolveMethod((Expression)target, identifier, paramListCtx, true); } + if (!isMethodInvocationEnabled()) { + throw new CqlCompilerException(String.format("The identifier %s could not be resolved as an invocation because method-style invocation is disabled.", identifier), CqlCompilerException.ErrorSeverity.Error); + } throw new IllegalArgumentException(String.format("Invalid invocation target: %s", target.getClass().getName())); } finally { diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/CollapsedElmRequirements.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/CollapsedElmRequirements.java index 174318819..f904ed964 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/CollapsedElmRequirements.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/CollapsedElmRequirements.java @@ -1,11 +1,18 @@ package org.cqframework.cql.elm.requirements; +import org.hl7.elm.r1.IncludeElement; +import org.hl7.elm.r1.Retrieve; + import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class CollapsedElmRequirements { private List uniqueRequirements = new ArrayList(); + + private Map requirementIdMap = new HashMap<>(); public Iterable getUniqueRequirements() { return uniqueRequirements; } @@ -17,10 +24,22 @@ public void add(ElmRequirement requirement) { } else { uniqueRequirements.remove(existing); + ElmRequirement newRequirement = ComparableElmRequirement.mergeRequirements(existing, requirement); + mapRequirementId(requirement, newRequirement); uniqueRequirements.add(ComparableElmRequirement.mergeRequirements(existing, requirement)); } } + public Map getRequirementIdMap() { + return requirementIdMap; + } + + private void mapRequirementId(ElmRequirement oldRequirement, ElmRequirement newRequirement) { + if (oldRequirement.getElement().getLocalId() != null) { + requirementIdMap.put(oldRequirement.getElement().getLocalId(), newRequirement.getElement().getLocalId()); + } + } + public ElmRequirement getEquivalent(ElmRequirement requirement) { for (ElmRequirement existing : uniqueRequirements) { if (ComparableElmRequirement.requirementsEquivalent(existing, requirement)) { diff --git a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirements.java b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirements.java index afa84ae59..dfaceab6a 100644 --- a/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirements.java +++ b/Src/java/elm-fhir/src/main/java/org/cqframework/cql/elm/requirements/ElmRequirements.java @@ -296,6 +296,7 @@ public ElmRequirements collapse(ElmRequirementsContext context) { // Has the same context, type/profile, code path and date path // If two retrieves are "equivalent" they can be merged // TODO: code/date-range consolidation + Map requirementIdMap = new HashMap<>(); for (Map.Entry> entry : retrievesByType.entrySet()) { // Determine unique set per type/profile CollapsedElmRequirements collapsedRetrieves = new CollapsedElmRequirements(); @@ -303,11 +304,38 @@ public ElmRequirements collapse(ElmRequirementsContext context) { collapsedRetrieves.add(requirement); } + // Collect target mappings + for (Map.Entry idMapEntry : collapsedRetrieves.getRequirementIdMap().entrySet()) { + requirementIdMap.put(idMapEntry.getKey(), idMapEntry.getValue()); + } + for (ElmRequirement r : collapsedRetrieves.getUniqueRequirements()) { result.reportRequirement(r); } } + // Fixup references in the resulting requirements + for (ElmRequirement requirement : result.getRequirements()) { + if (requirement.getElement() instanceof Retrieve) { + Retrieve r = ((Retrieve)requirement.getElement()); + if (r.getIncludedIn() != null) { + String mappedId = requirementIdMap.get(r.getIncludedIn()); + if (mappedId != null) { + r.setIncludedIn(mappedId); + } + } + + for (IncludeElement includeElement : r.getInclude()) { + if (includeElement.getIncludeFrom() != null) { + String mappedId = requirementIdMap.get(includeElement.getIncludeFrom()); + if (mappedId != null) { + includeElement.setIncludeFrom(mappedId); + } + } + } + } + } + return result; } } diff --git a/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.java b/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.java index 29b5c8638..6860ec537 100644 --- a/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.java +++ b/Src/java/elm-fhir/src/test/java/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.java @@ -1711,6 +1711,17 @@ public void TestCMS645() throws IOException { //outputModuleDefinitionLibrary(moduleDefinitionLibrary); } + @Test + public void TestPCSBMI() throws IOException { + CqlCompilerOptions compilerOptions = CqlCompilerOptions.defaultOptions(); + var manager = setupDataRequirementsAnalysis("PCSBMI/PCSBMIScreenAndFollowUpFHIR.cql", compilerOptions); + org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(manager, compilerOptions, new HashMap(), ZonedDateTime.of(2023, 1, 16, 0, 0, 0, 0, ZoneId.of("UTC"))); + assertNotNull(moduleDefinitionLibrary); + assertEqualToExpectedModuleDefinitionLibrary(moduleDefinitionLibrary, "PCSBMI/PCSBMI-ModuleDefinitionLibrary.json"); + + //outputModuleDefinitionLibrary(moduleDefinitionLibrary); + } + @Test public void TestCMS143() throws IOException { CqlCompilerOptions compilerOptions = CqlCompilerOptions.defaultOptions(); diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/FHIRHelpers.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/FHIRHelpers.cql new file mode 100644 index 000000000..b686345dd --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/FHIRHelpers.cql @@ -0,0 +1,711 @@ +library FHIRHelpers version '4.3.000' + +using FHIR version '4.0.1' + +/* +@description: Converts the given [Period](https://hl7.org/fhir/datatypes.html#Period) +value to a CQL DateTime Interval +@comment: If the start value of the given period is unspecified, the starting +boundary of the resulting interval will be open (meaning the start of the interval +is unknown, as opposed to interpreted as the beginning of time). +*/ +define function ToInterval(period FHIR.Period): + if period is null then + null + else + if period."start" is null then + Interval(period."start".value, period."end".value] + else + Interval[period."start".value, period."end".value] + +/* +@description: Converts a UCUM definite duration unit to a CQL calendar duration +unit using conversions specified in the [quantities](https://cql.hl7.org/02-authorsguide.html#quantities) +topic of the CQL specification. +@comment: Note that for durations above days (or weeks), the conversion is understood to be approximate +*/ +define function ToCalendarUnit(unit System.String): + case unit + when 'ms' then 'millisecond' + when 's' then 'second' + when 'min' then 'minute' + when 'h' then 'hour' + when 'd' then 'day' + when 'wk' then 'week' + when 'mo' then 'month' + when 'a' then 'year' + else unit + end + +/* +@description: Converts the given FHIR [Quantity](https://hl7.org/fhir/datatypes.html#Quantity) +value to a CQL Quantity +@comment: If the given quantity has a comparator specified, a runtime error is raised. If the given quantity +has a system other than UCUM (i.e. `http://unitsofmeasure.org`) or CQL calendar units (i.e. `http://hl7.org/fhirpath/CodeSystem/calendar-units`) +an error is raised. For UCUM to calendar units, the `ToCalendarUnit` function is used. +@seealso: ToCalendarUnit +*/ +define function ToQuantity(quantity FHIR.Quantity): + case + when quantity is null then null + when quantity.value is null then null + when quantity.comparator is not null then + Message(null, true, 'FHIRHelpers.ToQuantity.ComparatorQuantityNotSupported', 'Error', 'FHIR Quantity value has a comparator and cannot be converted to a System.Quantity value.') + when quantity.system is null or quantity.system.value = 'http://unitsofmeasure.org' + or quantity.system.value = 'http://hl7.org/fhirpath/CodeSystem/calendar-units' then + System.Quantity { value: quantity.value.value, unit: ToCalendarUnit(Coalesce(quantity.code.value, quantity.unit.value, '1')) } + else + Message(null, true, 'FHIRHelpers.ToQuantity.InvalidFHIRQuantity', 'Error', 'Invalid FHIR Quantity code: ' & quantity.unit.value & ' (' & quantity.system.value & '|' & quantity.code.value & ')') + end + +/* +@description: Converts the given FHIR [Quantity](https://hl7.org/fhir/datatypes.html#Quantity) value to a CQL Quantity, ignoring +the comparator element. This function should only be used when an application is justified in ignoring the comparator value (i.e. the +context is looking for boundary). +@comment: If the given quantity has a system other than UCUM (i.e. `http://unitsofmeasure.org`) or CQL calendar units +(i.e. `http://hl7.org/fhirpath/CodeSystem/calendar-units`) an error is raised. For UCUM to calendar units, the `ToCalendarUnit` function +is used. +@seealso: ToCalendarUnit +*/ +define function ToQuantityIgnoringComparator(quantity FHIR.Quantity): + case + when quantity is null then null + when quantity.value is null then null + when quantity.system is null or quantity.system.value = 'http://unitsofmeasure.org' + or quantity.system.value = 'http://hl7.org/fhirpath/CodeSystem/calendar-units' then + System.Quantity { value: quantity.value.value, unit: ToCalendarUnit(Coalesce(quantity.code.value, quantity.unit.value, '1')) } + else + Message(null, true, 'FHIRHelpers.ToQuantity.InvalidFHIRQuantity', 'Error', 'Invalid FHIR Quantity code: ' & quantity.unit.value & ' (' & quantity.system.value & '|' & quantity.code.value & ')') + end + +/* +@description: Converts the given FHIR [Quantity](https://hl7.org/fhir/datatypes.html#Quantity) value to a CQL Interval of Quantity. +@comment: If the given quantity has a comparator, it is used to construct an interval based on the value of the comparator. If the comparator +is less than, the resulting interval will start with a null closed boundary and end with an open boundary on the quantity. If the comparator +is less than or equal, the resulting interval will start with a null closed boundary and end with a closed boundary on the quantity. If the +comparator is greater or equal, the resulting interval will start with a closed boundary on the quantity and end with a closed null boundary. +If the comparator is greatter than, the resulting interval will start with an open boundary on the quantity and end with a closed null boundary. +If no comparator is specified, the resulting interval will start and end with a closed boundary on the quantity. +*/ +define function ToInterval(quantity FHIR.Quantity): + if quantity is null then null else + case quantity.comparator.value + when '<' then + Interval[ + null, + ToQuantityIgnoringComparator(quantity) + ) + when '<=' then + Interval[ + null, + ToQuantityIgnoringComparator(quantity) + ] + when '>=' then + Interval[ + ToQuantityIgnoringComparator(quantity), + null + ] + when '>' then + Interval( + ToQuantityIgnoringComparator(quantity), + null + ] + else + Interval[ToQuantity(quantity), ToQuantity(quantity)] + end + +/* +@description: Converts the given FHIR [Ratio](https://hl7.org/fhir/datatypes.html#Ratio) value to a CQL Ratio. +*/ +define function ToRatio(ratio FHIR.Ratio): + if ratio is null then + null + else + System.Ratio { numerator: ToQuantity(ratio.numerator), denominator: ToQuantity(ratio.denominator) } + +/* +@description: Converts the given FHIR [Range](https://hl7.org/fhir/datatypes.html#Range) value to a CQL Interval of Quantity +*/ +define function ToInterval(range FHIR.Range): + if range is null then + null + else + Interval[ToQuantity(range.low), ToQuantity(range.high)] + +/* +@description: Converts the given FHIR [Coding](https://hl7.org/fhir/datatypes.html#Coding) value to a CQL Code. +*/ +define function ToCode(coding FHIR.Coding): + if coding is null then + null + else + System.Code { + code: coding.code.value, + system: coding.system.value, + version: coding.version.value, + display: coding.display.value + } + +/* +@description: Converts the given FHIR [CodeableConcept](https://hl7.org/fhir/datatypes.html#CodeableConcept) value to a CQL Concept. +*/ +define function ToConcept(concept FHIR.CodeableConcept): + if concept is null then + null + else + System.Concept { + codes: concept.coding C return ToCode(C), + display: concept.text.value + } + +/* +@description: Converts the given value (assumed to be a URI) to a CQL [ValueSet](https://cql.hl7.org/09-b-cqlreference.html#valueset) +*/ +define function ToValueSet(uri String): + if uri is null then + null + else + System.ValueSet { + id: uri + } + +/* +@description: Constructs a FHIR [Reference](https://hl7.org/fhir/datatypes.html#Reference) from the given reference (assumed to be a FHIR resource URL) +*/ +define function reference(reference String): + if reference is null then + null + else + Reference { reference: string { value: reference } } + +/* +@description: Converts the given value to a CQL value using the appropriate accessor or conversion function. +@comment: TODO: document conversion +*/ +define function ToValue(value Choice): + case + when value is base64Binary then (value as base64Binary).value + when value is boolean then (value as boolean).value + when value is canonical then (value as canonical).value + when value is code then (value as code).value + when value is date then (value as date).value + when value is dateTime then (value as dateTime).value + when value is decimal then (value as decimal).value + when value is id then (value as id).value + when value is instant then (value as instant).value + when value is integer then (value as integer).value + when value is markdown then (value as markdown).value + when value is oid then (value as oid).value + when value is positiveInt then (value as positiveInt).value + when value is string then (value as string).value + when value is time then (value as time).value + when value is unsignedInt then (value as unsignedInt).value + when value is uri then (value as uri).value + when value is url then (value as url).value + when value is uuid then (value as uuid).value + when value is Age then ToQuantity(value as Age) + when value is CodeableConcept then ToConcept(value as CodeableConcept) + when value is Coding then ToCode(value as Coding) + when value is Count then ToQuantity(value as Count) + when value is Distance then ToQuantity(value as Distance) + when value is Duration then ToQuantity(value as Duration) + when value is Quantity then ToQuantity(value as Quantity) + when value is Range then ToInterval(value as Range) + when value is Period then ToInterval(value as Period) + when value is Ratio then ToRatio(value as Ratio) + else value as Choice + end + +/* +@description: Resolve the given reference as a url to a resource. If the item resolves, the Resource is returned, otherwise the result is null. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function resolve(reference String) returns Resource: external +/* +@description: Resolve the reference element of the given Reference. If the item resolves, the Resource is returned, otherwise the result is null. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function resolve(reference Reference) returns Resource: external +/* +@description: Constructs a Reference to the given Resource. The resulting reference will typically be relative, but implementations may provide a base URL if one can be unambiguously determined. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function reference(resource Resource) returns Reference: external +/* +@description: Returns any extensions with the given url defined on the given element. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function extension(element Element, url String) returns List: external +/* +@description: Returns any extensions with the given url defined on the given resource. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function extension(resource DomainResource, url String) returns List: external +/* +@description: Returns any modifier extensions with the given url defined on the given element. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function modifierExtension(element BackboneElement, url String) returns List: external +/* +@description: Returns any modifier extensions with the given url defined on the given resource. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function modifierExtension(resource DomainResource, url String) returns List: external +/* +@description: Returns true if the element is a FHIR primitive type with a value element (as opposed to having only extensions); false otherwise +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function hasValue(element Element) returns Boolean: external +/* +@description: Returns the value of the FHIR primitive; null otherwise +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function getValue(element Element) returns Any: external +/* +@description: Returns a list containing only those elements in the input that are of the given type, specified as a string. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function ofType(identifier String) returns List: external +/* +@description: Returns true if the input is of the given type; false otherwise +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function is(identifier String) returns Boolean: external +/* +@description: If the input is of the given type; returns the value as that type; null otherwise. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function as(identifier String) returns Any: external +/* +@description: Returns the FHIR element definition for the given element +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function elementDefinition(element Element) returns ElementDefinition: external +/* +@description: Returns the given slice as defined in the given structure definition. The structure argument is a uri that resolves to the structure definition, and the name must be the name of a slice within that structure definition. If the structure cannot be resolved, or the name of the slice within the resolved structure is not present, an error is thrown. +@comment: For every element in the input collection, if the resolved slice is present on the element, it will be returned. If the slice does not match any element in the input collection, or if the input collection is empty, the result is an empty collection ({ }). +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function slice(element Element, url String, name String) returns List: external +/* +@description: For each element in the input collection, verifies that there are no modifying extensions defined other than the ones given by the modifier argument. If the check passes, the input collection is returned. Otherwise, an error is thrown. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function checkModifiers(resource Resource) returns Resource: external +/* +@description: For each element in the input collection, verifies that there are no modifying extensions defined other than the ones given by the modifier argument. If the check passes, the input collection is returned. Otherwise, an error is thrown. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function checkModifiers(resource Resource, modifier String) returns Resource: external +/* +@description: For each element in the input collection, verifies that there are no modifying extensions defined other than the ones given by the modifier argument. If the check passes, the input collection is returned. Otherwise, an error is thrown. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function checkModifiers(element Element) returns Element: external +/* +@description: For each element in the input collection, verifies that there are no modifying extensions defined other than the ones given by the modifier argument. If the check passes, the input collection is returned. Otherwise, an error is thrown. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function checkModifiers(element Element, modifier String) returns Element: external +/* +@description: Returns true if the single input element conforms to the profile specified by the structure argument, and false otherwise. If the structure cannot be resolved to a valid profile, an error is thrown. If the input contains more than one element, an error is thrown. If the input is empty, the result is empty. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function conformsTo(resource Resource, structure String) returns Boolean: external +/* +@description: Returns true if the given code is equal to a code in the valueset, so long as the valueset only contains one codesystem. If the valueset contains more than one codesystem, an error is thrown. +@comment: If the valueset cannot be resolved as a uri to a value set, an error is thrown. +Note that implementations are encouraged to make use of a terminology service to provide this functionality. +For example: +```fhirpath +Observation.component.where(code.memberOf('http://hl7.org/fhir/ValueSet/observation-vitalsignresult')) +``` +This expression returns components that have a code that is a member of the observation-vitalsignresult valueset. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function memberOf(code code, valueSet String) returns Boolean: external +/* +@description: Returns true if the code is a member of the given valueset. +@comment: If the valueset cannot be resolved as a uri to a value set, an error is thrown. +Note that implementations are encouraged to make use of a terminology service to provide this functionality. +For example: +```fhirpath +Observation.component.where(code.memberOf('http://hl7.org/fhir/ValueSet/observation-vitalsignresult')) +``` +This expression returns components that have a code that is a member of the observation-vitalsignresult valueset. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function memberOf(coding Coding, valueSet String) returns Boolean: external +/* +@description: Returns true if any code in the concept is a member of the given valueset. +@comment: If the valueset cannot be resolved as a uri to a value set, an error is thrown. +Note that implementations are encouraged to make use of a terminology service to provide this functionality. +For example: +```fhirpath +Observation.component.where(code.memberOf('http://hl7.org/fhir/ValueSet/observation-vitalsignresult')) +``` +This expression returns components that have a code that is a member of the observation-vitalsignresult valueset. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function memberOf(concept CodeableConcept, valueSet String) returns Boolean: external +/* +@description: Returns true if the source code is equivalent to the given code, or if the source code subsumes the given code (i.e. the source code is an ancestor of the given code in a subsumption hierarchy), and false otherwise. +@comment: If the Codings are from different code systems, the relationships between the code systems must be well-defined or a run-time error is thrown. +Note that implementations are encouraged to make use of a terminology service to provide this functionality. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function subsumes(coding Coding, subsumedCoding Coding) returns Boolean: external +/* +@description: Returns true if any Coding in the source or given elements is equivalent to or subsumes the given code. +@comment: If the Codings are from different code systems, the relationships between the code systems must be well-defined or a run-time error is thrown. +Note that implementations are encouraged to make use of a terminology service to provide this functionality. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function subsumes(concept CodeableConcept, subsumedConcept CodeableConcept) returns Boolean: external +/* +@description: Returns true if the source code is equivalent to the given code, or if the source code is subsumed by the given code (i.e. the source code is a descendant of the given code in a subsumption hierarchy), and false otherwise. +@comment: If the Codings are from different code systems, the relationships between the code systems must be well-defined or a run-time error is thrown. +Note that implementations are encouraged to make use of a terminology service to provide this functionality. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function subsumedBy(coding Coding, subsumingCoding Coding) returns Boolean: external +/* +@description: Returns true if any Coding in the source or given elements is equivalent to or subsumed by the given code. +@comment: If the Codings are from different code systems, the relationships between the code systems must be well-defined or a run-time error is thrown. +Note that implementations are encouraged to make use of a terminology service to provide this functionality. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function subsumedBy(concept CodeableConcept, subsumingConcept CodeableConcept) returns Boolean: external +/* +@description: When invoked on an xhtml element, returns true if the rules around HTML usage are met, and false if they are not. The return value is undefined (null) on any other kind of element. +@seealso: https://hl7.org/fhir/fhirpath.html#functions +*/ +define function htmlChecks(element Element) returns Boolean: external + +define function ToString(value AccountStatus): value.value +define function ToString(value ActionCardinalityBehavior): value.value +define function ToString(value ActionConditionKind): value.value +define function ToString(value ActionGroupingBehavior): value.value +define function ToString(value ActionParticipantType): value.value +define function ToString(value ActionPrecheckBehavior): value.value +define function ToString(value ActionRelationshipType): value.value +define function ToString(value ActionRequiredBehavior): value.value +define function ToString(value ActionSelectionBehavior): value.value +define function ToString(value ActivityDefinitionKind): value.value +define function ToString(value ActivityParticipantType): value.value +define function ToString(value AddressType): value.value +define function ToString(value AddressUse): value.value +define function ToString(value AdministrativeGender): value.value +define function ToString(value AdverseEventActuality): value.value +define function ToString(value AggregationMode): value.value +define function ToString(value AllergyIntoleranceCategory): value.value +define function ToString(value AllergyIntoleranceCriticality): value.value +define function ToString(value AllergyIntoleranceSeverity): value.value +define function ToString(value AllergyIntoleranceType): value.value +define function ToString(value AppointmentStatus): value.value +define function ToString(value AssertionDirectionType): value.value +define function ToString(value AssertionOperatorType): value.value +define function ToString(value AssertionResponseTypes): value.value +define function ToString(value AuditEventAction): value.value +define function ToString(value AuditEventAgentNetworkType): value.value +define function ToString(value AuditEventOutcome): value.value +define function ToString(value BindingStrength): value.value +define function ToString(value BiologicallyDerivedProductCategory): value.value +define function ToString(value BiologicallyDerivedProductStatus): value.value +define function ToString(value BiologicallyDerivedProductStorageScale): value.value +define function ToString(value BundleType): value.value +define function ToString(value CapabilityStatementKind): value.value +define function ToString(value CarePlanActivityKind): value.value +define function ToString(value CarePlanActivityStatus): value.value +define function ToString(value CarePlanIntent): value.value +define function ToString(value CarePlanStatus): value.value +define function ToString(value CareTeamStatus): value.value +define function ToString(value CatalogEntryRelationType): value.value +define function ToString(value ChargeItemDefinitionPriceComponentType): value.value +define function ToString(value ChargeItemStatus): value.value +define function ToString(value ClaimResponseStatus): value.value +define function ToString(value ClaimStatus): value.value +define function ToString(value ClinicalImpressionStatus): value.value +define function ToString(value CodeSearchSupport): value.value +define function ToString(value CodeSystemContentMode): value.value +define function ToString(value CodeSystemHierarchyMeaning): value.value +define function ToString(value CommunicationPriority): value.value +define function ToString(value CommunicationRequestStatus): value.value +define function ToString(value CommunicationStatus): value.value +define function ToString(value CompartmentCode): value.value +define function ToString(value CompartmentType): value.value +define function ToString(value CompositionAttestationMode): value.value +define function ToString(value CompositionStatus): value.value +define function ToString(value ConceptMapEquivalence): value.value +define function ToString(value ConceptMapGroupUnmappedMode): value.value +define function ToString(value ConditionalDeleteStatus): value.value +define function ToString(value ConditionalReadStatus): value.value +define function ToString(value ConsentDataMeaning): value.value +define function ToString(value ConsentProvisionType): value.value +define function ToString(value ConsentState): value.value +define function ToString(value ConstraintSeverity): value.value +define function ToString(value ContactPointSystem): value.value +define function ToString(value ContactPointUse): value.value +define function ToString(value ContractPublicationStatus): value.value +define function ToString(value ContractStatus): value.value +define function ToString(value ContributorType): value.value +define function ToString(value CoverageStatus): value.value +define function ToString(value CurrencyCode): value.value +define function ToString(value DayOfWeek): value.value +define function ToString(value DaysOfWeek): value.value +define function ToString(value DetectedIssueSeverity): value.value +define function ToString(value DetectedIssueStatus): value.value +define function ToString(value DeviceMetricCalibrationState): value.value +define function ToString(value DeviceMetricCalibrationType): value.value +define function ToString(value DeviceMetricCategory): value.value +define function ToString(value DeviceMetricColor): value.value +define function ToString(value DeviceMetricOperationalStatus): value.value +define function ToString(value DeviceNameType): value.value +define function ToString(value DeviceRequestStatus): value.value +define function ToString(value DeviceUseStatementStatus): value.value +define function ToString(value DiagnosticReportStatus): value.value +define function ToString(value DiscriminatorType): value.value +define function ToString(value DocumentConfidentiality): value.value +define function ToString(value DocumentMode): value.value +define function ToString(value DocumentReferenceStatus): value.value +define function ToString(value DocumentRelationshipType): value.value +define function ToString(value EligibilityRequestPurpose): value.value +define function ToString(value EligibilityRequestStatus): value.value +define function ToString(value EligibilityResponsePurpose): value.value +define function ToString(value EligibilityResponseStatus): value.value +define function ToString(value EnableWhenBehavior): value.value +define function ToString(value EncounterLocationStatus): value.value +define function ToString(value EncounterStatus): value.value +define function ToString(value EndpointStatus): value.value +define function ToString(value EnrollmentRequestStatus): value.value +define function ToString(value EnrollmentResponseStatus): value.value +define function ToString(value EpisodeOfCareStatus): value.value +define function ToString(value EventCapabilityMode): value.value +define function ToString(value EventTiming): value.value +define function ToString(value EvidenceVariableType): value.value +define function ToString(value ExampleScenarioActorType): value.value +define function ToString(value ExplanationOfBenefitStatus): value.value +define function ToString(value ExposureState): value.value +define function ToString(value ExtensionContextType): value.value +define function ToString(value FHIRAllTypes): value.value +define function ToString(value FHIRDefinedType): value.value +define function ToString(value FHIRDeviceStatus): value.value +define function ToString(value FHIRResourceType): value.value +define function ToString(value FHIRSubstanceStatus): value.value +define function ToString(value FHIRVersion): value.value +define function ToString(value FamilyHistoryStatus): value.value +define function ToString(value FilterOperator): value.value +define function ToString(value FlagStatus): value.value +define function ToString(value GoalLifecycleStatus): value.value +define function ToString(value GraphCompartmentRule): value.value +define function ToString(value GraphCompartmentUse): value.value +define function ToString(value GroupMeasure): value.value +define function ToString(value GroupType): value.value +define function ToString(value GuidanceResponseStatus): value.value +define function ToString(value GuidePageGeneration): value.value +define function ToString(value GuideParameterCode): value.value +define function ToString(value HTTPVerb): value.value +define function ToString(value IdentifierUse): value.value +define function ToString(value IdentityAssuranceLevel): value.value +define function ToString(value ImagingStudyStatus): value.value +define function ToString(value ImmunizationEvaluationStatus): value.value +define function ToString(value ImmunizationStatus): value.value +define function ToString(value InvoicePriceComponentType): value.value +define function ToString(value InvoiceStatus): value.value +define function ToString(value IssueSeverity): value.value +define function ToString(value IssueType): value.value +define function ToString(value LinkType): value.value +define function ToString(value LinkageType): value.value +define function ToString(value ListMode): value.value +define function ToString(value ListStatus): value.value +define function ToString(value LocationMode): value.value +define function ToString(value LocationStatus): value.value +define function ToString(value MeasureReportStatus): value.value +define function ToString(value MeasureReportType): value.value +define function ToString(value MediaStatus): value.value +define function ToString(value MedicationAdministrationStatus): value.value +define function ToString(value MedicationDispenseStatus): value.value +define function ToString(value MedicationKnowledgeStatus): value.value +define function ToString(value MedicationRequestIntent): value.value +define function ToString(value MedicationRequestPriority): value.value +define function ToString(value MedicationRequestStatus): value.value +define function ToString(value MedicationStatementStatus): value.value +define function ToString(value MedicationStatus): value.value +define function ToString(value MessageSignificanceCategory): value.value +define function ToString(value Messageheader_Response_Request): value.value +define function ToString(value MimeType): value.value +define function ToString(value NameUse): value.value +define function ToString(value NamingSystemIdentifierType): value.value +define function ToString(value NamingSystemType): value.value +define function ToString(value NarrativeStatus): value.value +define function ToString(value NoteType): value.value +define function ToString(value NutritiionOrderIntent): value.value +define function ToString(value NutritionOrderStatus): value.value +define function ToString(value ObservationDataType): value.value +define function ToString(value ObservationRangeCategory): value.value +define function ToString(value ObservationStatus): value.value +define function ToString(value OperationKind): value.value +define function ToString(value OperationParameterUse): value.value +define function ToString(value OrientationType): value.value +define function ToString(value ParameterUse): value.value +define function ToString(value ParticipantRequired): value.value +define function ToString(value ParticipantStatus): value.value +define function ToString(value ParticipationStatus): value.value +define function ToString(value PaymentNoticeStatus): value.value +define function ToString(value PaymentReconciliationStatus): value.value +define function ToString(value ProcedureStatus): value.value +define function ToString(value PropertyRepresentation): value.value +define function ToString(value PropertyType): value.value +define function ToString(value ProvenanceEntityRole): value.value +define function ToString(value PublicationStatus): value.value +define function ToString(value QualityType): value.value +define function ToString(value QuantityComparator): value.value +define function ToString(value QuestionnaireItemOperator): value.value +define function ToString(value QuestionnaireItemType): value.value +define function ToString(value QuestionnaireResponseStatus): value.value +define function ToString(value ReferenceHandlingPolicy): value.value +define function ToString(value ReferenceVersionRules): value.value +define function ToString(value ReferredDocumentStatus): value.value +define function ToString(value RelatedArtifactType): value.value +define function ToString(value RemittanceOutcome): value.value +define function ToString(value RepositoryType): value.value +define function ToString(value RequestIntent): value.value +define function ToString(value RequestPriority): value.value +define function ToString(value RequestStatus): value.value +define function ToString(value ResearchElementType): value.value +define function ToString(value ResearchStudyStatus): value.value +define function ToString(value ResearchSubjectStatus): value.value +define function ToString(value ResourceType): value.value +define function ToString(value ResourceVersionPolicy): value.value +define function ToString(value ResponseType): value.value +define function ToString(value RestfulCapabilityMode): value.value +define function ToString(value RiskAssessmentStatus): value.value +define function ToString(value SPDXLicense): value.value +define function ToString(value SearchComparator): value.value +define function ToString(value SearchEntryMode): value.value +define function ToString(value SearchModifierCode): value.value +define function ToString(value SearchParamType): value.value +define function ToString(value SectionMode): value.value +define function ToString(value SequenceType): value.value +define function ToString(value ServiceRequestIntent): value.value +define function ToString(value ServiceRequestPriority): value.value +define function ToString(value ServiceRequestStatus): value.value +define function ToString(value SlicingRules): value.value +define function ToString(value SlotStatus): value.value +define function ToString(value SortDirection): value.value +define function ToString(value SpecimenContainedPreference): value.value +define function ToString(value SpecimenStatus): value.value +define function ToString(value Status): value.value +define function ToString(value StrandType): value.value +define function ToString(value StructureDefinitionKind): value.value +define function ToString(value StructureMapContextType): value.value +define function ToString(value StructureMapGroupTypeMode): value.value +define function ToString(value StructureMapInputMode): value.value +define function ToString(value StructureMapModelMode): value.value +define function ToString(value StructureMapSourceListMode): value.value +define function ToString(value StructureMapTargetListMode): value.value +define function ToString(value StructureMapTransform): value.value +define function ToString(value SubscriptionChannelType): value.value +define function ToString(value SubscriptionStatus): value.value +define function ToString(value SupplyDeliveryStatus): value.value +define function ToString(value SupplyRequestStatus): value.value +define function ToString(value SystemRestfulInteraction): value.value +define function ToString(value TaskIntent): value.value +define function ToString(value TaskPriority): value.value +define function ToString(value TaskStatus): value.value +define function ToString(value TestReportActionResult): value.value +define function ToString(value TestReportParticipantType): value.value +define function ToString(value TestReportResult): value.value +define function ToString(value TestReportStatus): value.value +define function ToString(value TestScriptRequestMethodCode): value.value +define function ToString(value TriggerType): value.value +define function ToString(value TypeDerivationRule): value.value +define function ToString(value TypeRestfulInteraction): value.value +define function ToString(value UDIEntryType): value.value +define function ToString(value UnitsOfTime): value.value +define function ToString(value Use): value.value +define function ToString(value VariableType): value.value +define function ToString(value VisionBase): value.value +define function ToString(value VisionEyes): value.value +define function ToString(value VisionStatus): value.value +define function ToString(value XPathUsageType): value.value +define function ToString(value base64Binary): value.value +define function ToBoolean(value boolean): value.value +define function ToDate(value date): value.value +define function ToDateTime(value dateTime): value.value +define function ToDecimal(value decimal): value.value +define function ToDateTime(value instant): value.value +define function ToInteger(value integer): value.value +define function ToString(value string): value.value +define function ToTime(value time): value.value +define function ToString(value uri): value.value +define function ToString(value xhtml): value.value \ No newline at end of file diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/Hospice.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/Hospice.cql new file mode 100644 index 000000000..3d93946f0 --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/Hospice.cql @@ -0,0 +1,49 @@ +library Hospice version '6.7.000' + +using QICore version '4.1.1' + +include FHIRHelpers version '4.3.000' called FHIRHelpers +include QICoreCommon version '1.5.000' called QICoreCommon +include Status version '1.6.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 + default Interval[@2024-01-01T00:00:00.0, @2025-01-01T00:00:00.0) + +context Patient + +define "Has Hospice Services": + exists ((([Encounter: "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: "Hospice Encounter"]).isEncounterPerformed()) HospiceEncounter + where HospiceEncounter.period.toInterval() overlaps day of "Measurement Period" + ) + or exists ((([Observation: "Hospice care [Minimum Data Set]"]).isAssessmentPerformed()) HospiceAssessment + where HospiceAssessment.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" + ) + or exists ((([Procedure: "Hospice Care Ambulatory"]).isInterventionPerformed()) HospicePerformed + where HospicePerformed.performed.toInterval() overlaps day of "Measurement Period" + ) + or exists (([Condition: "Hospice Diagnosis"]) HospiceCareDiagnosis + where HospiceCareDiagnosis.prevalenceInterval() overlaps day of "Measurement Period" + ) \ No newline at end of file diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PCSBMI-ModuleDefinitionLibrary.json b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PCSBMI-ModuleDefinitionLibrary.json new file mode 100644 index 000000000..df774e96b --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PCSBMI-ModuleDefinitionLibrary.json @@ -0,0 +1,583 @@ +{ + "resourceType": "Library", + "extension": [ { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode", + "valueCoding": { + "system": "http://loinc.org", + "code": "39156-5", + "display": "Body mass index (BMI) [Ratio]" + } + }, { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode", + "valueCoding": { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "code": "Z51.5", + "display": "Encounter for palliative care" + } + }, { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode", + "valueCoding": { + "system": "http://loinc.org", + "code": "71007-9", + "display": "Functional Assessment of Chronic Illness Therapy - Palliative Care Questionnaire (FACIT-Pal)" + } + }, { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode", + "valueCoding": { + "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", + "code": "VR", + "display": "virtual" + } + } ], + "status": "active", + "type": { + "coding": [ { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "module-definition" + } ] + }, + "relatedArtifact": [ { + "type": "depends-on", + "display": "QICore model information", + "resource": "http://hl7.org/fhir/Library/QICore-ModelInfo" + }, { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "Library/FHIRHelpers|4.3.000" + }, { + "type": "depends-on", + "display": "Library Hospice", + "resource": "Library/Hospice|6.7.000" + }, { + "type": "depends-on", + "display": "Library PalliativeCare", + "resource": "Library/PalliativeCare|1.7.000" + }, { + "type": "depends-on", + "display": "Library QICoreCommon", + "resource": "Library/QICoreCommon|1.5.000" + }, { + "type": "depends-on", + "display": "Library SDE", + "resource": "Library/SupplementalDataElements|3.4.000" + }, { + "type": "depends-on", + "display": "Code system ActCode", + "resource": "http://terminology.hl7.org/CodeSystem/v3-ActCode" + }, { + "type": "depends-on", + "display": "Code system ICD10CM", + "resource": "http://hl7.org/fhir/sid/icd-10-cm" + }, { + "type": "depends-on", + "display": "Code system LOINC", + "resource": "http://loinc.org" + }, { + "type": "depends-on", + "display": "Code system ObservationCategoryCodes", + "resource": "http://terminology.hl7.org/CodeSystem/observation-category" + }, { + "type": "depends-on", + "display": "Value set Encounter to Evaluate BMI", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1751" + }, { + "type": "depends-on", + "display": "Value set Follow Up for Above Normal BMI", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1525" + }, { + "type": "depends-on", + "display": "Value set Follow Up for Below Normal BMI", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1528" + }, { + "type": "depends-on", + "display": "Value set Medical Reason", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1007" + }, { + "type": "depends-on", + "display": "Value set Medications for Above Normal BMI", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1561" + }, { + "type": "depends-on", + "display": "Value set Medications for Below Normal BMI", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1562" + }, { + "type": "depends-on", + "display": "Value set Overweight or Obese", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.502" + }, { + "type": "depends-on", + "display": "Value set Patient Declined", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1582" + }, { + "type": "depends-on", + "display": "Value set Pregnancy or Other Related Diagnoses", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1623" + }, { + "type": "depends-on", + "display": "Value set Referrals Where Weight Assessment May Occur", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1527" + }, { + "type": "depends-on", + "display": "Value set Underweight", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1563" + } ], + "parameter": [ { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Resource" + }, { + "name": "Qualifying Encounter during Day of Measurement Period", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, { + "name": "Pregnancy", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Denominator Exclusions", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, { + "name": "BMI during Measurement Period", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Documented High BMI during Measurement Period", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "High BMI Interventions Ordered", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "High BMI Interventions Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "High BMI and Follow up Provided", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Documented Low BMI during Measurement Period", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Low BMI Interventions Ordered", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Low BMI Interventions Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Low BMI and Follow up Provided", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Has Normal BMI", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, { + "name": "Medical Reason for Not Documenting a Follow up Plan for Low or High BMI", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Medical Reason or Patient Reason for Not Performing BMI Exam", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "Denominator Exceptions", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, { + "name": "SDE Ethnicity", + "use": "out", + "min": 0, + "max": "1", + "type": "Resource" + }, { + "name": "SDE Payer", + "use": "out", + "min": 0, + "max": "*", + "type": "Resource" + }, { + "name": "SDE Race", + "use": "out", + "min": 0, + "max": "1", + "type": "Resource" + }, { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + } ], + "dataRequirement": [ { + "type": "Patient", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient" ], + "mustSupport": [ "ethnicity", "race" ] + }, { + "type": "Encounter", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-encounter" ], + "mustSupport": [ "type", "hospitalization", "hospitalization.dischargeDisposition", "period" ], + "codeFilter": [ { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + } ] + }, { + "type": "Encounter", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-encounter" ], + "mustSupport": [ "type", "period" ], + "codeFilter": [ { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.1003" + } ] + }, { + "type": "Encounter", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-encounter" ], + "mustSupport": [ "type", "period" ], + "codeFilter": [ { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1090" + } ] + }, { + "type": "Encounter", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-encounter" ], + "mustSupport": [ "type", "period", "class", "status", "status.value" ], + "codeFilter": [ { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1751" + }, { + "path": "class" + }, { + "path": "status.value", + "code": [ { + "code": "finished" + } ] + } ] + }, { + "type": "Condition", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" ], + "mustSupport": [ "code", "clinicalStatus" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1623" + }, { + "path": "clinicalStatus" + } ] + }, { + "type": "Condition", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" ], + "mustSupport": [ "code" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.1165" + } ] + }, { + "type": "Condition", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" ], + "mustSupport": [ "code" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.1167" + } ] + }, { + "type": "Condition", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" ], + "mustSupport": [ "code" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.502" + } ] + }, { + "type": "Condition", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" ], + "mustSupport": [ "code" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1563" + } ] + }, { + "type": "Observation", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation" ], + "mustSupport": [ "code", "value", "effective" ], + "codeFilter": [ { + "path": "code", + "code": [ { + "system": "http://loinc.org", + "code": "45755-6", + "display": "Hospice care [Minimum Data Set]" + } ] + }, { + "path": "value" + } ] + }, { + "type": "Observation", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation" ], + "mustSupport": [ "code", "effective" ], + "codeFilter": [ { + "path": "code", + "code": [ { + "system": "http://loinc.org", + "code": "71007-9", + "display": "Functional Assessment of Chronic Illness Therapy - Palliative Care Questionnaire (FACIT-Pal)" + } ] + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest" ], + "mustSupport": [ "code", "authoredOn", "authoredOn.value" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1584" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest" ], + "mustSupport": [ "code" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1525" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest" ], + "mustSupport": [ "code" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1528" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest" ], + "mustSupport": [ "code" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1527" + } ] + }, { + "type": "Procedure", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-procedure" ], + "mustSupport": [ "code", "performed" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1584" + } ] + }, { + "type": "Procedure", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-procedure" ], + "mustSupport": [ "code", "performed" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1135" + } ] + }, { + "type": "Procedure", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-procedure" ], + "mustSupport": [ "code", "reasonCode", "status", "status.value", "performed" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1525" + } ] + }, { + "type": "Procedure", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-procedure" ], + "mustSupport": [ "code", "reasonCode", "status", "status.value", "performed" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1528" + } ] + }, { + "type": "Observation", + "profile": [ "http://hl7.org/fhir/StructureDefinition/bmi" ], + "mustSupport": [ "value", "status", "status.value", "effective" ] + }, { + "type": "Medication", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication" ], + "mustSupport": [ "id" ] + }, { + "type": "Medication", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication" ], + "mustSupport": [ "id" ] + }, { + "type": "MedicationRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ], + "mustSupport": [ "medication" ], + "codeFilter": [ { + "path": "medication", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1561" + } ] + }, { + "type": "MedicationRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ], + "mustSupport": [ "medication.reference" ] + }, { + "type": "MedicationRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ], + "mustSupport": [ "medication" ], + "codeFilter": [ { + "path": "medication", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1562" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicenotrequested" ], + "mustSupport": [ "code", "authoredOn", "authoredOn.value", "status", "status.value", "extension" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1527" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicenotrequested" ], + "mustSupport": [ "code", "authoredOn", "authoredOn.value", "status", "status.value", "extension" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1527" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicenotrequested" ], + "mustSupport": [ "code", "authoredOn", "authoredOn.value", "status", "status.value", "extension" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1525" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicenotrequested" ], + "mustSupport": [ "code", "authoredOn", "authoredOn.value", "status", "status.value", "extension" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1525" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicenotrequested" ], + "mustSupport": [ "code", "authoredOn", "authoredOn.value", "status", "status.value", "extension" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1528" + } ] + }, { + "type": "ServiceRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicenotrequested" ], + "mustSupport": [ "code", "authoredOn", "authoredOn.value", "status", "status.value", "extension" ], + "codeFilter": [ { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.1.1528" + } ] + }, { + "type": "MedicationRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-mednotrequested" ], + "mustSupport": [ "medication", "authoredOn", "authoredOn.value", "status", "status.value", "reasonCode" ], + "codeFilter": [ { + "path": "medication", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1561" + } ] + }, { + "type": "MedicationRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-mednotrequested" ], + "mustSupport": [ "medication", "authoredOn", "authoredOn.value", "status", "status.value", "reasonCode" ], + "codeFilter": [ { + "path": "medication", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1561" + } ] + }, { + "type": "MedicationRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-mednotrequested" ], + "mustSupport": [ "medication", "authoredOn", "authoredOn.value", "status", "status.value", "reasonCode" ], + "codeFilter": [ { + "path": "medication", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1562" + } ] + }, { + "type": "MedicationRequest", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-mednotrequested" ], + "mustSupport": [ "medication", "authoredOn", "authoredOn.value", "status", "status.value", "reasonCode" ], + "codeFilter": [ { + "path": "medication", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1562" + } ] + }, { + "type": "Observation", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observationnotdone" ], + "mustSupport": [ "code", "effective", "status", "status.value", "extension" ], + "codeFilter": [ { + "path": "code", + "code": [ { + "system": "http://loinc.org", + "code": "39156-5", + "display": "Body mass index (BMI) [Ratio]" + } ] + } ] + }, { + "type": "Coverage", + "profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-coverage" ], + "mustSupport": [ "type", "period" ], + "codeFilter": [ { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + } ] + } ] +} diff --git a/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PCSBMIScreenAndFollowUpFHIR.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PCSBMIScreenAndFollowUpFHIR.cql new file mode 100644 index 000000000..b0c037707 --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PCSBMIScreenAndFollowUpFHIR.cql @@ -0,0 +1,199 @@ +library PCSBMIScreenAndFollowUpFHIR version '0.1.000' + +using QICore version '4.1.1' + +include FHIRHelpers version '4.3.000' called FHIRHelpers +include Hospice version '6.7.000' called Hospice +include PalliativeCare version '1.7.000' called PalliativeCare +include QICoreCommon version '1.5.000' called QICoreCommon +include SupplementalDataElements version '3.4.000' called SDE + +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[@2024-01-01T00:00:00.0, @2025-01-01T00:00:00.0) + +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 "Pregnancy" + +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": + ["observation-bmi"] 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 in Interval[18.5 'kg/m2', 24.9 '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 [Condition: "Overweight or Obese"] OverweightObese + where ( OverweightObese.isProblemListItem ( ) + or OverweightObese.isHealthConcern ( ) + ) + and OverweightObese.isActive ( ) + and OverweightObese.prevalenceInterval ( ) starts before or on day of HighInterventionsOrdered.authoredOn + and not ( OverweightObese.prevalenceInterval ( ) ends before day of HighInterventionsOrdered.authoredOn ) + ) + ) + +define "High BMI Interventions Performed": + ( [Procedure: "Follow Up for Above Normal BMI"] HighInterventionsPerformed + where HighInterventionsPerformed.reasonCode in "Overweight or Obese" + and HighInterventionsPerformed.status = 'completed' + or ( exists [Condition: "Overweight or Obese"] OverweightObese + where OverweightObese.isHealthConcern ( ) + and OverweightObese.isActive ( ) + and OverweightObese.prevalenceInterval ( ) starts before or on day of HighInterventionsPerformed.performed.toInterval ( ) + and not ( OverweightObese.prevalenceInterval ( ) ends before day of HighInterventionsPerformed.performed.toInterval ( ) ) + ) + ) + +define "Medical Reason or Patient Reason for Not Performing BMI Exam": + [ObservationNotDone: 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 ( NoBMI.notDoneReason in "Patient Declined" + or NoBMI.notDoneReason 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 [Condition: "Underweight"] Underweight + where ( Underweight.isHealthConcern ( ) ) + and Underweight.isActive ( ) + and Underweight.prevalenceInterval ( ) starts before or on day of LowInterventionsOrdered.authoredOn + and not ( Underweight.prevalenceInterval ( ) ends before 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 [Condition: "Underweight"] Underweight + where ( Underweight.isHealthConcern ( ) ) + and Underweight.isActive ( ) + and Underweight.prevalenceInterval ( ) starts before or on day of LowInterventionsPerformed.performed.toInterval ( ) + and not ( Underweight.prevalenceInterval ( ) ends before day of LowInterventionsPerformed.performed.toInterval ( ) + and LowInterventionsPerformed.performed.toInterval ( ) during day of "Measurement Period" + ) + ) + ) + +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 NoBMIFollowUp.reasonRefused 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 "Pregnancy": + [Condition: "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: "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/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PalliativeCare.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PalliativeCare.cql new file mode 100644 index 000000000..2496506fc --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PalliativeCare.cql @@ -0,0 +1,34 @@ +library PalliativeCare version '1.7.000' + +using QICore version '4.1.1' + +include FHIRHelpers version '4.3.000' called FHIRHelpers +include QICoreCommon version '1.5.000' called QICoreCommon +include Status version '1.6.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 + default Interval[@2024-01-01T00:00:00.0, @2025-01-01T00:00:00.0) + +context Patient + +define "Has Palliative Care in the Measurement Period": + exists ((([Observation: "Functional Assessment of Chronic Illness Therapy - Palliative Care Questionnaire (FACIT-Pal)"]).isAssessmentPerformed()) PalliativeAssessment + where PalliativeAssessment.effective.toInterval() overlaps day of "Measurement Period" + ) + or exists ([Condition: "Palliative Care Diagnosis"] PalliativeDiagnosis + where PalliativeDiagnosis.prevalenceInterval() overlaps day of "Measurement Period" + ) + or exists ((([Encounter: "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/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/QICoreCommon.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/QICoreCommon.cql new file mode 100644 index 000000000..f9a7aaa33 --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/QICoreCommon.cql @@ -0,0 +1,569 @@ +library QICoreCommon version '1.5.000' + +using QICore version '4.1.1' + +include FHIRHelpers version '4.3.000' + +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 'VR' + +// 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" +code "recurrence": 'recurrence' from "ConditionClinicalStatusCodes" +code "relapse": 'relapse' from "ConditionClinicalStatusCodes" +code "inactive": 'inactive' from "ConditionClinicalStatusCodes" +code "remission": 'remission' from "ConditionClinicalStatusCodes" +code "resolved": 'resolved' from "ConditionClinicalStatusCodes" + +// Condition Verification Status Codes - Consider value sets for these +code "unconfirmed": 'unconfirmed' from ConditionVerificationStatusCodes +code "provisional": 'provisional' from ConditionVerificationStatusCodes +code "differential": 'differential' from ConditionVerificationStatusCodes +code "confirmed": 'confirmed' from ConditionVerificationStatusCodes +code "refuted": 'refuted' from ConditionVerificationStatusCodes +code "entered-in-error": 'entered-in-error' from ConditionVerificationStatusCodes + +code "allergy-active": 'active' from "AllergyIntoleranceClinicalStatusCodes" +code "allergy-inactive": 'inactive' from "AllergyIntoleranceClinicalStatusCodes" +code "allergy-resolved": 'resolved' from "AllergyIntoleranceClinicalStatusCodes" + +// Allergy/Intolerance Verification Status Codes - Consider value sets for these +code "allergy-unconfirmed": 'unconfirmed' from AllergyIntoleranceVerificationStatusCodes +code "allergy-confirmed": 'confirmed' from AllergyIntoleranceVerificationStatusCodes +code "allergy-refuted": 'refuted' from AllergyIntoleranceVerificationStatusCodes + +// 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 diagnosis' + +// 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' +code "clinical-test": 'clinical-test' from "USCoreObservationCategoryExtensionCodes" display 'Clinical Test' + +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 Condition): + 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 Condition, 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 Condition): + 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 Condition): + exists (condition.category C + where C ~ "encounter-diagnosis" + ) + +/* +@description: Returns true if the given condition is a health concern +*/ +define fluent function isHealthConcern(condition Condition): + exists (condition.category C + where C ~ "health-concern" + ) + +/* +@description: Returns true if the given observation has the given category +*/ +define fluent function hasCategory(observation Observation, 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 Observation): + exists (observation.category C + where C ~ "social-history" + ) + +/* +@description: Returns true if the given observation is a vital sign +*/ +define fluent function isVitalSign(observation Observation): + exists (observation.category C + where C ~ "vital-signs" + ) + +/* +@description: Returns true if the given observation is an imaging observation +*/ +define fluent function isImaging(observation Observation): + exists (observation.category C + where C ~ "imaging" + ) + +/* +@description: Returns true if the given observation is a laboratory observation +*/ +define fluent function isLaboratory(observation Observation): + exists (observation.category C + where C ~ "laboratory" + ) + +/* +@description: REturns true if the given observation is a procedure observation +*/ +define fluent function isProcedure(observation Observation): + exists (observation.category C + where C ~ "procedure" + ) + +/* +@description: Returns true if the given observation is a survey observation +*/ +define fluent function isSurvey(observation Observation): + exists (observation.category C + where C ~ "survey" + ) + +/* +@description: Returns true if the given observation is an exam observation +*/ +define fluent function isExam(observation Observation): + exists (observation.category C + where C ~ "exam" + ) + +/* +@description: Returns true if the given observation is a therapy observation +*/ +define fluent function isTherapy(observation Observation): + exists (observation.category C + where C ~ "therapy" + ) + +/* +@description: Returns true if the given observation is an activity observation +*/ +define fluent function isActivity(observation Observation): + exists (observation.category C + where C ~ "activity" + ) + +/* +@description: Returns true if the given observation is a clinical test result +*/ +define fluent function isClinicalTest(observation Observation): + exists (observation.category C + where C ~ "clinical-test" + ) + +/* +@description: Returns true if the given MedicationRequest has a category of Community +*/ +define fluent function isCommunity(medicationRequest MedicationRequest): + exists (medicationRequest.category C + where C ~ Community + ) + +/* +@description: Returns true if the given MedicationRequest has a category of Community +*/ +define fluent function isCommunity(medicationRequest MedicationNotRequested): + exists (medicationRequest.category C + where C ~ Community + ) + +/* +@description: Returns true if the given MedicationRequest has a category of Discharge +*/ +define fluent function isDischarge(medicationRequest MedicationRequest): + exists (medicationRequest.category C + where C ~ Discharge + ) + +/* +@description: Returns true if the given MedicationRequest has a category of Discharge +*/ +define fluent function isDischarge(medicationRequest MedicationNotRequested): + exists (medicationRequest.category C + where C ~ Discharge + ) + +/* +@description: Returns true if the given DeviceRequest is a negation (i.e. do not perform this order) +*/ +define fluent function doNotPerform(deviceRequest DeviceRequest): + singleton from ( + deviceRequest.modifierExtension E + where E.url = 'http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-doNotPerform' + return E.value as Boolean + ) + +/* +@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. +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>): + 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 QICore.Timing then + null 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. +Any other input will reslt in a null DateTime Interval +*/ +define fluent function toInterval(choice Choice, Interval>): + 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) + 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 Condition): + 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 Condition): + 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, the resulting interval will have +a closed ending boundary. If the condition is not active, the resulting interval will have an open ending boundary. +@deprecated: This function is deprecated. Use the `prevalenceInterval()` fluent function instead +*/ +define function ToPrevalenceInterval(condition Condition): +if condition.clinicalStatus ~ "active" + or condition.clinicalStatus ~ "recurrence" + or condition.clinicalStatus ~ "relapse" then + Interval[start of ToInterval(condition.onset), end of ToAbatementInterval(condition)] +else + Interval[start of ToInterval(condition.onset), end of ToAbatementInterval(condition)) + +/* +@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, the resulting interval will have +a closed ending boundary. If the condition is not active, the resulting interval will have an open ending boundary. +*/ +define fluent function prevalenceInterval(condition Condition): +if condition.clinicalStatus ~ "active" + or condition.clinicalStatus ~ "recurrence" + or condition.clinicalStatus ~ "relapse" then + Interval[start of condition.onset.toInterval(), end of condition.abatementInterval()] +else + Interval[start of condition.onset.toInterval(), end of condition.abatementInterval()) + +/* +@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: 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/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/Status.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/Status.cql new file mode 100644 index 000000000..91d4c5faa --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/Status.cql @@ -0,0 +1,233 @@ +library Status version '1.6.000' + + +using QICore version '4.1.1' + +include FHIRHelpers version '4.3.000' 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 "active": 'active' from "ConditionClinicalStatusCodes" + +context Patient + +//This library contains functions that are based on QDM 5.6 to QICore 4.1.1 March 2023, used for measures authored by NCQA, with notes on intentional differences. The functions in this library have similarities to QICoreCommon (i.e., category constraints) but different in that there are additional constraints such as status or clinical status for each resource. + +//Assessment, Performed +define function "Final Survey Observation"(Obs List): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + and exists ( O.category ObservationCategory + where ( ObservationCategory ) ~ "survey" + ) + +//Similar but different from QICoreCommon.isSurvey, which does not have status constraints +define fluent function isAssessmentPerformed(Obs List): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + and exists ( O.category ObservationCategory + where ( ObservationCategory ) ~ "survey" + ) + +//Diagnosis +define function "Active Condition"(Condition List): + Condition C + where C.clinicalStatus ~ "active" + +//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 ~ "active" + +//Device, Order - Personal Use Devices: active and completed only +define function "Completed or Ongoing Device Request"(DeviceRequest List): + DeviceRequest D + where D.status in { 'active', 'completed' } + and D.intent = 'order' + +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 +//Intervention, Order: active and completed only +//Laboratory Test, Order: active and completed only +define function "Completed or Ongoing Service Request"(ServiceRequest List): + ServiceRequest S + where S.status in { 'active', 'completed' } + and S.intent = 'order' + +define fluent function isDiagnosticStudyOrder(ServiceRequest List): + ServiceRequest S + where S.status in { 'active', 'completed' } + and S.intent = 'order' + +define fluent function isInterventionOrder(ServiceRequest List): + ServiceRequest S + where S.status in { 'active', 'completed' } + and S.intent = 'order' + +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 O + where O.status in {'final', 'amended', 'corrected' } + +define fluent function isDiagnosticStudyPerformed(Obs List): + Obs O + where O.status in {'final', 'amended', 'corrected' } + +//Encounter, Performed +//General usage unless required otherwise by the measure's specific use case (e.g., follow-up encounters) +define function "Finished Encounter"(Enc List): + Enc E + where E.status in {'finished', 'arrived', 'triaged', 'in-progress', 'onleave'} + +define fluent function isEncounterPerformed(Enc List): + Enc E + where E.status in {'finished', 'arrived', 'triaged', 'in-progress', 'onleave'} + +// //Encounter, Performed: finished and in-progress only +// define function "Finished or Ongoing Encounter"(Enc List): +// Enc E +// where E.status in { 'finished', 'in-progress' } + +// define fluent function "isEncounterPerformed"(Enc Encounter): +// Enc.status in { 'finished', 'in-progress' } + +//Immunization, Administered: completed only +define function "Completed Immunization"(Immunization List): + Immunization I + where I.status ~ 'completed' + +define fluent function isImmunizationAdministered(Immunization List): + Immunization I + where I.status ~ 'completed' + +//Intervention, Performed +//Procedure, Performed +define function "Completed Procedure"(Proc List): + Proc P + where P.status ~ 'completed' + +define fluent function isInterventionPerformed(Proc List): + Proc P + where P.status ~ 'completed' + +define fluent function isProcedurePerformed(Proc List): + Proc P + where P.status ~ 'completed' + +//Laboratory Test, Performed +define function "Final Lab Observation"(Obs List): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + and exists ( O.category ObservationCategory + where ( ObservationCategory ) ~ "laboratory" + ) + +define fluent function isLaboratoryTestPerformed(Obs List): + 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): + MedicationRequest M + where M.status = 'active' + and M.intent = 'order' + +define fluent function isMedicationActive(MedicationRequest List): + MedicationRequest M + where M.status = 'active' + and M.intent = 'order' + +//Medication, Dispensed +define function "Dispensed Medication"(Med List): + Med M + where M.status in { 'completed', 'in-progress', 'on-hold' } + +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): + MedicationRequest M + where M.status in { 'active', 'completed' } + and M.intent = 'order' + +define fluent function isMedicationOrder(MedicationRequest List): + MedicationRequest M + where M.status in { 'active', 'completed' } + and M.intent = 'order' + +//Physical Exam, Performed +define function "Final Exam Observation"(Obs List): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + and exists ( O.category ObservationCategory + where ( ObservationCategory ) ~ "exam" + ) + +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 function "BloodPressure"(Obs List<"QICore.observation-bp">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define function "BodyHeight"(Obs List<"QICore.observation-bodyheight">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define function "BodyWeight"(Obs List<"QICore.observation-bodyweight">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define function "BMI"(Obs List<"QICore.observation-bmi">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +//fluent function versions +define fluent function isObservationBP(Obs List<"QICore.observation-bp">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define fluent function isObservationBodyHeight(Obs List<"QICore.observation-bodyheight">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define fluent function isObservationBodyWeight(Obs List<"QICore.observation-bodyweight">): + Obs O + where O.status in { 'final', 'amended', 'corrected' } + +define fluent function isObservationBMI(Obs List<"QICore.observation-bmi">): + 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/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/SupplementalDataElements.cql b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/SupplementalDataElements.cql new file mode 100644 index 000000000..0430621e3 --- /dev/null +++ b/Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/SupplementalDataElements.cql @@ -0,0 +1,60 @@ +/* +@update: BTR 2020-03-31 -> +Incremented version to 2.0.0 + +@update: BTR 2022-05-26 -> +Updated FHIR version to 4.0.1 +Updated FHIRHelpers version to 4.0.002 +Updated for AU 2022 Content + +@update: JSR 2023-07-18 +Updated "Payer" value set name to "Payer Type" + +@update: JSR 2023-07-20 -> +Updated FHIRHelpers version to 4.3.000 +*/ +library SupplementalDataElements version '3.4.000' + +using QICore version '4.1.1' + +include FHIRHelpers version '4.3.000' called FHIRHelpers + +codesystem "AdministrativeGender": 'http://hl7.org/fhir/v3/AdministrativeGender' + +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 "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' + +context Patient + +define "SDE Ethnicity": + Patient.ethnicity E + return Tuple { + codes: { E.ombCategory } union E.detailed, + display: E.text + } + +define "SDE Payer": + [Coverage: type in "Payer Type"] Payer + return { + code: Payer.type, + period: Payer.period + } + +define "SDE Race": + Patient.race R + return Tuple { + codes: R.ombCategory union R.detailed, + display: R.text + } + +define "SDE Sex": + case + when Patient.gender = 'male' then "M" + when Patient.gender = 'female' then "F" + else null + end \ No newline at end of file