Skip to content

Instantly share code, notes, and snippets.

@pacharanero
Last active September 26, 2018 08:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pacharanero/f83f66d8e266ef4d223405f1f56ced85 to your computer and use it in GitHub Desktop.
Save pacharanero/f83f66d8e266ef4d223405f1f56ced85 to your computer and use it in GitHub Desktop.
An illustration of how OpenEHR Expression Language woud look as Ruby
# real ruby libraries you can use to save having to develop them anew
require 'iso-639'
# fictional ruby libraries that would add the missing datatypes
require 'icd10' # a library which handles ICD-10 bindings
require 'snomed-ct' # a library which handles SNOMED-CT bindings
require 'adl-binding' # a library which lets us handle ADL as a ruby object
require 'terminology' # some arbitrary terminology helper library
# openEHR-ELOM.breast_cancer_treatment.v1
# inherits from BasicOpenEhrRule
class BreastCancerRule < BasicOpenEhrRule
# metadata
def initialize(cancer_diagnosis,
has_metastasis,
er_positive,
pr_positive,
her2_positive,
ki67,
tnm_t,
tnm_n,
tnm_g,
has_dx_transmural_mi,
ejection_fraction,
hs_dx_hf_stage_2_4)
# @@class_variables
@@title = 'openEHR-ELOM.breast_cancer_treatment.v1'
@@language = {
original_language: ISO_639.find("en") # using an existing language means the existing libraries for this stuff are already done!
}
@@description = {
lifecycle_state: 'unmanaged',
original_author: 'placeholder text'
}
@@use_model = 'org.openehr.rm'
end
def data_context
# I'm guessing this bit is a kind of 'capability statement' that the parser will use
inputs = {
cancer_diagnosis: "Terminology_code",
has_metastasis: "Boolean",
er_positive: "Boolean",
pr_positive: "Boolean",
her2_positive: "Boolean",
ki67: "Real",
tnm_t: "Boolean",
tnm_n: "Boolean",
tnm_g: "String",
has_dx_transmural_mi: "Boolean",
ejection_fraction: "Real",
hs_dx_hf_stage_2_4: "Boolean",
}
outputs = { recommendation: "String" }
end
def preconditions
return recommendation = "unsuitable rule for this diagnosis" unless cancer_diagnosis = ICD10::Code([ICD_10::C50|Breast cancer|)]
end
def rule_definition
if has_metastasis
# Luminal A
if tumor_molecular_subtype(er_positive, pr_positive, her2_positive, ki67) = Terminology.code("[1111|luminal A|]") then recommendation = "rec1"
# Luminal B (HER2 negative)
elsif tumor_molecular_subtype (er_positive, pr_positive, her2_positive, ki67) = Terminology.code("[2222|Luminal B (HER2 negative)|]") then recommendation = "rec2"
# Luminal B (HER2 positive)
elsif tumor_molecular_subtype (er_positive, pr_positive, her2_positive, ki67) = Terminology.code("[3333|Luminal B (HER2 positive)|]") then recommendation = "rec3"
# HER2 type
elsif tumor_molecular_subtype (er_positive, pr_positive, her2_positive, ki67) = Terminology.code("[4444|HER2|]") then recommendation = "rec4"
# Triple negative
elsif tumor_molecular_subtype (er_positive, pr_positive, her2_positive, ki67) = Terminology.code("[55555|Triple negative|]") then recommendation = "rec5"
end
return recommendation
else
# ruby has a 'case...when' switch statement which avoids too much madness with nested if..then..else..end
case tumor_molecular_subtype (er_positive, pr_positive, her2_positive, ki67)
when Terminology.code([1111|luminal A|]
if tnm_major_number (tnm_t) < 3 && tnm_major_number (tnm_n) < 2 && tnm_major_number (tnm_g) < 3 then
recommendation = :no_intervention_message
else
# consider contraindications to anthracyclines
if has_dx_transmural_mi || ejection_fraction < 0.4 || hs_dx_hf_stage_2_4 then
recommendation = :recommend_cmf_message
else
if has_critical_cardio_pathology or age > 75
recommendation = :recommend_apirubicin_cyclophosphamide_message
else
recommendation = :recommend_ac_message
end
end
end
when Terminology.code("[2222|Luminal B (HER2 negative)|]")
if tnm_t = "1a" && tnm_major_number (tnm_n) = 0 then recommendation = "rec6"
when Terminology.code("[3333|Luminal B (HER2 positive)|]")
if tnm_t = "1a" && tnm_major_number (tnm_n) = 0 then recommendation = "rec7"
elsif tnm_t matches {"T1b", "T1c"} && tnm_major_number (tnm_n) = 0 then recommendation = "rec8"
elsif tnm_major_number (tnm_t) matches {|2..4|} && tnm_major_number (tnm_n) > 0 then recommendation = "rec9"
end
when Terminology.code("[4444|HER2|]")
if tnm_t = "1a" && tnm_major_number (tnm_n) = 0 then recommendation = "re10c5"
elsif tnm_t matches {"T1b", "T1c"} && tnm_major_number (tnm_n) = 0 then recommendation = "rec11"
elsif tnm_major_number (tnm_t) matches {|2..4|} && tnm_major_number (tnm_n) > 0 then recommendation = "rec12"
else
end
when Terminology.code("[55555|Triple negative|]")
recommendation = "rec5"
end
return recommendation
end # end of rule_definition
# this utility method returns a terminology based on the inputs about ER status etc
def tumor_molecular_subtype(is_er_positive, is_pr_positive, is_her2_positive, ki67_level)
# these rules are all one-liners in Ruby, although you can have a multiline if..elsif..else..end instead
if is_er_positive && !is_her2_positive && ki67_level < 0.20 then return Terminology.new("[1111|luminal A|]")
# Luminal B (HER2 negative)
elsif is_er_positive && !is_her2_positive && ki67_level >= 0.20 then return Terminology.new("[2222|Luminal B (HER2 negative)|]")
# Luminal B (HER2 positive)
elsif is_er_positive && is_her2_positive then return Terminology.new("[3333|Luminal B (HER2 positive)|]")
# HER2
elsif !is_er_positive && !is_pr_positive && is_her2_positive then return Terminology.new("[4444|HER2|]")
# Triple negative
elsif !is_er_positive && !is_pr_positive && !is_her2_positive then return Terminology.new("[55555|Triple negative|]")
end
end
def terminology
symbol_definitions = AdlBinding.new("<
["en"] = <
["er_positive"] = <
text = <"Oestrogen receptor positive">
description = <"Oestrogen receptor positive">
>
>
>")
end
def data_bindings
content_bindings = AdlBinding.new('<
["openEHR-EHR-OBSERVATION.cancer_investigation.v1"] = <
["er_positive"] = <
target = <"/data/events[id3]/data/items[id5]/value/magnitude">
direction = <"in">
>
></div>
>
>')
query_bindings = AdlBinding.new('<
["https://oncology.health.org/cdr/"] = <
["has_dx_transmural_mi"] = <
query_id = <"dx_transmural_mi">,
parameters = <
["type"] = <"xxx">
>
>
>')
end
def execute
preconditions # execute the preconditions
rule_definition # execute the rule
end
end #end of class definition
@pacharanero
Copy link
Author

This is an example only and is probably not completely valid ruby, but you get the idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment