CPG Opioid Prescribing Guideline Examples - Local Development build (v1.0.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions
Common Opioid Decision Support Logic for use in implementing CDC Opioid Prescribing Guidelines.
This library contains common logic across recommendations including MME calculations, conversions, and looking up codes in valuesets.
Participants
Author | Kensaku Kawamoto, MD, PhD, MHS |
Author | Bryn Rhodes |
Author | Floyd Eisenberg, MD, MPH |
Author | Robert McClure, MD, MPH |
Related Artifacts
Documentation | CDC guideline for prescribing opioids for chronic pain | https://www.cdc.gov/mmwr/volumes/65/rr/rr6501e1.htm?CDC_AA_refVal=https%3A%2F%2Fwww.cdc.gov%2Fmmwr%2Fvolumes%2F65%2Frr%2Frr6501e1er.htm |
Depends On | Library OMTKLogic | http://cqframework.org/cpg-example-opioids/Library/OMTKLogic|0.0.0 |
Depends On | Code System SNOMED | SNOMED CT (all versions) |
Depends On | Code System Medication Request Category Codes | MedicationRequest Category Codes |
Depends On | Value Set Active Condition | CPG Active Condition Value Set |
Depends On | Value Set Benzodiazepines | http://fhir.org/guides/cdc/opioid-cds/ValueSet/benzodiazepines-medications |
Depends On | Value Set End of Life Conditions | http://fhir.org/guides/cdc/opioid-cds/ValueSet/end-of-life-conditions |
Depends On | Value Set Hospice Disposition | http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-disposition |
Depends On | Value Set Hospice Finding Codes | http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-finding |
Depends On | Value Set Hospice Procedure Codes | http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-procedure |
Depends On | Value Set Illicit Drug Screening | http://fhir.org/guides/cdc/opioid-cds/ValueSet/illicit-drug-urine-screening |
Depends On | Value Set Limited Life Expectancy Conditions | http://fhir.org/guides/cdc/opioid-cds/ValueSet/limited-life-expectancy-conditions |
Depends On | Value Set Long Acting Opioids | http://fhir.org/guides/cdc/opioid-cds/ValueSet/long-acting-opioids |
Depends On | Value Set Naloxone | http://fhir.org/guides/cdc/opioid-cds/ValueSet/naloxone-medications |
Depends On | Value Set Risk Assessment | http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-abuse-assessment |
Depends On | Value Set Opioid Drug Screening | http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-urine-screening |
Depends On | Value Set Ambulatory Abuse Potential Opioids | http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioids-abused-in-ambulatory-care |
Depends On | Value Set Substance Abuse | http://fhir.org/guides/cdc/opioid-cds/ValueSet/substance-abuse |
Parameters
IsForChronicPain | out | 0 | 1 | boolean |
Active Ambulatory Opioid Rx | out | 0 | * | MedicationRequest |
Active Ambulatory Benzodiazepine Rx | out | 0 | * | MedicationRequest |
Active Ambulatory Naloxone Rx | out | 0 | * | MedicationRequest |
Ambulatory Opioid Rx | out | 0 | * | MedicationRequest |
End of Life Assessment | out | 0 | 1 | boolean |
Data Requirements
Type: MedicationRequest |
Filter | Value |
medication | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioids-abused-in-ambulatory-care |
Type: MedicationRequest |
Filter | Value |
medication | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/benzodiazepines-medications |
Type: MedicationRequest |
Filter | Value |
medication | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/naloxone-medications |
Type: MedicationRequest |
Filter | Value |
medication | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioids-abused-in-ambulatory-care |
Type: Condition |
Filter | Value |
code | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/end-of-life-conditions |
Type: Condition |
Filter | Value |
code | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/limited-life-expectancy-conditions |
Type: ServiceRequest |
Filter | Value |
code | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-procedure |
Type: ServiceRequest |
Filter | Value |
code | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-procedure |
Type: Observation |
Filter | Value |
code | In ValueSet http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-finding |
Contents
text/cql
library OpioidCDSR4Common version '0.1.0'
using FHIR version '4.0.0'
include FHIRHelpers version '4.0.0' called FHIRHelpers
include OMTKLogic version '0.0.0' called OMTKLogic
codesystem "SNOMED": 'http://snomed.info/sct'
codesystem "Medication Request Category Codes": 'http://terminology.hl7.org/CodeSystem/medicationrequest-category'
valueset "Active Condition": 'http://hl7.org/fhir/uv/cpg/ValueSet/cpg-active-condition-vs'
valueset "Benzodiazepines": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/benzodiazepines-medications'
// TODO: Fix this name
valueset "End of Life Conditions": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/end-of-life-conditions'
// Harvested from VSAC - OID: 2.16.840.1.113762.1.4.1108.15
// NOTE: This harvest note is incorrect, none of the following 3 value sets contain any of the codes in the above referenced valueset
// Rob will construct an appropriate hospice value set aligned with current eCQM program usage and we will use that when available
valueset "Hospice Disposition": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/hospice-disposition'
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 "Illicit Drug Screening": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/illicit-drug-urine-screening'
// Harvested from VSAC - OID: 2.16.840.1.113883.3.526.3.1259
valueset "Limited Life Expectancy Conditions": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/limited-life-expectancy-conditions'
valueset "Long Acting Opioids": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/long-acting-opioids'
valueset "Naloxone": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/naloxone-medications'
valueset "Risk Assessment": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-abuse-assessment'
valueset "Opioid Drug Screening": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioid-urine-screening'
valueset "Ambulatory Abuse Potential Opioids": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/opioids-abused-in-ambulatory-care'
valueset "Substance Abuse": 'http://fhir.org/guides/cdc/opioid-cds/ValueSet/substance-abuse'
// TODO: Turn this into a valueset
code "Referral to Hospice": '306205009' from "SNOMED"
// TODO: Turn this into a valueset
code "Outpatient": 'outpatient' from "Medication Request Category Codes"
// TODO: Capture process decisions for long-term opioid use
define IsForChronicPain: true
define "Active Ambulatory Opioid Rx":
[MedicationRequest: "Ambulatory Abuse Potential Opioids"] Rx
where Rx.status = 'active'
and ToCodes(Rx.category.coding) contains "Outpatient"
define "Active Ambulatory Benzodiazepine Rx":
[MedicationRequest: "Benzodiazepines"] Rx
where Rx.status = 'active'
and ToCodes(Rx.category.coding) contains "Outpatient"
define "Active Ambulatory Naloxone Rx":
[MedicationRequest: "Naloxone"] Rx
where Rx.status = 'active'
and ToCodes(Rx.category.coding) contains "Outpatient"
define "Ambulatory Opioid Rx":
[MedicationRequest: "Ambulatory Abuse Potential Opioids"] Rx
where ToCodes(Rx.category.coding) contains "Outpatient"
define "End of Life Assessment":
// 1. Conditions indicating end of life or with limited life expectancy
exists (
(
[Condition: "End of Life Conditions"] C
where C.clinicalStatus in "Active Condition"
)
union
(
[Condition: code in "Limited Life Expectancy Conditions"] C
where C.clinicalStatus in "Active Condition"
)
)
// 2. Admitted/referred/discharged to hospice care
or exists (
(
[ServiceRequest: code in "Referral to Hospice"] RR
where RR.status in { 'active', 'completed' }
)
union
(
[ServiceRequest: code in "Hospice Procedure Codes"] P
where P.status in { 'in-progress', 'completed' }
)
union
(
[ServiceRequest: code in "Hospice Procedure Codes"] E
where E.status in { 'planned', 'arrived', 'in-progress', 'finished', 'onleave' }
)
union
(
[Observation: code in "Hospice Finding Codes"] O
where not (O.status 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 in { 'planned', 'arrived', 'in-progress', 'finished', 'onleave' }
)
)
define function Prescriptions(Orders List<MedicationRequest>):
Orders O
let
// NOTE: Assuming medication is specified as a CodeableConcept with a single RxNorm code
rxNormCode: ToCode(O.medication.coding[0]),
medicationName: OMTKLogic.GetMedicationName(rxNormCode),
// NOTE: Assuming a single dosage instruction element
dosageInstruction: O.dosageInstruction[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 dosageInstruction.doseAndRate[0].dose is FHIR.Range
then ToString(ToQuantity(dosageInstruction.doseAndRate[0].dose.low))
+ '-' + ToString(ToQuantity(dosageInstruction.doseAndRate[0].dose.high))
+ dosageInstruction.doseAndRate[0].dose.high.unit.value
else ToString(ToQuantity(dosageInstruction.doseAndRate[0].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.value,
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.value then ' PRN' else ''),
// TODO: Shouldn't need the ToQuantity here...
dose: if dosageInstruction.doseAndRate[0].dose is FHIR.Range
then ToQuantity(dosageInstruction.doseAndRate[0].dose.high)
else ToQuantity(dosageInstruction.doseAndRate[0].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 ProbableDaysInRange(Orders List<MedicationRequest>, daysPast Integer, numDaysInDaysPast Integer):
Orders orders
let
frequency: orders.dosageInstruction[0].timing.repeat.frequency.value,
period: orders.dosageInstruction[0].timing.repeat.period.value,
periodDays: GetPeriodDays(orders.dosageInstruction[0].timing.repeat.periodUnit.value),
dosesPerDay:
if (frequency / (period * periodDays)) >= 1.0
then 1.0
else frequency / (period * periodDays),
repeat: orders.dispenseRequest.numberOfRepeatsAllowed.value,
supplyDuration: GetDurationInDays(orders.dispenseRequest.expectedSupplyDuration),
validityPeriod: days between orders.dispenseRequest.validityPeriod."start".value and Today(),
endDifference:
if orders.dispenseRequest.validityPeriod."end".value < Today()
then days between orders.dispenseRequest.validityPeriod."end".value and Today()
else 0
return
if (repeat * supplyDuration) < numDaysInDaysPast then false
else
(dosesPerDay * ((repeat * supplyDuration) / validityPeriod) * (daysPast - endDifference)) >= numDaysInDaysPast
define function GetPeriodDays(value System.String):
case
when value = 'a' then 365.0
when value = 'mo' then 30.0
when value = 'h' then 1.0/24.0
when value = 'min' then 1.0/24.0*60.0
when value = 's' then 1.0/24.0*60.0*60.0
when value = 'ms' then 1.0/24.0*60.0*60.0*1000.0
else 1.0
end
define function GetDurationInDays(value FHIR.Duration):
case
when StartsWith(value.unit.value, 'a') then value.value.value * 365.0
when StartsWith(value.unit.value, 'mo') then value.value.value * 30.0
when StartsWith(value.unit.value, 'wk') then value.value.value * 7.0
when StartsWith(value.unit.value, 'd') then value.value.value
when StartsWith(value.unit.value, 'h') then value.value.value / 24.0
when StartsWith(value.unit.value, 'min') then value.value.value / 60.0 / 24.0
when StartsWith(value.unit.value, 's') then value.value.value / 60.0 / 60.0 / 24.0
when StartsWith(value.unit.value, 'ms') then value.value.value / 60.0 / 60.0 / 24.0 / 1000.0
else Message(1000, true, 'Undefined', 'Error', 'Unsupported duration unit')
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.coding)).rxNormCode.display
/*
* Conversion Functions
*/
define function ToCode(coding FHIR.Coding):
System.Code {
code: coding.code.value,
system: coding.system.value,
version: coding.version.value,
display: coding.display.value
}
define function ToCodes(coding List<FHIR.Coding>):
coding c return ToCode(c)
define function ToRxNormCode(coding List<FHIR.Coding>):
singleton from (
coding C where C.system = 'http://www.nlm.nih.gov/research/umls/rxnorm'
)
define function ToQuantity(quantity FHIR.Quantity):
System.Quantity { value: quantity.value.value, unit: quantity.unit.value }
Content not shown - (
application/elm+xml
, size = 266Kb )