CPG Opioid Prescribing Guideline Examples
1.0.0 - ci-build International flag

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

Library: OMTK Logic (Experimental)

Official URL: http://cqframework.org/cpg-example-opioids/Library/OMTKLogic Version: 1.0.0
Active as of 2024-11-18 Computable Name: OMTKLogic

Usage:Clinical Focus: Medication requested (situation), Clinical Focus: Chronic pain (finding)

Copyright/Legal: CDC 2016+

Opioid Management Terminology Knowledge Base Logic for use in implementing CDC Opioid Prescribing Guidelines.

This library normalizes units to UCUM units, identifies the conversion factor for the given ingredient, identifies the non-surgical opioid ingredients and their strengths that make up a drug identified by the given rxNormCode, calculates daily dose for a specific ingredient based on the ingredient strength, dose form, dose quantity, and daily frequency, calculates daily frequency given frequency within a period, and calculates the Morphine Milligram Equivalency (MME) for a given prescription.

Generated Narrative: Library OMTKLogic

Participants

AuthorKensaku Kawamoto, MD, PhD, MHS
AuthorBryn Rhodes
AuthorFloyd Eisenberg, MD, MPH
AuthorRobert McClure, MD, MPH

Related Artifacts

DocumentationCDC guideline for prescribing opioids for chronic painhttps://www.cdc.gov/mmwr/volumes/65/rr/rr6501e1.htm?CDC_AA_refVal=https%3A%2F%2Fwww.cdc.gov%2Fmmwr%2Fvolumes%2F65%2Frr%2Frr6501e1er.htm
DocumentationMME Conversion Tableshttps://www.cdc.gov/drugoverdose/pdf/calculating_total_daily_dose-a.pdf
Depends OnCode System RxNormRxNorm

Contents

text/cql

library OMTKLogic version '0.0.0'

/*
This is a "stub" version of the OMTKLogic library that has no dependencies
on the OMTK data. It provides a workable version of the logic suitable for
example usage. For the full version of this library, see the CDC Opioid
 Prescribing Support Implementation Guide.
*/

codesystem RxNorm: 'http://www.nlm.nih.gov/research/umls/rxnorm'

/*
  Normalizes the input units to UCUM units

  Note guidance for UCUM presentation of medication units from SNOMED here:
  https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwjU3vLpicPTAhWFMGMKHRpOBUAQFggiMAA&url=https%3A%2F%2Fconfluence.ihtsdotools.org%2Fdownload%2Fattachments%2F17859188%2FExpressing%2520Units%2520of%2520Measure%2520for%2520Medicinal%2520Products.doc%3Fapi%3Dv2&usg=AFQjCNE5sboicqvJDUyXJ2im8VzBpgHE8A

  The values listed here are the only ones currently present in the OMTK data

  Based on the HL7 UCUM subset here:
  http://download.hl7.de/documents/ucum/ucumdata.html
*/
define function ToUCUM(unit String):
  case unit
    when 'MG' then 'mg'
    when 'MG/ACTUAT' then 'mg/{actuat}'
    when 'MG/HR' then 'mg/h'
    when 'MG/ML' then 'mg/mL'
    else 'Error: unknown{' + unit + '}'
  end

/*
  Calculates daily frequency given frequency within a period
*/
define function ToDaily(frequency Integer, period System.Quantity):
  case period.unit
    when 'h' then frequency * (24.0 / period.value)
    when 'min' then frequency * (24.0 / period.value) * 60
    when 's' then frequency * (24.0 / period.value) * 60 * 60
    when 'd' then frequency * (24.0 / period.value) / 24
    when 'wk' then frequency * (24.0 / period.value) / (24 * 7)
    when 'mo' then frequency * (24.0 / period.value) / (24 * 30) /* assuming 30 days in month */
    when 'a' then frequency * (24.0 / period.value) / (24 * 365) /* assuming 365 days in year */
    else null
  end

/*
  Returns true if the given dose form is a patch (transdermal system)
*/
define function IsPatch(doseFormCode Code):
  ToInteger(doseFormCode.code) = 316987

/*
  Returns the conversion factor for the given ingredient

Opioid (strength in mg except where noted)	MME Conversion Factor*
Buprenorphine, transdermal patch (MCG/HR)	12.6
Buprenorphine, tablet or film	30
Buprenorphine, film (MCG)	0.03
Butorphanol	7
Codeine	0.15
Dihydrocodeine	0.25
Fentanyl, buccal/SL tabet or lozenge/troche (MCG)	0.13
Fentanyl, film or oral spray (MCG)	0.18
Fentanyl, nasal spray (MCG)	0.16
Fentanyl, transdermal patch (MCG/HR)	2.4
Hydrocodone	1
Hydromorphone	4
Levomethadyl acetate	8
Levorphanol tartrate	11
Meperidine 	0.1
Methadone	3
  1-20 mg/d 4
  21-40 mg/d 8
  41-60 mg/d 10
  61-80 mg/d 12
Morphine	1
Opium	1 // NOTE: Not present as an ingredient in the RxNorm data
Oxycodone	1.5
Oxymorphone	3
Pentazocine	0.37
Tapentadol	0.4
Tramadol	0.1

*/
define function GetConversionFactor(ingredientCode Code, dailyDose Quantity, doseFormCode Code):
  case ToInteger(ingredientCode.code)
    when 161 then 0  /*	Acetaminophen */
    when 1191 then 0 /*	Aspirin */
    when 1223 then 0 /*	Atropine */
    when 1767 then 0 /*	Brompheniramine */
    when 1819 then ( /*	Buprenorphine */
      case
        when ToInteger(doseFormCode.code) = 316987 then 12.6 /* Transdermal system */
        else 30 /* Tablet or Film (or Film in MCG) */
      end
    )
    when 1841 then 7 /*	Butorphanol */
    when 1886 then 0 /*	Caffeine */
    when 2101 then 0 /*	Carisoprodol */
    when 2354 then 0 /*	chlorcyclizine */
    when 2400 then 0 /*	Chlorpheniramine */
    when 2670 then 0.15 /*	Codeine */
    when 3423 then 4 /*	Hydromorphone */
    when 3498 then 0 /*	Diphenhydramine */
    when 4337 then ( /*	Fentanyl */
      case
        when ToInteger(doseFormCode.code) in { 970789, 317007, 316992 } then 0.13 /* Buccal Tablet, Sublingual Tablet, Oral Lozenge */
        when ToInteger(doseFormCode.code) = 346163 then 0.18 /* Buccal Film */
        when ToInteger(doseFormCode.code) in { 126542, 346163 } then 0.16 /* Nasal Spray, Mucosal Spray */
        when IsPatch(doseFormCode) then 2.4 /* Transdermal system */
        else Message(1000, true, 'Undefined', 'Error', 'The dose form is unexpected')
      end
    )
    when 5032 then 0 /*	Guaifenesin */
    when 5489 then 1 /*	Hydrocodone */
    when 5640 then 0 /*	Ibuprofen */
    when 6102 then 0 /*	Kaolin */
    when 6378 then 11 /* Levorphanol (NOTE: Given as Levorphanol tartrate in the CDC conversion table) */
    when 6754 then 0.1 /*	Meperidine */
    when 6813 then ( /*	Methadone */
      case
        when dailyDose.value between 1 and 20 then 4
        when dailyDose.value between 21 and 40 then 8
        when dailyDose.value between 41 and 60 then 10
        when dailyDose.value >= 61 then 12
        else Message(1000, dailyDose.value < 1, 'Undefined', 'Error', 'The dose range is unexpected')
      end
    )
    when 7052 then 1 /*	Morphine */
    when 7242 then 0 /*	Naloxone */
    when 7243 then 0 /*	Naltrexone */
    when 7804 then 1.5 /*	Oxycodone */
    when 7814 then 3 /*	Oxymorphone */
    when 8001 then 0.37 /*	Pentazocine */
    when 8163 then 0 /*	Phenylephrine */
    when 8175 then 0 /*	Phenylpropanolamine */
    when 8745 then 0 /*	Promethazine */
    when 8896 then 0 /*	Pseudoephedrine */
    when 9009 then 0 /*	Pyrilamine */
    when 10689 then 0.1 /*	Tramadol */
    when 10849 then 0 /*	Triprolidine */
    when 19759 then 0 /*	bromodiphenhydramine */
    when 19860 then 0 /*	butalbital */
    when 22696 then 0 /*	dexbrompheniramine */
    when 22697 then 0 /*	dexchlorpheniramine */
    when 23088 then 0.25 /*	dihydrocodeine */
    when 27084 then 0 /*	homatropine */
    when 35780 then 0 /*	ropivacaine */
    when 237005 then 8 /*	Levomethadyl (NOTE: given as Levomethadyl acetate in the CDC conversion table) */
    when 636827 then 0 /*	guaiacolsulfonate */
    when 787390 then 0.4 /*	tapentadol */
    else 0
  end

define function EnsureMicrogramQuantity(strength Quantity):
  if strength.value < 0.1 and (PositionOf('mg', strength.unit) = 0) then
    Quantity {
      value: strength.value * 1000,
      unit: 'mcg' + Substring(strength.unit, 2)
    }
  else
    strength

/*
  Returns the non-surgical opioid ingredients and their strengths that
  make up the drug identified by the given rxNormCode as a list of tuples:

  List<Tuple {
    rxNormCode Code,
    doseFormCode Code,
    doseFormName String,
    ingredientCode Code,
    ingredientName String,
    strength Quantity
  }>
*/
define function GetIngredients(rxNormCode Code):
  {
    {
      rxNormCode: rxNormCode,
      doseFormCode: null as Code,
      doseFormName: null as String,
      ingredientCode: null as Code,
      ingredientName: null as String,
      strength: null as Quantity
    }
  }
/*
  (
    [MED_SCDC_FOR_DRUG: DRUG_RXCUI in rxNormCode] SD
      where exists ([NON_SURGICAL_OPIOID_TO_INCLUDE: DRUG_RXCUI in SD.DRUG_RXCUI])
      return {
        rxNormCode: rxNormCode,
        component: SingletonFrom([MED_SCDC: SCDC_RXCUI in SD.SCDC_RXCUI]),
        ingredientCode: SingletonFrom([MED_INGREDIENT_FOR_SCDC: SCDC_RXCUI in SD.SCDC_RXCUI]).INGREDIENT_RXCUI,
        doseFormCode: SingletonFrom([MED_DRUG_DOSE_FORM: DRUG_RXCUI in SD.DRUG_RXCUI]).DOSE_FORM_RXCUI // Could potentially look this up only once...
      }
  ) C
    let
      ingredient: SingletonFrom([MED_INGREDIENT: INGREDIENT_RXCUI in C.ingredientCode]),
      doseForm: SingletonFrom([MED_DOSE_FORM: DOSE_FORM_RXCUI in C.doseFormCode])
    where exists (
      [MED_INGREDIENT_TYPE: INGREDIENT_RXCUI in C.ingredientCode] IT
        where IT.INGREDIENT_TYPE = 'NonSurgicalOpioid'
    )
    return {
      rxNormCode: rxNormCode,
      doseFormCode: C.doseFormCode,
      doseFormName: doseForm.DOSE_FORM_NAME,
      ingredientCode: C.ingredientCode,
      ingredientName: ingredient.INGREDIENT_NAME,
      strength:
        EnsureMicrogramQuantity(
          Quantity {
            value: C.component.STRENGTH_VALUE,
            unit: ToUCUM(C.component.STRENGTH_UNIT)
          }
        )
    }
*/

/*
  Calculates daily dose for a specific ingredient based on the ingredient strength, dose form, dose quantity, and daily frequency
*/
define function GetDailyDose(ingredientCode Code, strength Quantity, doseFormCode Code, doseQuantity Quantity, dosesPerDay Decimal):
  case
	  /* if patch --> daily dose = dose value (e.g, number patches with doseQuantity unit = "patch") * per-hour strength */
    when IsPatch(doseFormCode) then
      /* buprenorphine or fentanyl patch */
      if ToInteger(ingredientCode.code) in { 1819, 4337 } then
        Quantity { value: doseQuantity.value * strength.value, unit: strength.unit }
      else
        null

    /* if dose unit in actual mass units (mg or mcg -- when it's a single med) --> daily dose = numTimesPerDay * dose */
    when doseQuantity.unit in { 'mg', 'mcg' } then
      Quantity { value: dosesPerDay * doseQuantity.value, unit: doseQuantity.unit }

    /* if doseQuantity is in actual volume units (mL) --> daily dose = numTimesPerDay * dose * strength */
    when doseQuantity.unit = 'mL' and (PositionOf('/mL', strength.unit) = Length(strength.unit) - 3) then
      Quantity { value: dosesPerDay * doseQuantity.value * strength.value, unit: Substring(strength.unit, 0, PositionOf('/', strength.unit)) }

		/* if doseQuantity is not in actual units (e.g., 1 tab, 1 spray -- when it's a combo med with a unit of tablet, or it's mg/actuat) -->  daily dose = numTimesPerDay * dose value * strength value */
    else
      Quantity { value: dosesPerDay * doseQuantity.value * strength.value, unit: Substring(strength.unit, 0, PositionOf('/', strength.unit)) }
  end

define function GetMedicationName(rxNormCode Code):
  rxNormCode.display
  /*
  SingletonFrom([MED_DRUG: DRUG_RXCUI in rxNormCode]).DRUG_NAME
  */

/*
  Builds a description for the daily dose for an ingredient
*/
define function GetDailyDoseDescription(ingredientCode Code, ingredientName String, strength Quantity, doseFormCode Code, doseFormName String, doseQuantity Quantity, dosesPerDay Decimal, dailyDose Quantity):
  case
    /* if patch */
    when IsPatch(doseFormCode) then
      /* buprenorphine or fentanyl patch */
      if ToInteger(ingredientCode.code) in { 1819, 4337 } then
        ingredientName + ' patch: ' + ToString(doseQuantity.value) + ' * ' + ToString(strength) + ' = ' + ToString(dailyDose)
      else
        null

    /* if dose unit in actual mass units (mg or mcg -- when it's a single med) */
    when doseQuantity.unit in { 'mg', 'mcg' } then
      ingredientName + ' ' + doseFormName + ': ' + ToString(dosesPerDay) + '/d * ' + ToString(doseQuantity) + ' = ' + ToString(dailyDose)

    /* if doseQuantity in actual volume units (mL) or not in actual units (e.g. 1 tab, 1 spray) */
    else
      ingredientName + ' ' + doseFormName + ': ' + ToString(dosesPerDay) + '/d * ' + ToString(doseQuantity) + ' * ' + ToString(strength) + ' = ' + ToString(dailyDose)
  end

/*
  Calculates MMEs for the given input prescription information and returns it
  as a list of tuples:

  List<Tuple {
    rxNormCode Code,
    doseFormCode Code,
    doseQuantity Quantity,
    dosesPerDay Decimal,
    ingredientCode Code,
    ingredientName String,
    strength Quantity,
    dailyDose Quantity,
    dailyDoseDescription String,
    conversionFactor Decimal,
    mme Quantity
  }>
*/
define function CalculateMMEs(medications List<Tuple { rxNormCode Code, doseQuantity Quantity, dosesPerDay Decimal }>):
  Flatten(
    medications M
      let Ingredients: GetIngredients(M.rxNormCode)
      return
        Ingredients I
          let
            adjustedDoseQuantity: EnsureMicrogramQuantity(M.doseQuantity),
            dailyDose: GetDailyDose(I.ingredientCode, I.strength, I.doseFormCode, adjustedDoseQuantity, M.dosesPerDay),
            factor: GetConversionFactor(I.ingredientCode, dailyDose, I.doseFormCode)
          return {
            rxNormCode: M.rxNormCode,
            doseFormCode: I.doseFormCode,
            doseQuantity: adjustedDoseQuantity,
            dosesPerDay: M.dosesPerDay,
            ingredientCode: I.ingredientCode,
            ingredientName: I.ingredientName,
            strength: I.strength,
            dailyDose: dailyDose,
            dailyDoseDescription: GetDailyDoseDescription(I.ingredientCode, I.ingredientName, I.strength, I.doseFormCode, I.doseFormName, adjustedDoseQuantity, M.dosesPerDay, dailyDose),
            conversionFactor: factor,
            mme: Quantity {
              value: dailyDose.value * factor,
              unit: dailyDose.unit + '/d'
            }
          }
  )

//define TestCalculateMMEs:
//  CalculateMMEs({ { rxNormCode: Code '388508' from RxNorm, doseQuantity: Quantity { value: 1, unit: 'patch' }, dosesPerDay: 0.33 } })

Content not shown - (application/elm+xml, size = 179Kb )