Skip to content

Commit

Permalink
Add tty prop to rxnorm concept, impl zen project stuff generation for…
Browse files Browse the repository at this point in the history
… rxnorm

Co-authored-by: ApricotLace <[email protected]>
Co-authored-by: Panthevm <[email protected]>
  • Loading branch information
3 people committed Jan 11, 2024
1 parent 2495fc0 commit 9ad48ab
Show file tree
Hide file tree
Showing 2 changed files with 337 additions and 3 deletions.
8 changes: 5 additions & 3 deletions src/ftr/extraction/rxnorm.clj
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,8 @@
:with {:preparations
{:ql/type :pg/select
:select {:suppress :suppress
:rxcui :rxcui
:rxcui :rxcui
:tty [:pg/sql "jsonb_agg(tty order by CASE WHEN tty='SCD' THEN 110 WHEN tty='SCDG' THEN 120 WHEN tty='SCDF' THEN 130 WHEN tty='SCDC' THEN 140 WHEN tty='SBD' THEN 210 WHEN tty='SBDG' THEN 220 WHEN tty='SBDF' THEN 230 WHEN tty='SBDC' THEN 240 WHEN tty='MIN' THEN 310 WHEN tty='PIN' THEN 320 WHEN tty='IN' THEN 330 WHEN tty='GPCK' THEN 410 WHEN tty='BPCK' THEN 420 WHEN tty='PSN' THEN 510 WHEN tty='SY' THEN 520 WHEN tty='TMSY' THEN 530 WHEN tty='BN' THEN 610 WHEN tty='DF' THEN 710 WHEN tty='ET' THEN 720 WHEN tty='DFG' THEN 730 ELSE NULL END)"]
:displays [:pg/sql "jsonb_agg(str order by CASE WHEN tty='SCD' THEN 110 WHEN tty='SCDG' THEN 120 WHEN tty='SCDF' THEN 130 WHEN tty='SCDC' THEN 140 WHEN tty='SBD' THEN 210 WHEN tty='SBDG' THEN 220 WHEN tty='SBDF' THEN 230 WHEN tty='SBDC' THEN 240 WHEN tty='MIN' THEN 310 WHEN tty='PIN' THEN 320 WHEN tty='IN' THEN 330 WHEN tty='GPCK' THEN 410 WHEN tty='BPCK' THEN 420 WHEN tty='PSN' THEN 510 WHEN tty='SY' THEN 520 WHEN tty='TMSY' THEN 530 WHEN tty='BN' THEN 610 WHEN tty='DF' THEN 710 WHEN tty='ET' THEN 720 WHEN tty='DFG' THEN 730 ELSE NULL END)"]}
;; ^--- RXCONSO might contain multiple entries for a single RXCUI value. These entries
;; represent various additional variations of the same atom. We empirically determine
Expand All @@ -320,7 +321,8 @@
^:pg/obj {:other-display [:cond
[:= ^:pg/fn[:jsonb_array_length [:- :preparations.displays 0]] 0] nil
[:- :preparations.displays 0]]
:suppressible-flag :preparations.suppress}
:suppressible-flag :preparations.suppress
:tty :preparations.tty}

{:ql/type :pg/cte
:with {:attrs
Expand All @@ -340,7 +342,7 @@
[:= :rxcui :preparations.rxcui]
[:in :atn [:pg/inplace-params-list (:single rxnsat-atn-collections)]]]}}}}
:select {:ql/type :pg/select
:select {:attrs_res [:pg/coalesce ^:pg/fn[:jsonb_object_agg :name :value] ^:pg/obj {}]}
:select {:attrs_res [:pg/coalesce ^:pg/fn[:jsonb_object_agg ^:pg/fn[:lower :name] :value] ^:pg/obj {}]}
:from :attrs}}

{:ql/type :pg/cte
Expand Down
332 changes: 332 additions & 0 deletions src/gen.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,332 @@
(ns gen
(:require
[next.jdbc :as jdbc]
[ftr.extraction.rxnorm]
[dsql.pg :as dsql]
[clojure.string :as str]))

(comment ;Samples
'{Concept
{:zen/tags #{zen/schema zen.fhir/base-schema}
:confirms #{zen.fhir/Resource}
:zen.fhir/type "Concept"
:zen.fhir/version "0.5.11"
:type zen/map
:require #{:system :code},
:validation-type :open
:keys {:resourceType {:const {:value "Concept"}}
:deprecated {:type zen/boolean
:zen.fhir/type "boolean"}
:system {:type zen/string
:zen.fhir/type "string"}
:code {:type zen/string
:zen.fhir/type "string"}
:display {:type zen/string
:zen.fhir/type "string"}
:definition {:type zen/string
:zen.fhir/type "string"}
:ancestors {:type zen/map
:validation-type :open}
:hierarchy {:type zen/map
:validation-type :open}
:valueset {:type zen/vector
:every {:type zen/string
:zen.fhir/type "string"}}
:property {:type zen/map
:validation-type :open
:keys
{:has_ingredient ; relation (for all)
{:type zen/vector
:every {:type zen/map
:keys {:code {:type zen/string
:zen.fhir/type "string"}
:display {:type zen/string
:zen.fhir/type "string"}}}}
:rxn_human_drug ;attr single
{:type zen/string
:zen.fhir/type "string"}

:rxn_am
{:type zen/vector
:every {:type zen/string
:zen.fhir/type "string"}}}}}}

has-ingredient-code
{:zen/tags #{aidbox.search-parameter.v1/search-parameter}
:name "hasIngredientCode"
:type :token
:resource {:resourceType "Entity" :id "Concept"}
:expression [["property" "has_ingredient" "code"]]}

has-ingredient-display
{:zen/tags #{aidbox.search-parameter.v1/search-parameter}
:name "hasIngredientDisplay"
:type :string
:resource {:resourceType "Entity" :id "Concept"}
:expression [["property" "has_ingredient" "display"]]}

has-ingredient-code-index
{:zen/tags #{aidbox.index.v1/auto-index}
:for has-ingredient-code}

has-ingredient-display-index
{:zen/tags #{aidbox.index.v1/auto-index}
:for has-ingredient-display}

concept-repository
{:zen/tags #{aidbox.repository.v1/repository}
:resourceType "Concept"
:base-profile Concept
:indexes #{has-ingredient-code-index has-ingredient-display-index}
:extra-parameter-sources :all
:search-parameters #{has-ingredient-code has-ingredient-display}}
})

(def rxnsat-atn-collections
{:all ["AMBIGUITY_FLAG"
"NDC"
"ORIG_CODE"
"ORIG_SOURCE"
"RXN_ACTIVATED"
"RXN_AI"
"RXN_AM"
"RXN_AVAILABLE_STRENGTH"
"RXN_BN_CARDINALITY"
"RXN_BOSS_FROM"
"RXN_BOSS_STRENGTH_DENOM_UNIT"
"RXN_BOSS_STRENGTH_DENOM_VALUE"
"RXN_BOSS_STRENGTH_NUM_UNIT"
"RXN_BOSS_STRENGTH_NUM_VALUE"
"RXN_HUMAN_DRUG"
"RXN_IN_EXPRESSED_FLAG"
"RXN_OBSOLETED"
"RXN_QUALITATIVE_DISTINCTION"
"RXN_QUANTITY"
"RXN_STRENGTH"
"RXN_VET_DRUG"
"RXTERM_FORM"]

:multi ["ORIG_SOURCE"
"RXN_AM"
"RXN_BOSS_FROM"
"AMBIGUITY_FLAG"
"RXN_AVAILABLE_STRENGTH"
"RXN_OBSOLETED"
"RXN_ACTIVATED"
"ORIG_CODE"
"RXN_AI"]

:single ["RXN_IN_EXPRESSED_FLAG"
"RXN_HUMAN_DRUG"
"RXN_BOSS_STRENGTH_DENOM_VALUE"
"RXN_BOSS_STRENGTH_NUM_VALUE"
"RXN_QUANTITY"
"RXN_BOSS_STRENGTH_NUM_UNIT"
"RXN_BOSS_STRENGTH_DENOM_UNIT"
"RXN_BN_CARDINALITY"
"RXN_STRENGTH"
"RXN_VET_DRUG"
"RXTERM_FORM"
"RXN_QUALITATIVE_DISTINCTION"]})

(def helpful-map
(let [connection (jdbc/get-connection "jdbc:postgresql://localhost:5125/ftr?user=ftr&password=password")
multi-attrs (:multi rxnsat-atn-collections)
single-attrs (:single rxnsat-atn-collections)
rels (->>
(dsql/format {:ql/type :pg/select
:select {:rel [:distinct :rela]}
:from :rxnrel})
(jdbc/execute! connection)
(map #(get % :rxnrel/rel))
(filter #(not (nil? %))))
rels (->> rels
(reduce
(fn [acc value]
(assoc acc (keyword (str/lower-case value)) :multi-rel))
{}))
multi-attrs (->> multi-attrs
(reduce
(fn [acc value]
(assoc acc (keyword (str/lower-case value)) :multi-attr))
{}))
single-attrs (->> single-attrs
(reduce
(fn [acc value]
(assoc acc (keyword (str/lower-case value)) :single-attr))
{}))
attrs (merge rels multi-attrs single-attrs)]
attrs))

(defn to-zen-name [kw & [postfix]]
(let [res (-> kw
(str)
(str/replace-first #":" "")
(str/lower-case)
(str/replace #"_" "-"))]
(if postfix
(symbol (str res "-" postfix))
(symbol res))))

(defn to-camel-name [input-string & [postfix]]
(let [input-string (str/replace-first (str input-string) #":" "")
words (str/split input-string #"[\s_-]+")
res (str/join "" (cons (str/lower-case (first words)) (map str/capitalize (rest words))))]
(if postfix
(str res (str/capitalize postfix))
res)))

(defn kw-to-string [kw]
(-> kw
(str)
(str/replace-first #":" "")
))

(defn generate-concept-schema []
(let [all-keys (reduce
(fn [acc [k v]]
(case v
:single-attr
(assoc acc
(keyword (str/lower-case (kw-to-string k)))
'{:type zen/map
:keys {:type zen/string
:zen.fhir/type "string"}})
:multi-attr
(assoc acc
(keyword (str/lower-case (kw-to-string k)))
'{:type zen/vector
:every {:type zen/string
:zen.fhir/type "string"}})
:multi-rel
(assoc acc
(keyword (str/lower-case (kw-to-string k)))
'{:type zen/vector
:every {:type zen/map
:keys {:code {:type zen/string
:zen.fhir/type "string"}
:display {:type zen/string
:zen.fhir/type "string"}}}})))
{}
helpful-map)]
`{:zen/tags #{zen/schema zen.fhir/base-schema}
:confirms #{zen.fhir/Resource}
:zen.fhir/type "Concept"
:zen.fhir/version "0.5.11"
:type zen/map
:require #{:system :code},
:validation-type :open
:keys {:resourceType {:const {:value "Concept"}}
:deprecated {:type zen/boolean
:zen.fhir/type "boolean"}
:system {:type zen/string
:zen.fhir/type "string"}
:code {:type zen/string
:zen.fhir/type "string"}
:display {:type zen/string
:zen.fhir/type "string"}
:definition {:type zen/string
:zen.fhir/type "string"}
:ancestors {:type zen/map
:validation-type :open}
:hierarchy {:type zen/map
:validation-type :open}
:valueset {:type zen/vector
:every {:type zen/string
:zen.fhir/type "string"}}
:property {:type zen/map
:validation-type :open
:keys ~all-keys}}}
))


(defn generate-search-schemas []
(let [all-keys (reduce
(fn [acc [k v]]
(case v
:single-attr
(assoc acc
(to-zen-name k)
`{:zen/tags #{aidbox.search-parameter.v1/search-parameter}
:name ~(to-camel-name k)
:type :string
:resource {:resourceType "Entity" :id "Concept"}
:expression [["property" ~(kw-to-string k)]]})
:multi-attr
(assoc acc
(to-zen-name k)
`{:zen/tags #{aidbox.search-parameter.v1/search-parameter}
:name ~(to-camel-name k)
:type :string
:resource {:resourceType "Entity" :id "Concept"}
:expression [["property" ~(kw-to-string k)]]})
:multi-rel
(assoc acc
(to-zen-name k "code")
`{:zen/tags #{aidbox.search-parameter.v1/search-parameter}
:name ~(to-camel-name "code")
:type :token
:resource {:resourceType "Entity" :id "Concept"}
:expression [["property" ~(kw-to-string k) "code"]]}
(to-zen-name k "display")
`{:zen/tags #{aidbox.search-parameter.v1/search-parameter}
:name ~(to-camel-name "display")
:type :string
:resource {:resourceType "Entity" :id "Concept"}
:expression [["property" ~(kw-to-string k) "display"]]})))
{}
helpful-map)]
all-keys))

(defn generate-index-schemas []
(let [all-keys (reduce
(fn [acc [k v]]
(case v
:single-attr
(assoc acc
(to-zen-name k "index")
`{:zen/tags #{aidbox.index.v1/auto-index}
:for ~(to-zen-name k)})
:multi-attr
(assoc acc
(to-zen-name k "index")
`{:zen/tags #{aidbox.index.v1/auto-index}
:for ~(to-zen-name k)})
:multi-rel
(assoc acc
(to-zen-name k "code-index")
`{:zen/tags #{aidbox.index.v1/auto-index}
:for ~(to-zen-name k "code")}
(to-zen-name k "display-index")
`{:zen/tags #{aidbox.index.v1/auto-index}
:for ~(to-zen-name k "display")})))
{}
helpful-map)]
all-keys))

(comment
(generate-concept-schema)

(generate-search-schemas)

(generate-index-schemas)

(merge
`{~(symbol nil "Concept")
~(generate-concept-schema)

~(symbol nil "concept-repository")
{:zen/tags #{aidbox.repository.v1/repository}
:resourceType "Concept"
:base-profile ~(symbol nil "Concept")
:indexes ~(-> (generate-index-schemas)
keys
set)
:extra-parameter-sources :all
:search-parameters ~(-> (generate-search-schemas)
keys
set)}}
(generate-index-schemas)
(generate-search-schemas))

)

0 comments on commit 9ad48ab

Please sign in to comment.