/*
This example is a work in progress and should not be considered a final specification
or recommendation for guidance. This example will help guide and direct the process
of finding conventions and usage patterns that meet the needs of the various stakeholders
in the measure development community.
@update: BTR 2020-03-31 ->
Incremented version to 5.0.000
Updated FHIR version to 4.0.1
Changed timezone keyword to timezoneoffset for use with CQL 1.4
Removed Normalize Onset in favor of more general Normalize Interval
*/
library MATGlobalCommonFunctions version '5.0.000'
using FHIR version '4.0.1'
include FHIRHelpers version '4.0.1' called FHIRHelpers
codesystem "LOINC": 'http://loinc.org'
codesystem "SNOMEDCT": 'http://snomed.info/sct'
codesystem "RoleCode": 'http://terminology.hl7.org/CodeSystem/v3-RoleCode'
codesystem "Diagnosis Role": 'http://terminology.hl7.org/CodeSystem/diagnosis-role'
codesystem "RequestIntent": 'http://terminology.hl7.org/CodeSystem/request-intent'
codesystem "MedicationRequestCategory": 'http://terminology.hl7.org/CodeSystem/medicationrequest-category'
codesystem "ConditionClinicalStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-clinical'
codesystem "ConditionVerificationStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-verification'
codesystem "AllergyIntoleranceClinicalStatusCodes": 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical'
codesystem "AllergyIntoleranceVerificationStatusCodes": 'http://terminology.hl7.org/CodeSystem/allergyintolerance-verification'
valueset "Encounter Inpatient": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307'
valueset "Emergency Department Visit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.292'
valueset "Observation Services": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1111.143'
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'
// 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 "Community": 'community' from "MedicationRequestCategory" display 'Community'
code "Discharge": 'discharge' from "MedicationRequestCategory" display 'Discharge'
parameter "Measurement Period" Interval<DateTime>
default Interval[@2019-01-01T00:00:00.0, @2020-01-01T00:00:00.0)
context Patient
define "Inpatient Encounter":
[Encounter: "Encounter Inpatient"] EncounterInpatient
where EncounterInpatient.status = 'finished'
and "LengthInDays"(EncounterInpatient.period) <= 120
and EncounterInpatient.period ends during "Measurement Period"
define function "ToDate"(Value DateTime):
DateTime(year from Value, month from Value, day from Value, 0, 0, 0, 0, timezoneoffset from Value)
define function "CalendarAgeInDaysAt"(BirthDateTime DateTime, AsOf DateTime):
days between ToDate(BirthDateTime)and ToDate(AsOf)
define function "CalendarAgeInDays"(BirthDateTime DateTime):
CalendarAgeInDaysAt(BirthDateTime, Today())
define function "CalendarAgeInMonthsAt"(BirthDateTime DateTime, AsOf DateTime):
months between ToDate(BirthDateTime)and ToDate(AsOf)
define function "CalendarAgeInMonths"(BirthDateTime DateTime):
CalendarAgeInMonthsAt(BirthDateTime, Today())
define function "CalendarAgeInYearsAt"(BirthDateTime DateTime, AsOf DateTime):
years between ToDate(BirthDateTime)and ToDate(AsOf)
define function "CalendarAgeInYears"(BirthDateTime DateTime):
CalendarAgeInYearsAt(BirthDateTime, Today())
define function "LengthInDays"(Value Interval<DateTime>):
difference in days between start of Value and end of Value
define function "ED Visit"(TheEncounter FHIR.Encounter):
singleton from (
[Encounter: "Emergency Department Visit"] EDVisit
where EDVisit.status = 'finished'
and EDVisit.period ends 1 hour or less on or before start of FHIRHelpers.ToInterval(TheEncounter.period)
)
define function "Hospitalization"(TheEncounter FHIR.Encounter):
( "ED Visit"(TheEncounter) ) X
return
if X is null then TheEncounter.period
else Interval[start of FHIRHelpers.ToInterval(X.period), end of FHIRHelpers.ToInterval(TheEncounter.period)]
define function "Hospitalization Locations"(TheEncounter FHIR.Encounter):
( "ED Visit"(TheEncounter) ) EDEncounter
return
if EDEncounter is null then TheEncounter.location
else flatten { EDEncounter.location, TheEncounter.location }
define function "Hospitalization Length of Stay"(TheEncounter FHIR.Encounter):
LengthInDays("Hospitalization"(TheEncounter))
define function "Hospital Admission Time"(TheEncounter FHIR.Encounter):
start of "Hospitalization"(TheEncounter)
define function "Hospital Discharge Time"(TheEncounter FHIR.Encounter):
end of FHIRHelpers.ToInterval(TheEncounter.period)
define function "Hospital Arrival Time"(TheEncounter FHIR.Encounter):
start of FHIRHelpers.ToInterval(First(
( "Hospitalization Locations"(TheEncounter) ) HospitalLocation
sort by start of FHIRHelpers.ToInterval(period)
).period)
define function "HospitalizationWithObservation"(TheEncounter FHIR.Encounter):
TheEncounter Visit
let ObsVisit: Last([Encounter: "Observation Services"] LastObs
where LastObs.period ends 1 hour or less on or before start of Visit.period
sort by end of period
),
VisitStart: Coalesce(start of ObsVisit.period, start of Visit.period),
EDVisit: Last([Encounter: "Emergency Department Visit"] LastED
where LastED.period ends 1 hour or less on or before VisitStart
sort by end of period
)
return Interval[Coalesce(start of EDVisit.period, VisitStart), end of Visit.period]
define function "HospitalizationWithObservationLengthofStay"(Encounter FHIR.Encounter):
"LengthInDays"("HospitalizationWithObservation"(Encounter))
/*
*
* CQFMeasures Common Logic
*
*/
define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string, FHIR.Age, FHIR.Range>):
case
when choice is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]
when choice is FHIR.Period then
FHIRHelpers.ToInterval(choice as FHIR.Period)
when choice is FHIR.instant then
Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as FHIR.instant)]
when choice is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1 year)
when choice is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).high) + 1 year)
when choice is FHIR.Timing then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a Timing type')
when choice is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else
null as Interval<DateTime>
end
define function "Normalize Abatement"(condition Condition):
if condition.abatement is FHIR.dateTime then
Interval[FHIRHelpers.ToDateTime(condition.abatement as FHIR.dateTime), FHIRHelpers.ToDateTime(condition.abatement as FHIR.dateTime)]
else if condition.abatement is FHIR.Period then
FHIRHelpers.ToInterval(condition.abatement as FHIR.Period)
else if condition.abatement is FHIR.string then
Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')
else if condition.abatement is FHIR.Age then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(condition.abatement as FHIR.Age),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(condition.abatement as FHIR.Age) + 1 year)
else if condition.abatement is FHIR.Range then
Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((condition.abatement as FHIR.Range).low),
FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((condition.abatement as FHIR.Range).high) + 1 year)
else if condition.abatement is FHIR.boolean then
Interval[end of "Normalize Interval"(condition.onset), condition.recordedDate)
else null
define function "Prevalence Period"(condition Condition):
Interval[start of "Normalize Interval"(condition.onset), end of "Normalize Abatement"(condition))
define function "GetId"(uri String):
Last(Split(uri, '/'))
define function "EncounterDiagnosis"(Encounter Encounter):
Encounter.diagnosis D
return singleton from ([Condition] C where C.id = "GetId"(D.condition.reference))
// Returns the condition that is specified as the principal diagnosis for the encounter
// TODO: BTR 2019-07-30: Shouldn't need the FHIRHelpers reference here, investigate
define function "PrincipalDiagnosis"(Encounter Encounter):
(singleton from (Encounter.diagnosis D where FHIRHelpers.ToInteger(D.rank) = 1)) PD
return singleton from ([Condition] C where C.id = "GetId"(PD.condition.reference))
// Returns the location for the given location reference
define function GetLocation(reference Reference):
singleton from (
[Location] L where L.id = GetId(reference.reference)
)
/*
NOTE: Extensions are not the preferred approach, but are used as a way to access
content that is defined by extensions but not yet surfaced in the
CQL model info.
*/
define function "GetExtensions"(domainResource DomainResource, url String):
domainResource.extension E
where E.url = ('http://hl7.org/fhir/us/qicore/StructureDefinition/' + url)
return E
define function "GetExtension"(domainResource DomainResource, url String):
singleton from "GetExtensions"(domainResource, url)
/*
NOTE: Extensions are not the preferred approach, but are used as a way to access
content that is defined by extensions but not yet surfaced in the
CQL model info.
*/
define function "GetExtensions"(element Element, url String):
element.extension E
where E.url = (url)
return E
define function "GetExtension"(element Element, url String):
singleton from "GetExtensions"(element, url)
/*
NOTE: Extensions are not the preferred approach, but are used as a way to access
content that is defined by extensions but not yet surfaced in the
CQL model info.
*/
define function "GetBaseExtensions"(domainResource DomainResource, url String):
domainResource.extension E
where E.url = ('http://hl7.org/fhir/StructureDefinition/' + url)
return E
define function "GetBaseExtension"(domainResource DomainResource, url String):
singleton from "GetBaseExtensions"(domainResource, url)
/*
NOTE: Provenance is not the preferred approach, this is provided only as an illustration
for what using Provenance could look like, and is not a tested pattern
*/
define function "GetProvenance"(resource Resource):
singleton from ([Provenance: target in resource.id])
define function "GetMedicationCode"(request MedicationRequest):
if request.medication is CodeableConcept then
request.medication as CodeableConcept
else
(singleton from ([Medication] M where M.id = GetId((request.medication as Reference).reference))).code
|