Id: OHSUHTNCommon Url: Common Logic Version: 0.1.0 Name: OHSUHTNCommon Title: Common Logic Status: draft Date: 2026-02-19 22:08:15+0000 Publisher: Oregon Health and Science University Description: Common logic spanning multiple interventions. Jurisdiction: 840 Copyright: Published by OHSU under an Apache 2.0 License Related Artifacts: Type Resource depends-on http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1 depends-on http://fhir.org/guides/ohsuhypertensionig/Library/FHIRHelpers|4.0.1 depends-on http://loinc.org depends-on SNOMED CT (all versions) depends-on Condition Clinical Status Codes depends-on ConditionVerificationStatus depends-on ActCode depends-on Condition Category Codes depends-on Medicationrequest status depends-on Systolic blood pressure depends-on Diastolic Blood Pressure depends-on Blood Pressure Measured depends-on Ambulatory Blood Pressure Monitoring (ABPM) depends-on Antihypertensive Medications depends-on Anti Hypertensive Drugs depends-on Hypertension depends-on Non essential Hypertension SNOMEDCT depends-on Pregnancy depends-on End Stage Renal Disease depends-on Hospice care ambulatory Parameters: Name Type Min Max In/Out Patient Patient 0 1 Out Patient Under Age 18 boolean 0 1 Out Patient Over Age 100 boolean 0 1 Out Conditions Condition 0 * Out Problem Conditions Condition 0 * Out Encounter Conditions Condition 0 * Out Condition Indicating End Stage Renal Disease Condition 0 * Out Condition Indicating Pregnancy Condition 0 * Out Problem Condition Indicating Preexisting Hypertension Condition 0 * Out Encounter Condition Indicating Preexisting Hypertension Condition 0 * Out Condition Indicating Preexisting Hypertension Condition 0 * Out All Observations Observation 0 * Out Component BP Observations Resource 0 * Out Paired BP Observations Resource 0 * Out All BP Observations Resource 0 * Out Most Recent BP Reading Resource 0 1 Out HTN High Crisis boolean 0 1 Out HTN Low Crisis boolean 0 1 Out HTN Crisis boolean 0 1 Out Blood Pressure Observations for Last 2 Years Resource 0 * Out Blood Pressure Observations for Last 2 Years Descending Resource 0 * Out Home Blood Pressure Observations Resource 0 * Out Office Blood Pressure Observations Resource 0 * Out Ambulatory Blood Pressure Monitoring Observations Resource 0 * Out Has BP Set boolean 0 1 Out Has 12 Home BPs boolean 0 1 Out Most Recent BP Set Resource 0 * Out Average All BP Last 2 Years Resource 0 1 Out Average Most Recent BP Set Resource 0 1 Out Patient Has Potential HTN Stage 1 BP boolean 0 1 Out Patient Has Potential HTN Stage 2 BP boolean 0 1 Out Qualifying Blood Pressure Goals Goal 0 * Out Most Recently Established Blood Pressure Goal Goal 0 1 Out Patient has a BP Goal boolean 0 1 Out BP from Most Recent Goal Resource 0 1 Out Above Goal Average Most Recent boolean 0 1 Out Active Medication Requests MedicationRequest 0 * Out Medication Requests With Medication MedicationRequest 0 * Out Patient is Using Antihypertensive Medications boolean 0 1 Out Data Requirements: Type Profile MS Code Filter Patient http://hl7.org/fhir/StructureDefinition/Patient Condition http://hl7.org/fhir/StructureDefinition/Condition ; Observation http://hl7.org/fhir/StructureDefinition/Observation ; Goal http://hl7.org/fhir/StructureDefinition/Goal ; MedicationRequest http://hl7.org/fhir/StructureDefinition/MedicationRequest ;;;;;;;;;; Content: text/cql library OHSUHTNCommon version '0.1'#xD; #xD; using FHIR version '4.0.1'#xD; #xD; include FHIRHelpers version '4.0.1' called FHIRHelpers#xD; #xD; codesystem "LOINC": 'http://loinc.org'#xD; codesystem "SNOMED": 'http://snomed.info/sct'#xD; codesystem "ConditionClinicalStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-clinical'#xD; codesystem "ConditionVerificationStatusCodes": 'http://terminology.hl7.org/CodeSystem/condition-ver-status'#xD; codesystem "v3 Code System ActCode": 'http://terminology.hl7.org/CodeSystem/v3-ActCode'#xD; codesystem "ConditionCategorySystem": 'http://terminology.hl7.org/CodeSystem/condition-category'#xD; codesystem "Medication request status": 'http://hl7.org/fhir/CodeSystem/medicationrequest-status'#xD; #xD; valueset "Systolic Blood Pressure": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1104.2'#xD; valueset "Diastolic Blood Pressure": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.2.1045'#xD; valueset "Blood Pressure Measured": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.600.2012'#xD; valueset "Ambulatory Blood Pressure Monitoring (ABPM)": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1047.511'#xD; valueset "Antihypertensive Medications 1": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1178.10'#xD; valueset "Antihypertensive Medications 2": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1200.242'#xD; #xD; /* Possible exclusions from workflows */#xD; /* valueset "Hypertension": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.3157.4012' */#xD; valueset "Hypertension": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.104.12.1016'#xD; valueset "Non essential Hypertension SNOMEDCT": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1032.10'#xD; valueset "Pregnancy": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378'#xD; valueset "End Stage Renal Disease": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.2.590'#xD; valueset "Hospice care ambulatory": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15'#xD; #xD; code "ambulatory": 'AMB' from "v3 Code System ActCode" display 'ambulatory'#xD; #xD; // Condition Clinical Status Codes - Consider value sets for these#xD; code "active": 'active' from "ConditionClinicalStatusCodes"#xD; code "recurrence": 'recurrence' from "ConditionClinicalStatusCodes"#xD; code "relapse": 'relapse' from "ConditionClinicalStatusCodes"#xD; code "inactive": 'inactive' from "ConditionClinicalStatusCodes"#xD; code "remission": 'remission' from "ConditionClinicalStatusCodes"#xD; code "resolved": 'resolved' from "ConditionClinicalStatusCodes"#xD; #xD; // Condition Verification Status Codes - Consider value sets for these#xD; code "unconfirmed": 'unconfirmed' from ConditionVerificationStatusCodes#xD; code "provisional": 'provisional' from ConditionVerificationStatusCodes#xD; code "differential": 'differential' from ConditionVerificationStatusCodes#xD; code "confirmed": 'confirmed' from ConditionVerificationStatusCodes#xD; code "refuted": 'refuted' from ConditionVerificationStatusCodes#xD; code "entered-in-error": 'entered-in-error' from ConditionVerificationStatusCodes#xD; #xD; code "problem-list-item": 'problem-list-item' from "ConditionCategorySystem" display 'Problem List Item'#xD; code "encounter-diagnosis": 'encounter-diagnosis' from "ConditionCategorySystem" display 'Encounter Diagnosis'#xD; #xD; code "home-measurement": '264362003' from "SNOMED"#xD; #xD; code "Active Medication Request": 'active' from "Medication request status"#xD; #xD; context Patient#xD; #xD; define function "Avg Systolic BP"(list List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; Avg(list O return all O.systolic)#xD; #xD; define function "Avg Diastolic BP"(list List<Tuple {id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; Avg(list O return all O.diastolic)#xD; #xD; define function "Avg BP"(list List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; Tuple { systolic: "Avg Systolic BP"(list), diastolic: "Avg Diastolic BP"(list) }#xD; #xD; define function "Normal BP"(list List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; ("Avg BP"(list)) O#xD; where O.systolic <= 130#xD; and O.diastolic <= 80#xD; #xD; define function "Elevated or Above BP"(list List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; ("Avg BP"(list)) O#xD; where O.systolic > 130#xD; or O.diastolic > 80#xD; #xD; define function "HTN Stage 1 BP"(list List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; ("Avg BP"(list)) O#xD; where (O.systolic > 130 and O.systolic <= 140)#xD; or (O.diastolic > 80 and O.diastolic <= 90)#xD; #xD; define function "HTN Stage 2 BP"(list List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; ("Avg BP"(list)) O#xD; where O.systolic > 140#xD; or O.diastolic > 90#xD; #xD; define function "HTN Stage 2 BP Systolic Second Test"(list List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; ("Avg BP"(list)) O#xD; where O.systolic > 160#xD; #xD; define function "HTN Crisis BP"(O Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }):#xD; O.systolic >= 180 or O.diastolic >= 120#xD; #xD; define function "HTN Low Crisis BP"(O Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }):#xD; O.systolic < 90 or O.diastolic < 60#xD; #xD; /* Helpers */#xD; define function WithUnit(list List<Observation>, Unit String):#xD; list Observations#xD; where (#xD; ((singleton from (Observations.component C where C.code in "Systolic Blood Pressure").value as FHIR.Quantity).unit.value ~ Unit#xD; and (singleton from (Observations.component C where C.code in "Diastolic Blood Pressure").value as FHIR.Quantity).unit.value ~ Unit)#xD; or ((singleton from (Observations.component C where C.code in "Systolic Blood Pressure").value as FHIR.Quantity).code.value ~ Unit#xD; and (singleton from (Observations.component C where C.code in "Diastolic Blood Pressure").value as FHIR.Quantity).code.value ~ Unit)#xD; )#xD; #xD; define function WithUnit(value Quantity, Unit String):#xD; (#xD; value quantity#xD; where (#xD; quantity.code.value ~ Unit#xD; or quantity.unit.value ~ Unit#xD; )#xD; ) is not null#xD; #xD; define function BPReadingType(o FHIR.Observation):#xD; if ("MeasurementSettings"(o.extension) contains "home-measurement") then 'home'#xD; else 'office'#xD; #xD; define function QualifiedEncounter(list List<FHIR.Encounter>):#xD; list Encounter#xD; //planned | arrived | triaged | in-progress | onleave | finished | cancelled +#xD; where Encounter.status ~ 'finished'#xD; #xD; define function QualifiedCondition(list List<FHIR.Condition>):#xD; list Condition#xD; //active | recurrence | relapse | inactive | remission | resolved#xD; //where ActiveCondition(Condition) is not null#xD; //unconfirmed | provisional | differential | confirmed | refuted | entered-in-error#xD; where ConfirmedCondition(Condition) is not null#xD; #xD; define function QualifiedObservation(list List<FHIR.Observation>):#xD; list Observation#xD; where (#xD; //registered | preliminary | final | amended | corrected | cancelled | entered-in-error | unknown#xD; Observation.status ~ 'final'#xD; or Observation.status ~ 'amended'#xD; or Observation.status ~ 'corrected'#xD; )#xD; #xD; define function QualifiedProcedure(list List<FHIR.Procedure>):#xD; list Procedure#xD; //preparation | in-progress | not-done | on-hold | stopped | completed | entered-in-error | unknown#xD; where Procedure.status ~ 'completed'#xD; #xD; define function ConfirmedCondition(list List<Condition>):#xD; list Condition#xD; where Condition.verificationStatus is null#xD; or FHIRHelpers.ToConcept(Condition.verificationStatus) ~ "confirmed"#xD; #xD; define function ConfirmedCondition(value Condition):#xD; value Condition#xD; where Condition.verificationStatus is null#xD; or FHIRHelpers.ToConcept(Condition.verificationStatus) ~ "confirmed"#xD; #xD; define function ActiveCondition(list List<Condition>):#xD; list Condition#xD; where (#xD; Condition.clinicalStatus is null#xD; or FHIRHelpers.ToConcept(Condition.clinicalStatus) ~ "active"#xD; )#xD; and Condition.abatement is null#xD; #xD; define function ActiveCondition(value Condition):#xD; value Condition#xD; where (#xD; Condition.clinicalStatus is null#xD; or FHIRHelpers.ToConcept(Condition.clinicalStatus) ~ "active"#xD; )#xD; and Condition.abatement is null#xD; #xD; define function ActiveOrRecurring(list List<Condition>):#xD; list Condition#xD; where ActiveCondition(Condition) is not null#xD; // (#xD; // FHIRHelpers.ToConcept(Condition.clinicalStatus) ~ "active"#xD; // and Condition.abatement is null#xD; // )#xD; or FHIRHelpers.ToConcept(Condition.clinicalStatus) ~ "relapse"#xD; #xD; // Epic sometimes provides the oid instead of the named system, but we need the named system for comparing to ValueSets#xD; define function FixEpicSystemMapping(system String):#xD; if Matches(system, 'urn:oid:2.16.840.1.113883.6.96') then 'http://snomed.info/sct'#xD; else if Matches(system, 'urn:oid:2.16.840.1.113883.6.90') then 'http://hl7.org/fhir/sid/icd-10-cm'#xD; else system#xD; #xD; // Take the list of Codes and replace the systems when needed#xD; define function TranformCodeList(list List<System.Code>):#xD; list coding#xD; return System.Code {#xD; code: coding.code,#xD; system: FixEpicSystemMapping(coding.system),#xD; version: coding.version,#xD; display: coding.display#xD; }#xD; #xD; // Transform the codes in the CodeableConcept, replacing the systems when needed#xD; define function TransformCodeableConcept(cc FHIR.CodeableConcept):#xD; TranformCodeList(FHIRHelpers.ToConcept(cc).codes)#xD; #xD; define function QualifiedGoal(list List<Goal>):#xD; list Goal#xD; //proposed | planned | accepted | active | on-hold | completed | cancelled | entered-in-error | rejected#xD; where (#xD; Goal.lifecycleStatus ~ 'accepted'#xD; or Goal.lifecycleStatus ~ 'active'#xD; )#xD; #xD; #xD; define function "GetId"(uri String):#xD; Last(Split(uri, '/'))#xD; #xD; /*#xD; @description: Normalizes a value that is a choice of timing-valued types to an equivalent interval#xD; @comment: Normalizes a choice type of FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instance, FHIR.string, FHIR.Age, or FHIR.Range types#xD; to an equivalent interval. This selection of choice types is a superset of the majority of choice types that are used as possible#xD; representations for timing-valued elements in FHIR, allowing this function to be used across any resource. NOTE: Due to the#xD; complexity of determining a single interval from a Timing or String type, this function will throw a run-time exception if it is used#xD; with a Timing or String.#xD; */#xD; define function "Normalize Interval"(choice Choice<FHIR.dateTime, FHIR.Period, FHIR.Timing, FHIR.instant, FHIR.string, FHIR.Age, FHIR.Range>):#xD; case#xD; when choice is FHIR.dateTime then#xD; Interval[FHIRHelpers.ToDateTime(choice as FHIR.dateTime), FHIRHelpers.ToDateTime(choice as FHIR.dateTime)]#xD; when choice is FHIR.Period then#xD; FHIRHelpers.ToInterval(choice as FHIR.Period)#xD; when choice is FHIR.instant then#xD; Interval[FHIRHelpers.ToDateTime(choice as FHIR.instant), FHIRHelpers.ToDateTime(choice as FHIR.instant)]#xD; when choice is FHIR.Age then#xD; Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age),#xD; FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(choice as FHIR.Age) + 1 year)#xD; when choice is FHIR.Range then#xD; Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).low),#xD; FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((choice as FHIR.Range).high) + 1 year)#xD; when choice is FHIR.Timing then#xD; Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute a single interval from a Timing type')#xD; when choice is FHIR.string then#xD; Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')#xD; else#xD; null as Interval<DateTime>#xD; end#xD; #xD; define function "Check Goal Start"(choice Choice<FHIR.date,FHIR.CodeableConcept>):#xD; case#xD; when choice is FHIR.date then#xD; FHIRHelpers.ToDate(choice)#xD; else#xD; null as System.Date#xD; end#xD; #xD; /*#xD; @description: Returns an interval representing the normalized Abatement of a given Condition resource.#xD; @comment: NOTE: Due to the complexity of determining an interval from a String, this function will throw#xD; a run-time exception if used with a Condition instance that has a String as the abatement value.#xD; */#xD; define function "Normalize Abatement"(condition Condition):#xD; if condition.abatement is FHIR.dateTime then#xD; Interval[FHIRHelpers.ToDateTime(condition.abatement as FHIR.dateTime), FHIRHelpers.ToDateTime(condition.abatement as FHIR.dateTime)]#xD; else if condition.abatement is FHIR.Period then#xD; FHIRHelpers.ToInterval(condition.abatement as FHIR.Period)#xD; else if condition.abatement is FHIR.string then#xD; Message(null as Interval<DateTime>, true, '1', 'Error', 'Cannot compute an interval from a String value')#xD; else if condition.abatement is FHIR.Age then#xD; Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(condition.abatement as FHIR.Age),#xD; FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity(condition.abatement as FHIR.Age) + 1 year)#xD; else if condition.abatement is FHIR.Range then#xD; Interval[FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((condition.abatement as FHIR.Range).low),#xD; FHIRHelpers.ToDate(Patient.birthDate) + FHIRHelpers.ToQuantity((condition.abatement as FHIR.Range).high) + 1 year)#xD; else if condition.abatement is FHIR.boolean then#xD; Interval[end of "Normalize Interval"(condition.onset), condition.recordedDate)#xD; else null#xD; #xD; /*#xD; @description: returns TRUE if the prevalence period is specified and encompasses today. considers clinical status#xD; as a surrogate in the absence of prevalence period components (onset, abatement).#xD; */#xD; define function "Is Valid Prevalence Period"(condition Condition):#xD; if condition.onset is not null and condition.abatement is not null then#xD; start of "Normalize Interval"(condition.onset) <= Today() + 1 day and end of "Normalize Abatement"(condition) >= Today() + 1 day#xD; else if condition.clinicalStatus is null#xD; or condition.clinicalStatus ~ "active"#xD; or condition.clinicalStatus ~ "recurrence"#xD; or condition.clinicalStatus ~ "relapse" then#xD; if condition.onset is not null then#xD; start of "Normalize Interval"(condition.onset) <= Today() + 1 day#xD; else if condition.abatement is not null then#xD; end of "Normalize Abatement"(condition) >= Today() + 1 day#xD; else#xD; true#xD; else#xD; false#xD; #xD; /*#xD; A pregnancy is considered active if one of the following are true:#xD; - An onset and abatement exist, and today is after the onset and before the abatement#xD; - Clinical status is active or null and#xD; -- Only the onset exists and it's at least 42 weeks in the past#xD; -- Only the abatement exists and it's in the future#xD; -- Only the recorded date exists and it's at least 42 weeks in the past#xD; -- No dates exist to provide additional context#xD; */#xD; define function "Is Active Pregnancy"(condition Condition):#xD; if condition.onset is not null and condition.abatement is not null then#xD; start of "Normalize Interval"(condition.onset) <= Today() + 1 day and end of "Normalize Abatement"(condition) >= Today() + 1 day#xD; else if condition.clinicalStatus is null or condition.clinicalStatus ~ "active" then#xD; if condition.onset is not null then#xD; start of "Normalize Interval"(condition.onset) > Today() - 42 week#xD; else if condition.abatement is not null then#xD; end of "Normalize Abatement"(condition) >= Today() + 1 day#xD; else if condition.recordedDate is not null then#xD; condition.recordedDate > Today() - 42 week#xD; else#xD; true#xD; else#xD; false#xD; #xD; #xD; #xD; /*#xD; @description: returns true if the procedure was known to be performed in the previous 2 years. Note that 'performed' seems to#xD; be going away in FHIR 5 and another way to get at a date could be through a encounter reference, so this is not very robust.#xD; This is only used to decide whether to show someone counseling again if the record is stale, and we will err on the side of#xD; showing if we don't know.#xD; */#xD; define function "Procedure Occurred In Last 2 Years"(procedure Procedure):#xD; if procedure is null then false#xD; else if procedure.performed is not null then "Normalize Interval"(procedure.performed) ends 24 months or less before Now()#xD; else false#xD; #xD; /*#xD; @description: Returns an interval representing the normalized prevalence period of a given Condition resource.#xD; DEPRECATED - storer 2022-07-06 - use "Is Valid Prevalence Period" function above instead#xD; */#xD; define function "Prevalence Period"(condition Condition):#xD; if condition.clinicalStatus ~ "active"#xD; or condition.clinicalStatus ~ "recurrence"#xD; or condition.clinicalStatus ~ "relapse"#xD; // This prevents errors when a condition has an onset and abatement on the same day and no timestamp is provided (Epic)#xD; or end of "Normalize Abatement"(condition) is not null then#xD; Interval[start of "Normalize Interval"(condition.onset), end of "Normalize Abatement"(condition)]#xD; else#xD; // The condition is not active but has no abatement date, so we can't say when it ended#xD; Interval[start of "Normalize Interval"(condition.onset), null)#xD; #xD; /* Extract the Category code from the Condition */#xD; define function "Condition Category"(condition FHIR.Condition):#xD; FHIRHelpers.ToCode(singleton from (condition.category.coding c where c.system = 'http://terminology.hl7.org/CodeSystem/condition-category'))#xD; #xD; /***** Common Data *****/#xD; #xD; /* Potential Exclusion Criteria */#xD; define "Patient Under Age 18":#xD; AgeInYearsAt(Today()) < 18#xD; #xD; define "Patient Over Age 100":#xD; AgeInYearsAt(Today()) >= 100#xD; #xD; // This query will not work in Epic so we rely on at least one Condition to be present in the prefetch#xD; define "Conditions":#xD; QualifiedCondition(["Condition"])#xD; #xD; define "Problem Conditions":#xD; "Conditions" C where "Condition Category"(C) ~ "problem-list-item"#xD; #xD; define "Encounter Conditions":#xD; "Conditions" C where "Condition Category"(C) ~ "encounter-diagnosis"#xD; #xD; define "Condition Indicating End Stage Renal Disease":#xD; "Conditions" Condition#xD; where TransformCodeableConcept(Condition.code) in "End Stage Renal Disease" and "Is Valid Prevalence Period"(Condition)#xD; #xD; define "Condition Indicating Pregnancy":#xD; "Conditions" Condition#xD; where TransformCodeableConcept(Condition.code) in "Pregnancy" and "Is Active Pregnancy"(Condition)#xD; #xD; define "Condition Indicating Preexisting Hypertension":#xD; "Problem Condition Indicating Preexisting Hypertension" union#xD; "Encounter Condition Indicating Preexisting Hypertension"#xD; #xD; define "Problem Condition Indicating Preexisting Hypertension":#xD; "Problem Conditions" Condition#xD; where (TransformCodeableConcept(Condition.code) in "Hypertension" or TransformCodeableConcept(Condition.code) in#xD; "Non essential Hypertension SNOMEDCT") and "Is Valid Prevalence Period"(Condition)#xD; #xD; define "Encounter Condition Indicating Preexisting Hypertension":#xD; "Encounter Conditions" Condition#xD; where (TransformCodeableConcept(Condition.code) in "Hypertension" or TransformCodeableConcept(Condition.code) in#xD; "Non essential Hypertension SNOMEDCT")#xD; #xD; // We do not limit Observations by code to prevent CQF Ruler from looking beyond what is provided in the prefetch#xD; define "All Observations":#xD; ["Observation"]#xD; #xD; // "Normal" observations with systolic/diastolic components#xD; define "Component BP Observations":#xD; (WithUnit(QualifiedObservation("All Observations" A where TransformCodeableConcept(A.code) in "Blood Pressure Measured"), 'mm[Hg]')) O#xD; return Tuple {#xD; id: O.id.value,#xD; effective: Coalesce(start of "Normalize Interval"(O.effective), O.issued),#xD; systolic: FHIRHelpers.ToQuantity(singleton from (O.component C where C.code in "Systolic Blood Pressure").value).value,#xD; diastolic: FHIRHelpers.ToQuantity(singleton from (O.component C where C.code in "Diastolic Blood Pressure").value).value,#xD; readingType: BPReadingType(O)#xD; }#xD; #xD; define function ConvertSystolic(systolic List<FHIR.Observation>):#xD; systolic O#xD; return Tuple {#xD; id: O.id.value,#xD; effective: Coalesce(start of "Normalize Interval"(O.effective), O.issued),#xD; systolic: FHIRHelpers.ToQuantity(O.value).value,#xD; diastolic: null as Decimal,#xD; readingType: "BPReadingType"(O)#xD; } sort by effective#xD; #xD; define function ConvertDiastolic(diastolic List<FHIR.Observation>):#xD; diastolic O#xD; return Tuple {#xD; id: O.id.value,#xD; effective: Coalesce(start of "Normalize Interval"(O.effective), O.issued),#xD; systolic: null as Decimal,#xD; diastolic: FHIRHelpers.ToQuantity(O.value).value,#xD; readingType: "BPReadingType"(O)#xD; }#xD; #xD; /* For each systolic observation, gather a list of diastolic values that match by date */#xD; define function GatherMatches(s List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>,#xD; d List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; s S#xD; return Tuple {#xD; systolicObservation : S,#xD; diastolicValues : (d D where S.effective = D.effective and S.readingType = D.readingType).diastolic#xD; }#xD; #xD; /* For each systolic observation with exactly one disatolic match, return the paired observation */#xD; define function PairMatchedBPObservations(list List<Tuple { systolicObservation Tuple {id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String },#xD; diastolicValues List<Decimal>} > ):#xD; (list B where Count(B.diastolicValues) = 1) matched return {#xD; // Only keeping the systolic id on the match - this is just for distinguishing unique readings at the same time which should be an edge case#xD; id: matched.systolicObservation.id,#xD; effective: matched.systolicObservation.effective,#xD; systolic: matched.systolicObservation.systolic,#xD; diastolic: matched.diastolicValues[0],#xD; readingType: matched.systolicObservation.readingType#xD; }#xD; #xD; define function PairBPObservations(s List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>,#xD; d List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; PairMatchedBPObservations(GatherMatches(s, d))#xD; #xD; define "Paired BP Observations":#xD; PairBPObservations(ConvertSystolic(QualifiedObservation("All Observations" A where TransformCodeableConcept(A.code) in "Systolic Blood Pressure")), ConvertDiastolic(QualifiedObservation("All Observations" A where TransformCodeableConcept(A.code) in "Diastolic Blood Pressure")))#xD; #xD; define "All BP Observations":#xD; "Component BP Observations" union "Paired BP Observations"#xD; #xD; define "Most Recent BP Reading":#xD; Last("All BP Observations" O sort by effective)#xD; #xD; define "HTN High Crisis":#xD; "Most Recent BP Reading" is not null and "HTN Crisis BP"("Most Recent BP Reading")#xD; #xD; define "HTN Low Crisis":#xD; "Most Recent BP Reading" is not null and "HTN Low Crisis BP"("Most Recent BP Reading")#xD; #xD; define "HTN Crisis":#xD; "HTN High Crisis" or "HTN Low Crisis"#xD; #xD; define "Blood Pressure Observations for Last 2 Years":#xD; "All BP Observations" BP where BP.effective + 24 months >= Today()#xD; sort by effective#xD; #xD; define "Blood Pressure Observations for Last 2 Years Descending":#xD; "Blood Pressure Observations for Last 2 Years" O sort by effective desc#xD; #xD; define function MeasurementSettings(list List<Extension>):#xD; list Extension#xD; where Extension.url = 'http://hl7.org/fhir/us/vitals/StructureDefinition/MeasurementSettingExt'#xD; return FHIRHelpers.ToCode(Extension.value)#xD; #xD; define "Home Blood Pressure Observations":#xD; "Blood Pressure Observations for Last 2 Years" O#xD; where O.readingType = 'home'#xD; #xD; define "Office Blood Pressure Observations":#xD; "Blood Pressure Observations for Last 2 Years" except "Home Blood Pressure Observations"#xD; #xD; // Revisit logic when ready to address ambulatory observations. Check out commit 57046448a24d24a767d402e5fa8a96779a488f02 for old logic#xD; define "Ambulatory Blood Pressure Monitoring Observations":#xD; {}#xD; #xD; define "Has BP Set":#xD; if not exists "Blood Pressure Observations for Last 2 Years" then false#xD; else calculateScore("Blood Pressure Observations for Last 2 Years") >= 4#xD; #xD; define "Has 12 Home BPs":#xD; if not exists "Home Blood Pressure Observations" then false#xD; else Count("Home Blood Pressure Observations") >= 12#xD; #xD; define function score(observation Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }):#xD; if observation in "Ambulatory Blood Pressure Monitoring Observations" then 0.334#xD; else if observation in "Home Blood Pressure Observations" then 0.334#xD; else 1#xD; #xD; define function calculateScore(list List<Tuple { id String, effective DateTime, systolic Decimal, diastolic Decimal, readingType String }>):#xD; Sum(list O#xD; return all score(O)#xD; )#xD; #xD; define "Most Recent BP Set":#xD; if not "Has BP Set" then null#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 4)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 4)#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 5)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 5)#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 6)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 6)#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 7)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 7)#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 8)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 8)#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 9)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 9)#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 10)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 10)#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 11)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 11)#xD; else if calculateScore(Take("Blood Pressure Observations for Last 2 Years Descending", 12)) >= 4 then Take("Blood Pressure Observations for Last 2 Years Descending", 12)#xD; else null#xD; #xD; define "Average All BP Last 2 Years":#xD; "Avg BP"("Blood Pressure Observations for Last 2 Years")#xD; #xD; define "Average Most Recent BP Set":#xD; "Avg BP"("Most Recent BP Set")#xD; #xD; // Is last BP set or all BPs avge > 130/80?#xD; define "Patient Has Potential HTN Stage 1 BP":#xD; (#xD; "HTN Stage 1 BP"("Most Recent BP Set") is not null#xD; or "HTN Stage 1 BP"("Blood Pressure Observations for Last 2 Years") is not null#xD; )#xD; #xD; // Is last BP set or all BPs average > 140 SBP or > 90 DBP?#xD; define "Patient Has Potential HTN Stage 2 BP":#xD; (#xD; "HTN Stage 2 BP"("Most Recent BP Set") is not null#xD; or "HTN Stage 2 BP"("Blood Pressure Observations for Last 2 Years") is not null#xD; )#xD; #xD; // Does patient have a BP goal?#xD; define "Qualifying Blood Pressure Goals":#xD; (QualifiedGoal(["Goal"])) BPGoal#xD; where (#xD; singleton from (BPGoal.target Systolic#xD; where Systolic.measure in "Systolic Blood Pressure"#xD; and Systolic.detail is Quantity#xD; and WithUnit(Systolic.detail, 'mm[Hg]'))#xD; ) SystolicTarget is not null#xD; and (#xD; singleton from (BPGoal.target Diastolic#xD; where Diastolic.measure in "Diastolic Blood Pressure"#xD; and Diastolic.detail is Quantity#xD; and WithUnit(Diastolic.detail, 'mm[Hg]'))#xD; ) DiastolicTarget is not null#xD; #xD; define "Most Recently Established Blood Pressure Goal":#xD; Last (#xD; "Qualifying Blood Pressure Goals" G#xD; sort by (Coalesce("Check Goal Start"(start), FHIRHelpers.ToDate(statusDate)))#xD; )#xD; #xD; define "Patient has a BP Goal":#xD; exists "Qualifying Blood Pressure Goals"#xD; #xD; #xD; define "BP from Most Recent Goal":#xD; "Most Recently Established Blood Pressure Goal" BPGoal#xD; return Tuple {#xD; systolic: (singleton from (BPGoal.target Systolic where Systolic.measure in "Systolic Blood Pressure").detail as Quantity).value,#xD; diastolic: (singleton from (BPGoal.target Diastolic where Diastolic.measure in "Diastolic Blood Pressure").detail as Quantity).value#xD; }#xD; #xD; define "Above Goal Average Most Recent":#xD; "Average Most Recent BP Set".systolic > "BP from Most Recent Goal".systolic or#xD; "Average Most Recent BP Set".diastolic > "BP from Most Recent Goal".diastolic#xD; #xD; define "Active Medication Requests":#xD; ["MedicationRequest"] Rx where Rx.status.value ~ "Active Medication Request".code#xD; #xD; // Medication may be a Reference or a CodeableConcept. Return it as a Medication - only the code matters for processing#xD; define function getMedication(Rx MedicationRequest):#xD; if Rx.medication is FHIR.Reference then#xD; singleton from ([Medication: id in Last(Split(Rx.medication.reference, '/'))])#xD; else#xD; Medication {code: Rx.medication}#xD; #xD; define "Medication Requests With Medication":#xD; "Active Medication Requests" Rx#xD; let medication: getMedication(Rx)#xD; return#xD; MedicationRequest {#xD; id: Rx.id,#xD; status: Rx.status,#xD; intent: Rx.intent,#xD; category: Rx.category,#xD; medication: medication.code,#xD; subject: Rx.subject,#xD; authoredOn: Rx.authoredOn,#xD; recorder: Rx.recorder,#xD; dosageInstruction: Rx.dosageInstruction,#xD; dispenseRequest: Rx.dispenseRequest#xD; }#xD; #xD; define "Patient is Using Antihypertensive Medications":#xD; "Medication Requests With Medication".medication in "Antihypertensive Medications 1" or#xD; "Medication Requests With Medication".medication in "Antihypertensive Medications 2"#xD; Content: application/elm+xml Encoded data (865736 characters) Content: application/elm+json Encoded data (1628632 characters)
Common logic spanning multiple interventions.
Published by OHSU under an Apache 2.0 License
Code system ConditionClinicalStatusCodes
Code system ConditionVerificationStatusCodes
Value set Ambulatory Blood Pressure Monitoring (ABPM)
Value set Antihypertensive Medications 1
Value set Antihypertensive Medications 2
Value set Non essential Hypertension SNOMEDCT
Condition Indicating End Stage Renal Disease
Problem Condition Indicating Preexisting Hypertension
Encounter Condition Indicating Preexisting Hypertension
Condition Indicating Preexisting Hypertension
Blood Pressure Observations for Last 2 Years
Blood Pressure Observations for Last 2 Years Descending
Ambulatory Blood Pressure Monitoring Observations
Most Recently Established Blood Pressure Goal
Patient is Using Antihypertensive Medications