Breast Cancer Screening and Management (BCSM) Clinical Decision Support (CDS) Implementation Guide
1.0.0 - ci-build

Breast Cancer Screening and Management (BCSM) Clinical Decision Support (CDS) Implementation Guide - Local Development build (v1.0.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions

Library: BCSM Common Functions

Official URL: http://cancerscreeningcds.github.io/bcsm-cds/Library/BCSMCommonFunctions Version: 1.0.0
Draft as of 2025-02-28 Computable Name: BCSMCommonFunctions
Title: BCSM Common Functions
Id: BCSMCommonFunctions
Version: 1.0.0
Url: BCSM Common Functions
Status: draft
Type:

system: http://terminology.hl7.org/CodeSystem/library-type

code: logic-library

Date: 2025-02-28 23:16:14+0000
Publisher: MITRE
Related Artifacts:

Dependencies

Parameters:
NameTypeMinMaxIn/Out
OverdueDefaultQuantity01In
DueDefaultQuantity01In
OverdueAnnualQuantity01In
DueAnnualQuantity01In
OverdueFollowupQuantity01In
PatientPatient01Out
SourceDocumentationACSResource01Out
SourceDocumentationUSPSTFResource01Out
SourceDocumentationOtherResource01Out
Data Requirements:
TypeProfileMSCode Filter
Patient http://hl7.org/fhir/StructureDefinition/Patient
DiagnosticReport http://hl7.org/fhir/StructureDefinition/DiagnosticReport ;;;
DiagnosticReport http://hl7.org/fhir/StructureDefinition/DiagnosticReport ;;; code filter:
path: code
Content: text/cql
/*  
  Author: CMS Alliance to Modernize Healthcare, operated by THE MITRE Corporation.

  (C) 2024 The MITRE Corporation. All Rights Reserved. 
  Approved for Public Release: 24-2711. 
  Distribution Unlimited.

  Unless otherwise noted, this work is available under an Apache 2.0 license. 
  It was produced by the MITRE Corporation for the Division of Cancer Prevention 
  and Control, Centers for Disease Control and Prevention in accordance with the 
  Statement of Work, contract number 75FCMC18D0047, task order number 75D30123F17931.
*/

library BCSMCommonFunctions version '1.0.0'

using FHIR version '4.0.1'

include CDSConnectCommonsforFHIRv401 version '1.0.0' called C3F
include FHIRHelpers version '4.0.1' called FHIRHelpers
include CSMCommonFunctions version '1.0.0' called CSMCommonFunctions

codesystem "FlagCode": 'http://cancerscreeningcds.github.io/bcsm-cds/CodeSystem/recommendation-flag-code-system'

code "OVERDUE" : 'overdue' from "FlagCode" display 'Overdue'
code "DUE" : 'due' from "FlagCode" display 'Due'
code "NOT DUE" : 'not-due' from "FlagCode" display 'Not due'
codesystem "cpg-activity-type-cs": 'http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-activity-type-cs'
code "Record an inference": 'record-inference' from "cpg-activity-type-cs" display 'Record an inference'

parameter "OverdueDefault" System.Quantity default 3 months
parameter "DueDefault" System.Quantity default 2 months
parameter "OverdueAnnual" System.Quantity default 2 months
parameter "DueAnnual" System.Quantity default 2 months
parameter "OverdueFollowup" System.Quantity default 90 days

context Patient

//------------------------------------------------------------------------------
// Recommendation model
//------------------------------------------------------------------------------

define SourceDocumentationACS:
  {
    label: 'ACS'
  }

define SourceDocumentationUSPSTF:
  {
    label: 'USPSTF'
  }

define SourceDocumentationOther:
  {
    label: 'Other'
  }    

define function SetRecommendation(          
    code System.Code,
    reason System.Code,
    documentation Tuple{label System.String},
    timingAge Tuple{value Decimal, code String},
    event System.DateTime,
    repeat Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}) 
    returns Tuple {code System.Code,reason System.Code,documentation Tuple{label System.String},timingAge Tuple{value Decimal, code String},
      timingTiming Tuple{event List<System.String>,repeat Tuple{boundsPeriod Tuple{start System.String},frequency System.Integer,period System.Decimal,periodUnit System.String}},
      flag System.Code
    }: 
  code C
  let startDate: if timingAge is not null then CalculateStartDateByAge(timingAge) else null,
  nextdue: if event is not null then event 
    else CalculateNextDueByCodeAndInterval(ToConcept(C), startDate, repeat),
  timingTiming: {
      event: {ToString(nextdue)},
      repeat: {
        boundsPeriod: {
          start: ToString(startDate)
        },
        frequency: repeat.frequency,
        period: repeat.period,
        periodUnit: repeat.periodUnit
      }
    },
  flag: EvaluateFlag(repeat, nextdue)    
  return      
  Tuple {
    code: C,
    reason: reason,
    documentation: documentation,
    timingAge: timingAge,
    timingTiming: timingTiming,
    flag: flag
  }

define function CalculateStartDateByAge(timingAge Tuple{value Decimal, code String}): 
  Patient.birthDate + System.Quantity{value: (timingAge.value), unit: FHIRHelpers.ToCalendarUnit(timingAge.code)}

define function SetFollowupRecommendation(          
    code System.Code,
    reason System.Code,
    documentation Tuple{label System.String},
    event System.DateTime)
    returns Tuple {code System.Code,reason System.Code,documentation Tuple{label System.String},
      timingTiming Tuple{event List<System.String>},
      flag System.Code
    }: 
  code C
  let nextdue: event,
  timingTiming: {
      event: {ToString(nextdue)}
    },
  flag: EvaluateFollowupFlag(nextdue)    
  return      
  Tuple {
    code: C,
    reason: reason,
    documentation: documentation,
    timingTiming: timingTiming,
    flag: flag
  }  

//------------------------------------------------------------------------------
// Calculated next due date
//------------------------------------------------------------------------------
//  +startDate, +repeat, +lastreport = use startDate if > lastreport else lastreport + repeat
//  +startDate, +repeat, -lastreport = use startDate
//  +startDate, -repeat, +lastreport = use startDate if > lastreport else no prediction (non-repeating and ?completed)
//  +startDate, -repeat, -lastreport = use startDate
//  -startDate, +repeat, +lastreport = use lastreport + repeat
//  -startDate, +repeat, -lastreport = use today
//  -startDate, -repeat, +lastreport = no prediction (non-repeating and ?completed)
//  -startDate, -repeat, -lastreport = use today
define function CalculateNextDueByCodeAndInterval(code Concept, 
    startDate DateTime,
    repeat Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}):
  code C
  let lastReport: MostRecentDiagnosticReportByCode(C)
  return
    case
      // +startDate, +repeat, +lastReport
      when startDate is not null and RepeatIsValid(repeat) and lastReport is not null then
        if startDate > FHIRHelpers.ToDateTime(lastReport.effective) as DateTime then startDate 
        else date from AddIntervalToDateTime(FHIRHelpers.ToDateTime(lastReport.effective) as DateTime, repeat)
      // +startDate, +repeat, -lastReport
      when startDate is not null and RepeatIsValid(repeat) and lastReport is null then
        startDate
      // +startDate, -repeat, +lastReport
      when startDate is not null and repeat is null and lastReport is not null then
        if startDate > FHIRHelpers.ToDateTime(lastReport.effective) as DateTime then startDate else null
      // +startDate, -repeat, -lastReport
      when startDate is not null and repeat is null and lastReport is null then
        startDate
      // -startDate, +repeat, +lastReport
      when startDate is null and RepeatIsValid(repeat) and lastReport is not null then
        date from AddIntervalToDateTime(FHIRHelpers.ToDateTime(lastReport.effective) as DateTime, repeat)
      // -startDate, +repeat, -lastReport
      when startDate is null and RepeatIsValid(repeat) and lastReport is null then
        Today()
      // -startDate, -repeat, +lastReport
      when startDate is null and RepeatIsValid(repeat) is false and lastReport is not null then
        null
      // -startDate, -repeat, -lastReport
      when startDate is null and RepeatIsValid(repeat) is false and lastReport is null then
        Today()
      else null  
    end

define function RepeatIsValid(repeat Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}):
  repeat.frequency is not null and repeat.period is not null and repeat.periodUnit is not null

define function MostRecentDiagnosticReportByCode(code Concept):
    CSMCommonFunctions.MostRecentDiagnosticReport(DiagnosticReportsByCode(code))
    
define function DiagnosticReportsByCode(concept Concept):
  [DiagnosticReport] R
    where R.code ~ concept  

define function MostRecentDiagnosticReportByVS(vs System.ValueSet):
    CSMCommonFunctions.MostRecentDiagnosticReport(DiagnosticReportsByVS(vs))

define function DiagnosticReportsByVS(vs System.ValueSet):
  [DiagnosticReport: vs] R

define function AddIntervalToDateTime(startDateTime System.DateTime, 
    repeat Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}):
  startDateTime + System.Quantity {
    value: (repeat.frequency * repeat.period) * 1.0,
    unit: FHIRHelpers.ToCalendarUnit(repeat.periodUnit)
  }

//------------------------------------------------------------------------------
// scheduled frequency
//------------------------------------------------------------------------------

define function EvaluateInterval(repeat Choice<FHIR.Timing.Repeat, Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}>):
  case
    when IntervalIsUnknown(repeat)
      then 'Unknown'
    when IntervalIsAnnual(repeat)
      then 'Annual'
    when IntervalIsBiennial(repeat)
      then 'Biennial'
    else 'Other'
  end

define function IntervalIsAnnual(repeat Choice<FHIR.Timing.Repeat, Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}>):
  if repeat.frequency.value = 1 and repeat.period.value = 1 and repeat.periodUnit.value = 'a' 
  then true else false

define function IntervalIsBiennial(repeat Choice<FHIR.Timing.Repeat, Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}>):
  if repeat.frequency.value = 1 and repeat.period.value = 2 and repeat.periodUnit.value = 'a' 
  then true else false

 define function IntervalIsUnknown(repeat Choice<FHIR.Timing.Repeat, Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}>):
  if repeat is null or repeat.frequency is null
  then true else false 

//------------------------------------------------------------------------------
// flag due/overdue
//------------------------------------------------------------------------------

define function EvaluateFlag(repeat Choice<FHIR.Timing.Repeat, Tuple{frequency System.Integer,period System.Decimal,periodUnit System.String}>, 
    nextDueDate DateTime):
  case
    when IntervalIsAnnual(repeat)
      then EvaluateFlagAnnual(nextDueDate)
    when IntervalIsBiennial(repeat)
      then EvaluateFlagBiennial(nextDueDate)
    else EvaluateFlagDefault(nextDueDate)
  end

define function EvaluateFlagDefault(nextDueDate DateTime):
  if nextDueDate < Today() - (OverdueDefault) then
    "OVERDUE"
  else if nextDueDate <= Today() + (DueDefault) and nextDueDate >= Today() - (OverdueDefault) then
    "DUE"
  else
    "NOT DUE"

define function EvaluateFlagBiennial(nextDueDate DateTime):
  EvaluateFlagDefault(nextDueDate)

define function EvaluateFlagAnnual(nextDueDate DateTime):
  if nextDueDate < (Today() - OverdueAnnual) then
    "OVERDUE"
  else if nextDueDate <= (Today() + DueAnnual) and nextDueDate >= (Today() - OverdueAnnual) then
    "DUE"
  else
    "NOT DUE" 

define function EvaluateFollowupFlag(nextDueDate DateTime):
  if nextDueDate < Today() - (OverdueFollowup) then
    "OVERDUE"
  else if nextDueDate <= Today() + (DueDefault) and nextDueDate >= Today() - (OverdueFollowup) then
    "DUE"
  else
    "NOT DUE"    

//------------------------------------------------------------------------------
// Create extension
//------------------------------------------------------------------------------
define function CreateExtension(url String, coding Code):
  {    
    url: url,
    valueCodeableConcept: {
      coding: { coding }
    } 
  }

define function CreateRecommendationFlagExtension(coding Code):
  {{    
    url: 'http://cancerscreeningcds.github.io/bcsm-cds/StructureDefinition/csm-recommendation-flag',
    valueCodeableConcept: {
      coding: { coding }
    } 
  }}

//------------------------------------------------------------------------------
// Group recommendations
//------------------------------------------------------------------------------

define function AggregateRecommendationsByCode(recommendations List<Choice<Tuple{code Code,reason Code}, 
  Tuple{code System.Code,reason System.Code,timingAge Tuple{value Decimal, code String},
    timingTiming Tuple{event List<String>,repeat Tuple{boundsPeriod Tuple{start System.String},frequency System.Integer,period System.Integer,periodUnit System.String}}}>>)
  returns List<Tuple{code Code, value List<Any>}>:
  // For each unique code, create a  Tuple with the code and the List of associated values
  distinct(recommendations.code) actCode
  return
  {
    code: actCode,
    value: recommendations R where R.code ~ actCode
  }   

//------------------------------------------------------------------------------
// Diagnostic report results
//------------------------------------------------------------------------------
// Aggregate result codes -
//  DiagnosticReport.conclusionCode[]
//  Observatio.value where DiagnosticReport.result[].reference = Observation
//  Observatio.value where Observation.basedOn = DiagnosticReport.basedOn (request)

// if a DiagnosticReport.code has target code, then call original CollateConclusionCodes
// if a DiagnosticReport.code does not have target code, then return values from matched Observations
define function CollateConclusionCodes(D DiagnosticReport, ObsList List<Observation>, code System.Code):
  if FHIRHelpers.ToConcept(D.code) ~ code then
    CollateConclusionCodes(D, ObsList)
  else
    ValuesFromObservation(D, ObsList)  

define function CollateConclusionCodes(D DiagnosticReport, ObsList List<Observation>):
  D.conclusionCode union
  ValuesFromObservation(D, ObsList)

define function ValuesFromObservation(D DiagnosticReport, ObsList List<Observation>):
  (ObsList) O
  where
    Exists( (D.result ) R where Last(Split(R.reference,'/')) =  O.id ) or
    Exists( (O.basedOn) oB where AnyTrue((D.basedOn) dB return dB = oB ) )
  return O.value as FHIR.CodeableConcept

//------------------------------------------------------------------------------
// Local observations
//------------------------------------------------------------------------------  

define function MostRecentLocalObservation(observations List<FHIR.Observation>, encounter String, ObservationLookbackPeriod System.Quantity):
  C3F.MostRecent(
    (observations O where encounter is not null and O.encounter.reference = encounter)
    union
    (C3F.ObservationLookBack(
      observations,
      ObservationLookbackPeriod
    )) except { null }
  )

define function SortedObservations(obs List<FHIR.Observation>):
  obs o sort by Coalesce(
    (effective as FHIR.dateTime).value,
    (effective as FHIR.instant).value,
    (effective as FHIR.Period)."end".value,
    (effective as FHIR.Period)."start".value,
    (issued as FHIR.instant).value
  ) desc   

//------------------------------------------------------------------------------
// Service requests
//------------------------------------------------------------------------------
define function ServiceRequestsWithOrderIntent(requests List<ServiceRequest>):
  requests O
    where O.intent in {'order', 'original-order', 'reflex-order', 'filler-order', 'instance-order'}  

define function ServiceRequestWithOrderIntent(request ServiceRequest):
  request.intent in {'order', 'original-order', 'reflex-order', 'filler-order', 'instance-order'}
Content: application/elm+xml
Encoded data (313308 characters)
Content: application/elm+json
Encoded data (591424 characters)