Id: library-OpioidCDSCommon Type: logic-library Version: 2022.1.0 Status: active Related: type: depends-on Resource: http://fhir.org/guides/cdc/opioid-cds/Library/FHIRHelpers type: depends-on Resource: http://fhir.org/guides/cdc/opioid-cds/Library/OMTKLogicMK2020 Data Requirements: type: MedicationRequest type: Medication code filter: path: id type: MedicationRequest type: Medication code filter: path: id type: MedicationRequest type: Medication code filter: path: id type: Medication code filter: path: id type: Medication code filter: path: id type: Condition code filter: path: category valueset: http://hl7.org/fhir/us/core/ValueSet/us-core-condition-category type: Condition code filter: path: category valueset: http://hl7.org/fhir/us/core/ValueSet/us-core-condition-category type: Procedure code filter: path: code valueset: http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-procedure type: ServiceRequest code filter: path: code valueset: http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-procedure type: Observation code filter: path: code valueset: http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-finding type: Encounter type: MedicationRequest code filter: path: medication valueset: http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-analgesics-with-ambulatory-misuse-potential Content: type: text/cql library OpioidCDSCommon version '2022.1.0' using FHIR version '4.0.1' include FHIRHelpers version '4.0.1' called FHIRHelpers include OMTKLogicMK2020 version '2022.1.0' called OMTKLogic codesystem "LOINC": 'http://loinc.org' codesystem "SNOMED": 'http://snomed.info/sct' codesystem "Medication Request Category Codes": 'http://terminology.hl7.org/CodeSystem/medicationrequest-category' codesystem "Medication Request Status Codes": 'http://hl7.org/fhir/medication-request-status' codesystem "Condition Clinical Status Codes": 'http://terminology.hl7.org/CodeSystem/condition-clinical' valueset "US Core Condition Category Codes": 'http://hl7.org/fhir/us/core/ValueSet/us-core-condition-category' // Expression-based valueset "Opioid analgesics with ambulatory misuse potential": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-analgesics-with-ambulatory-misuse-potential' valueset "Extended release opioid with ambulatory misuse potential": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/extended-release-opioid-with-ambulatory-misuse-potential' valueset "Buprenorphine and methadone medications": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/buprenorphine-and-methadone-medications' // Enumerated-compose valueset "Limited life expectancy conditions": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/limited-life-expectancy-conditions' valueset "Therapies indicating end of life care": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/therapies-indicating-end-of-life-care' valueset "Conditions likely terminal for opioid prescribing": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/conditions-likely-terminal-for-opioid-prescribing' valueset "CDC malignant cancer conditions": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/cdc-malignant-cancer-conditions' valueset "Oncology specialty designations (NUCC)": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/oncology-specialty-designations' valueset "Opioid misuse disorders": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-misuse-disorders' valueset "Substance misuse behavioral counseling": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/substance-misuse-behavioral-counseling' valueset "Conditions documenting substance misuse": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/conditions-documenting-substance-misuse' // Harvested from VSAC - OID: 2.16.842022.1.013883.3.464.1003.101.12.1001 // Reviewed with Terminology, 2020-02-05 - Value set name in VSAC is "Office Visit", need to verify suitability valueset "Office Visit": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/office-visit' valueset "Opioid counseling procedure": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-counseling-procedure' /* Existing sets for first six recs */ valueset "Benzodiazepine medications": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/benzodiazepine-medications' valueset "Non-opioid drug urine screening": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/non-opioid-drug-urine-screening' valueset "Naloxone medications": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/naloxone-medications' valueset "Opioid misuse assessment procedure": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-misuse-assessment-procedure' valueset "Opioid drug urine screening": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-drug-urine-screening' valueset "Hospice Disposition": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-disposition' // Harvested from VSAC - OID: 2.16.842022.1.013762.1.4.1108.15 valueset "Hospice Finding Codes": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-finding' valueset "Hospice Procedure Codes": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-procedure' valueset "Pain treatment plan": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/pain-treatment-plan' valueset "Pain management procedure": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/pain-management-procedure' valueset "PDMP review procedure": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/pdmp-review-procedure' valueset "PDMP data reviewed finding": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/pdmp-data-reviewed-finding' code "Community": 'community' from "Medication Request Category Codes" code "Active Condition": 'active' from "Condition Clinical Status Codes" code "Active MedicationRequest": 'active' from "Medication Request Status Codes" // TODO: code "Nonpharmacologic therapy and nonopioid pharmocologic": 'TODO' from "TODO" context Patient // TODO: Capture process decisions for long-term opioid use define IsForChronicPain: true define "Active Ambulatory Opioid Rx": /* ("Get Active Ambulatory Medication Requests"([MedicationRequest: status in "Active MedicationRequest"])) Rx */ ( ("Get Active Ambulatory Medication Requests"([MedicationRequest])) MR where date from MR.authoredOn 2 years or less on or before Today() and MR.medication is Reference or MR.medication in "Opioid analgesics with ambulatory misuse potential" ) Rx let Med: [Medication: id in (Last(Split((Rx.medication as FHIR.Reference).reference, '/')))] where not(Rx.medication is Reference) or Med.code in "Opioid analgesics with ambulatory misuse potential" return MedicationRequest { id: Rx.id, status: Rx.status, intent: Rx.intent, category: Rx.category, medication: if Rx.medication is Reference then First(Med.code) else Rx.medication as CodeableConcept, subject: Rx.subject, recorder: Rx.recorder, dosageInstruction: Rx.dosageInstruction, dispenseRequest: Rx.dispenseRequest } define "Active Ambulatory Benzodiazepine Rx": /* ("Get Active Ambulatory Medication Requests"([MedicationRequest: status in "Active MedicationRequest"])) Rx */ ( ("Get Active Ambulatory Medication Requests"([MedicationRequest])) MR where date from MR.authoredOn 2 years or less on or before Today() and MR.medication is Reference or MR.medication in "Benzodiazepine medications" ) Rx let Med: [Medication: id in (Last(Split((Rx.medication as FHIR.Reference).reference, '/')))] where not(Rx.medication is Reference) or Med.code in "Benzodiazepine medications" return MedicationRequest { id: Rx.id, status: Rx.status, intent: Rx.intent, category: Rx.category, medication: if Rx.medication is Reference then First(Med.code) else Rx.medication as CodeableConcept, subject: Rx.subject, recorder: Rx.recorder, dosageInstruction: Rx.dosageInstruction, dispenseRequest: Rx.dispenseRequest } define "Active Ambulatory Naloxone Rx": /* ("Get Active Ambulatory Medication Requests"([MedicationRequest: status in "Active MedicationRequest"])) Rx */ ( ("Get Active Ambulatory Medication Requests"([MedicationRequest])) MR where date from MR.authoredOn 2 years or less on or before Today() and MR.medication is Reference or MR.medication in "Naloxone medications" ) Rx let Med: [Medication: id in (Last(Split((Rx.medication as FHIR.Reference).reference, '/')))] where not(Rx.medication is Reference) or Med.code in "Naloxone medications" return MedicationRequest { id: Rx.id, status: Rx.status, intent: Rx.intent, category: Rx.category, medication: if Rx.medication is Reference then First(Med.code) else Rx.medication as CodeableConcept, subject: Rx.subject, recorder: Rx.recorder, dosageInstruction: Rx.dosageInstruction, dispenseRequest: Rx.dispenseRequest } define function "Get Active Ambulatory Medication Requests" (value List<MedicationRequest>) returns List<MedicationRequest>: value Rx where Rx.status.value = 'active' and exists ( Rx.category RxCategory where FHIRHelpers.ToConcept(RxCategory) ~ "Community" ) define function "Is Ambulatory Medication Request?" (value List<MedicationRequest>) returns List<MedicationRequest>: value Rx where exists ( Rx.category RxCategory where FHIRHelpers.ToConcept(RxCategory) ~ "Community" ) define function "Is Opioid Analgesic with Ambulatory Misuse Potential?"(value List<MedicationRequest>): value Rx let MedRef: if (Rx.medication is FHIR.Reference) then (Rx.medication as FHIR.Reference).reference else null, Med: if MedRef is null then (Rx.medication as FHIR.CodeableConcept) else SingletonFrom([Medication: id in (Last(Split(MedRef, '/')))] Med return Med.code) where Med in "Opioid analgesics with ambulatory misuse potential" and exists ( Rx.category RxCategory where FHIRHelpers.ToConcept(RxCategory) ~ "Community" ) return MedicationRequest { id: Rx.id, status: Rx.status, intent: Rx.intent, category: Rx.category, medication: FHIR.CodeableConcept{ coding: Med.coding }, subject: Rx.subject, authoredOn: Rx.authoredOn, recorder: Rx.recorder, dosageInstruction: Rx.dosageInstruction, dispenseRequest: Rx.dispenseRequest } define function "Is Benzodiazepine?"(value List<MedicationRequest>): value Rx let MedRef: if (Rx.medication is FHIR.Reference) then (Rx.medication as FHIR.Reference).reference else null, Med: if MedRef is null then (Rx.medication as FHIR.CodeableConcept) else SingletonFrom([Medication: id in (Last(Split(MedRef, '/')))] Med return Med.code) where Med in "Benzodiazepine medications" and exists ( Rx.category RxCategory where FHIRHelpers.ToConcept(RxCategory) ~ "Community" ) return MedicationRequest { id: Rx.id, status: Rx.status, intent: Rx.intent, category: Rx.category, medication: FHIR.CodeableConcept{ coding: Med.coding }, subject: Rx.subject, authoredOn: Rx.authoredOn, recorder: Rx.recorder, dosageInstruction: Rx.dosageInstruction, dispenseRequest: Rx.dispenseRequest } define "End of Life Assessment": // 1. Conditions indicating end of life or with limited life expectancy exists ( "Conditions Indicating End of Life or With Limited Life Expectancy" ) // 2. Admitted/referred/discharged to hospice care or exists ( "Admitted/Referred/Discharged to Hospice Care" ) // 3. Medications indicating end of life /* or exists ( "Medications Indicating End of Life" ) */ define "Conditions Indicating End of Life or With Limited Life Expectancy": ( [Condition: category in "US Core Condition Category Codes"] C where C.code in "Conditions likely terminal for opioid prescribing" and exists ( C.clinicalStatus.coding Coding where FHIRHelpers.ToCode(Coding) ~ "Active Condition" ) ) union ( [Condition: category in "US Core Condition Category Codes"] C where C.code in "Limited life expectancy conditions" and exists ( C.clinicalStatus.coding Coding where FHIRHelpers.ToCode(Coding) ~ "Active Condition" ) ) define "Admitted/Referred/Discharged to Hospice Care": ( [Procedure: code in "Hospice Procedure Codes"] P where P.status.value in { 'in-progress', 'completed' } ) union ( [ServiceRequest: code in "Hospice Procedure Codes"] E where E.status.value in { 'planned', 'arrived', 'in-progress', 'finished', 'onleave' } ) union ( [Observation: code in "Hospice Finding Codes"] O where not (O.status.value in { 'unknown', 'entered-in-error', 'cancelled' }) ) union ( [Encounter] E where ( if E.hospitalization.dischargeDisposition.coding is null or not exists (E.hospitalization.dischargeDisposition.coding) then false else E.hospitalization.dischargeDisposition in "Hospice Disposition" ) and E.status.value in { 'planned', 'arrived', 'in-progress', 'finished', 'onleave' } ) /* define "Medications Indicating End of Life": ( [MedicationAdministration: "End Of Life Opioids"] MA where MA.status.value in { 'in-progress', 'on-hold', 'completed' } ) union ( [MedicationDispense: "End Of Life Opioids"] MD where MD.status.value in { 'preparation', 'in-progress', 'on-hold', 'completed' } ) union ( [MedicationRequest: "End Of Life Opioids"] MR where MR.status.value in { 'active', 'completed' } ) union ( [MedicationStatement: "End Of Life Opioids"] MS where MS.status.value in { 'active', 'completed', 'intended' } ) */ define "Previous 10 Days Interval": Interval[Today() - 10 days, Today()] define "First Month": Interval[Today() - 3 months, Today() - 2 months] define "Second Month": Interval[Today() - 2 months, Today() - 1 months] define "Third Month": Interval[Today() - 1 months, Today()] define "Prescribed Opioids for 21 or more of 30 Days for each of the past 3 Months": "Days on Opioids during Period"("First Month") >= 21 and "Days on Opioids during Period"("Second Month") >= 21 and "Days on Opioids during Period"("Third Month") >= 21 define function "Prescription Relevant Period"(prescription FHIR.MedicationRequest): if ( prescription.authoredOn is not null and prescription.dispenseRequest is not null and prescription.dispenseRequest.expectedSupplyDuration is not null ) then Interval[ date from prescription.authoredOn, date from prescription.authoredOn + System.Quantity{ value: GetDurationInDays(prescription.dispenseRequest.expectedSupplyDuration), unit: 'days' } ] else null define function "Days on Opioids during Period"(period Interval<Date>): Sum( ( collapse ( [MedicationRequest: "Opioid analgesics with ambulatory misuse potential"] OpioidPrescription return "Prescription Relevant Period"( OpioidPrescription ) intersect period ) ) OpioidUseInterval return days between start of OpioidUseInterval and end of OpioidUseInterval ) define function Prescriptions(Orders List<MedicationRequest>): Orders O let // NOTE: Assuming medication is specified as a CodeableConcept with a single RxNorm code rxNormCode: FHIRHelpers.ToCode((O.medication as FHIR.CodeableConcept).coding[0]), medicationName: OMTKLogic.GetMedicationName(rxNormCode), // NOTE: Assuming a single dosage instruction element dosageInstruction: O.dosageInstruction[0], // NOTE: Assuming a single dose and rate element doseAndRate: dosageInstruction.doseAndRate[0], repeat: dosageInstruction.timing.repeat, frequency: Coalesce(repeat.frequencyMax.value, repeat.frequency.value), period: System.Quantity { value: repeat.period.value, unit: repeat.periodUnit.value }, doseDescription: Coalesce( // There should be a conversion from FHIR.SimpleQuantity to System.Quantity if doseAndRate.dose is FHIR.Range then ToString((doseAndRate.dose as FHIR.Range).low) + '-' + ToString((doseAndRate.dose as FHIR.Range).high) + (doseAndRate.dose as FHIR.Range).high.unit.value else ToString(FHIRHelpers.ToQuantity(doseAndRate.dose)), '' ), frequencyDescription: ToString(dosageInstruction.timing.repeat.frequency.value) + Coalesce( '-' + ToString(dosageInstruction.timing.repeat.frequencyMax.value), '' ) return { rxNormCode: rxNormCode, isDraft: O.status.value = 'draft', // NOTE: Assuming asNeeded is expressed as a boolean isPRN: dosageInstruction.asNeeded, prescription: if dosageInstruction.text is not null then medicationName + ' ' + dosageInstruction.text.value else // TODO: Shouldn't need the .value here on asNeededBoolean medicationName + ' ' + doseDescription + ' q' + frequencyDescription + (if dosageInstruction.asNeeded then ' PRN' else ''), dose: if doseAndRate.dose is FHIR.Range then (doseAndRate.dose as FHIR.Range).high else FHIRHelpers.ToQuantity(doseAndRate.dose), dosesPerDay: Coalesce(OMTKLogic.ToDaily(frequency, period), 1.0) } define function MME(prescriptions List<MedicationRequest>): (Prescriptions(prescriptions)) P let mme: SingletonFrom(OMTKLogic.CalculateMMEs({ { rxNormCode: P.rxNormCode, doseQuantity: P.dose, dosesPerDay: P.dosesPerDay } })) return { rxNormCode: P.rxNormCode, isDraft: P.isDraft, isPRN: P.isPRN, prescription: P.prescription, dailyDose: mme.dailyDoseDescription, conversionFactor: mme.conversionFactor, mme: mme.mme } define function TotalMME(prescriptions List<MedicationRequest>): System.Quantity { value: Sum((MME(prescriptions)) M return M.mme.value), unit: 'mg/d' } define function GetDurationInDays(value FHIR.Duration): if value is null then null else case when value.code.value ~ 'a' then value.value.value * 365.0 when value.code.value ~ 'mo' then value.value.value * 30.0 when value.code.value ~ 'wk' then value.value.value * 7.0 when value.code.value ~ 'd' then value.value.value when value.code.value ~ 'h' then value.value.value / 24.0 when value.code.value ~ 'min' then value.value.value / 60.0 / 24.0 when value.code.value ~ 's' then value.value.value / 60.0 / 60.0 / 24.0 when value.code.value ~ 'ms' then value.value.value / 60.0 / 60.0 / 24.0 / 1000.0 when value.code.value is null then Message(1000, true, 'Undefined', 'Error', 'Duration unit code is null') else Message(1000, true, 'Undefined', 'Error', 'Unsupported duration unit code: ' + value.code.value) end define function GetIngredient(rxNormCode Code): OMTKLogic.GetIngredients(rxNormCode).ingredientName define function GetIngredients(rxNormCodes List<Code>): rxNormCodes rnc return GetIngredient(rnc) define function GetMedicationNames(medications List<MedicationRequest>): medications M return OMTKLogic.GetIngredients(ToRxNormCode((M.medication as FHIR.CodeableConcept).coding)).rxNormCode.display /* * Conversion Functions */ define function CodeableConceptsToString(concepts List<FHIR.CodeableConcept>): concepts c return CodeableConceptToString(c) define function CodingToString(coding FHIR.Coding): if (coding is null) then null else 'Code {' & 'code: ' & coding.code & 'system: ' & coding.system & 'version: ' & coding.version & 'display: ' & coding.display & '}' define function CodeableConceptToString(concept FHIR.CodeableConcept): if (concept is null or concept.coding is null) then null else 'CodeableConcept {' & 'Coding: [' & Combine(concept.coding Coding return CodingToString(Coding), ',') & ']' & '}' define function ToCodes(coding List<FHIR.Coding>): coding c return FHIRHelpers.ToCode(c) define function ToRxNormCode(coding List<FHIR.Coding>): singleton from ( coding C where C.system = 'http://www.nlm.nih.gov/research/umls/rxnorm' )
CDC / Security Risk Solutions, Inc. (SRS)
Centers for Disease Control and Prevention (CDC)
Value set Limited life expectancy conditions
Value set Therapies indicating end of life care
Value set Conditions likely terminal for opioid prescribing
Value set CDC malignant cancer conditions
Value set Oncology specialty designations (NUCC)
Value set Substance misuse behavioral counseling
Value set Conditions documenting substance misuse
Value set Opioid misuse assessment procedure
Value set Observation Category Laboratory
Value set Observation Category Procedure
Value set Conditions Documenting Sleep Disordered Breathing
Value set Opioid analgesics with ambulatory misuse potential
Value set Extended release opioid with ambulatory misuse potential
Value set Buprenorphine and methadone medications
Value set Non-synthetic opioid medications
Value set Non-opioid drug urine screening
Value set Encounter Diagnosis Condition Category
Value set Problem List Condition Category
Value set US Core Health Concern Condition Category
Opioid Other Than Synthetic Ordered In Last 12 Months
Active Ambulatory Benzodiazepine or CNS Depressant Rx
Conditions Indicating End of Life or With Limited Life Expectancy
Admitted/Referred/Discharged to Hospice Care
Prescribed Opioids for 21 or more of 30 Days for each of the past 3 Months