Skip to content

Commit

Permalink
profiles to classes
Browse files Browse the repository at this point in the history
  • Loading branch information
GenaRazmakhnin committed Dec 26, 2023
1 parent 6e82589 commit a782b3e
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/python-generator/helpers.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
(:require
[python-generator.extractor]
[cheshire.core]
[taoensso.nippy :as nippy]
[clojure.java.io :as io]
[clojure.string :as str]))

Expand Down Expand Up @@ -81,6 +82,10 @@
(mapv (fn [json-row]
(cheshire.core/parse-string json-row keyword))))))

(defn parse-nippy [path]
(->> (io/file path)
(nippy/thaw-from-file)))

(defn side-effect-map [method, list] (doall (map method list)))

(defn create-on-missing [dir]
Expand Down
63 changes: 63 additions & 0 deletions src/python-generator/profile-extractor.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
(ns python-generator.profile-extractor
(:require [cheshire.core] [clojure.string :as str]))


(defn filter-base [reduce-keys]
(->> (reduce-keys :structures)
(filter #(not (and (contains? % :from) (contains? % :to))))
(filter #(not (contains? % :derivation)))
(filter #(not (contains? % :base)))
(filter #(contains? % :elements))
(map (fn [item] [(item :fqn) item]))
(into {})
(assoc {} :base)
(hash-map :structures (reduce-keys :structures) :accumulator)))

(defn filter-element [reduce-keys]
(->> (reduce-keys :structures)
(filter #(str/includes? (or (:base %) "") "Element"))
(filter #(not (str/includes? (or (:base %) "") "BackboneElement")))
(filter #(not (str/includes? (or (:base %) "") "ElementDefinition")))
(filter #(contains? % :elements))
(map (fn [item] [(item :fqn) item]))
(into {})
(assoc (reduce-keys :accumulator) :element)
(hash-map :structures (reduce-keys :structures) :accumulator)))

(defn filter-backbone-element [reduce-keys]
(->> (reduce-keys :structures)
(filter #(str/includes? (or (:base %) "") "/BackboneElement"))
(filter #(= "specialization" (:derivation %)))
(map (fn [item] [(item :fqn) item]))
(into {})
(assoc (reduce-keys :accumulator) :backbone-element)
(hash-map :structures (reduce-keys :structures) :accumulator)))

(defn filter-domain-resource [reduce-keys]
(->> (reduce-keys :structures)
(filter #(str/includes? (or (:base %) "") "/DomainResource"))
(filter #(= "specialization" (:derivation %)))
(map (fn [item] [(item :fqn) item]))
(into {})
(assoc (reduce-keys :accumulator) :domain-resource)
(hash-map :structures (reduce-keys :structures) :accumulator)))

(defn filter-resource [reduce-keys]
(->> (reduce-keys :structures)
(filter #(str/includes? (or (:base %) "") "/Resource"))
(filter #(= "specialization" (:derivation %)))
(map (fn [item] [(item :fqn) item]))
(into {})
(assoc (reduce-keys :accumulator) :resource)
(hash-map :structures (reduce-keys :structures) :accumulator)))

(defn filter-constraint [reduce-keys]
(->> (reduce-keys :structures)
(filter #(= "constraint" (:derivation %)))
(filter #(not (= "hl7.fhir.r4.core#4.0.1/Extension" (:base %))))
(map (fn [item] [(item :fqn) item]))
(into {})
(assoc (reduce-keys :accumulator) :constraint)
(hash-map :structures (reduce-keys :structures) :accumulator)))


98 changes: 98 additions & 0 deletions src/python-generator/profile-helpers.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
(ns python-generator.profile-helpers
(:require
[python-generator.extractor]
[cheshire.core]
[taoensso.nippy :as nippy]
[clojure.java.io :as io]
[clojure.string :as str]))


;; TODO: do not hardcode
(def elements #{"HumanName" "Signature" "Range" "Coding" "Attachment" "BackboneElement" "Address" "Money" "Period" "Expression" "TriggerDefinition" "Contributor" "Identifier" "Extension" "Quantity" "RelatedArtifact" "Ratio" "UsageContext" "ContactPoint" "Narrative" "Meta" "SampledData" "Annotation" "Reference" "CodeableConcept" "ContactDetail" "ParameterDefinition" "DataRequirement"})
(def backbone-elements #{"Population" "Timing" "MarketingStatus" "SubstanceAmount" "ProductShelfLife" "ProdCharacteristic" "Dosage" "ElementDefinition"})
(def primitives-string #{"dateTime" "xhtml" "Distance" "time" "date" "string" "uuid" "oid" "id" "Dosage" "Duration" "instant" "Count" "decimal" "code" "base64Binary" "unsignedInt" "url" "markdown" "uri" "positiveInt" "canonical" "Age" "Timing"})

(defn uppercase-first-letter [string]
(str (str/upper-case (first string)) (subs string 1)))

(defn escape-keyword [word]
(if (.contains #{"class", "from", "assert", "global", "for", "import"} word) (str word "_") word))

(defn string-interpolation [left, right, string]
(str left, string, right))

(defn wrap-vector [string]
(string-interpolation "list[", "]", string))

(defn wrap-optional [string]
(string-interpolation "Optional[", "]", string))

(defn get-resource-name [reference]
(last (str/split (str reference) #"/")))

(defn get-type [name type]
(cond
(= type "BackboneElement") (str "" (uppercase-first-letter name))
(= type "boolean") "bool"
(= type "integer") "int"
(= type "") "str"
(.contains primitives-string type) "str"
:else (or type "str")))

(defn derive-basic-type [name element]
(get-type name (get-resource-name (:type element))))

(defn append-default-none [string] (str string " = None"))
(defn append-default-vector [string] (str string " = []"))

(defn transform-element [name element required]
(->> (derive-basic-type name element)
((if (:array element) wrap-vector str))
((if (and (not required) (not (:array element))) wrap-optional str))
((if (and (not required) (not (:array element))) append-default-none str))
((if (and (not required) (:array element)) append-default-vector str))))

(defn elements-to-vector [definition]
(->> (seq (:elements definition))
(filter (fn [[_, v]] (not (contains? v :choices))))))

(defn get-parent [base-reference]
(->> (get-resource-name base-reference)
(string-interpolation "(" ")")))

(defn collect-types [parent_name, required, [k, v]]
(hash-map :name (escape-keyword (name k)) :base parent_name :value (transform-element (str parent_name "_" (uppercase-first-letter (name k))) v (.contains required (name k)))))

(defn resolve-backbone-elements [[k, v]]
(if (= (get-resource-name (:type v)) "BackboneElement") (vector k, v) (vector)))

(defn get-typings-and-imports [parent_name, required, data]
(reduce (fn [acc, item]
(hash-map :elements (conj (:elements acc) (collect-types parent_name required item))
:backbone-elements (conj (:backbone-elements acc) (resolve-backbone-elements item))))
(hash-map :elements [] :backbone-elements []) data))

(defn parse-ndjson-gz [path]
(with-open [rdr (-> path
(io/input-stream)
(java.util.zip.GZIPInputStream.)
(io/reader))]
(->> rdr
line-seq
(mapv (fn [json-row]
(cheshire.core/parse-string json-row keyword))))))

(defn parse-nippy [path]
(->> (io/file path)
(nippy/thaw-from-file)))

(defn side-effect-map [method, list] (doall (map method list)))

(defn create-on-missing [dir]
(when-not (.exists (io/file dir)) (.mkdir (io/file dir))))

(defn write-to-file [directory, filename, text]
(create-on-missing directory)
(with-open [writer (io/writer (io/file directory (str filename ".py")))] (.write writer text)))

;; (get-resource-name filename)
146 changes: 146 additions & 0 deletions src/python-generator/profile-parser.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
(ns python-generator.profile-parser
(:require
[python-generator.profile-extractor :as ext]
[python-generator.profile-helpers :as help]
[python-generator.profile-resources :as gen]
[cheshire.core]
[clojure.string :as str]))

;; (str parent_name "_" (help/uppercase-first-letter (name property_name)))

(defn compile-backbone [parent_name property_name definition]
(let [name "NaMe"
data (help/get-typings-and-imports name (or (:required definition) []) (help/elements-to-vector definition))
backbone-elements (filter (fn [item] (> (count item) 0)) (:backbone-elements data))]
(conj data (hash-map :backbone-elements (if (= (count backbone-elements) 0) [] (map (fn [[k, v]] (compile-backbone name k v)) backbone-elements))))))

(defn test [name data]
(->> (filter (fn [item] (> (count item) 0)) (:backbone-elements data))
(map (fn [[k, v]] (compile-backbone name k v)))
(hash-map :backbone-elements)
(conj data)))



;; (helpers/get-resource-name (:type definition))

(defn attach-parent-data [parent a context child]
(if (nil? parent) child (conj child (hash-map :elements (concat (get-in context [:classes parent a :elements]) (:elements child))))))

(defn compile-elements [property parent context]
(->> (map (fn [[name definition]]
(->> (help/elements-to-vector definition)
(help/get-typings-and-imports (:type definition) (or (:required definition) []))
(test "ktulh-ftagn")
(attach-parent-data parent (:base definition) context)
(conj (hash-map :name name :source (:base definition)))
(hash-map name))) (property (:accumulator context)))
#_(apply merge)))


(defn group-by [ig]
(->> (ext/filter-base ig)
(ext/filter-element)
(ext/filter-resource)
(ext/filter-backbone-element)
(ext/filter-domain-resource)
(ext/filter-constraint)))

(defn c-base [context]
(->> context
(compile-elements :base nil)
(hash-map :base)
(hash-map :classes)
(conj context)))

(defn c-resource [context]
(->> context
(compile-elements :resource :base)
(hash-map :resource)
(conj (:classes context))
(hash-map :classes)
(conj context)))

(defn c-domain-resource [context]
(->> context
(compile-elements :domain-resource :resource)
(hash-map :domain-resource)
(conj (:classes context))
(hash-map :classes)
(conj context)))

(defn c-constraint [context]
(->> context
(compile-elements :constraint :domain-resource)
(hash-map :constraint)
(conj (:classes context))
(hash-map :classes)
(conj context)))

(defn compile [context]
(->> context
(c-base)
(c-resource)
(c-domain-resource)
(c-constraint)))

(defn compile-profiles []
(->> (hash-map :structures (help/parse-ndjson-gz "/Users/gena.razmakhnin/Documents/aidbox-sdk-js/fhir-schema/hl7.fhir.r4.core#4.0.1/package.ndjson.gz"))
(group-by)
(compile)
(:classes)
(:domain-resource)))

(compile-profiles)








;; (filter (fn [item] (:elements item)) [{:elements {:a {:type 1} :b {:type 2} :c {:type 2}}}])

#_(->> (helpers/parse-ndjson-gz "/Users/gena.razmakhnin/Documents/aidbox-python-tooklit/fhir-schema/hl7.fhir.r4.core#4.0.1_package.ndjson.gz")
(filter #(= "constraint" (:derivation %)))
(filter (fn [item] ()))
(filter (fn [item]
(some
(fn [[_ value]] (and (= "hl7.fhir.r4.core#4.0.1/CodeableConcept" (:type value)) (contains? value :binding)))
(:elements item)))))

;; (map (fn [[key value]] key) {"hello" 3 "hello2" 3})
;; (type (hash-map "hello" 3 "hello2" 3))
;; ((hash-map "hello" 1 "hello2" 2) "hello2")

#_(->> (helpers/parse-nippy "/Users/gena.razmakhnin/Documents/aidbox-python-tooklit/fhir-schema/hl7.fhir.r4.core#4.0.1_terminology-index.nippy")
#_(:valuesets)
(:codesystems)
#_(map (fn [[key value]] value))
#_(filter (fn [[key value]] (= key "http://terminology.hl7.org/CodeSystem/v2-0334")))
((fn [map] (map "http://loinc.org"))))

;; (map (fn [item] (print item)))
;; (keyword "http://hl7.org/fhir/supplydelivery-status")
;; (keyword "http://hl7.org/fhir/ValueSet/ldlcholesterol-codes")


;; :fhirVersions ["4.0.1"]
;; :name "hl7.fhir.r4.core"
;; :type "fhir.core"
;; :version "4.0.1"
;; :dependencies []

;; :fhirVersions ["4.0.1"]
;; :name "hl7.fhir.us.core"
;; :type "IG"
;; :version "5.0.1"
;; :dependencies [":hl7.fhir.r4.core#4.0.1" ":hl7.terminology.r4#3.1.0" ":hl7.fhir.uv.bulkdata#2.0.0" ":hl7.fhir.uv.smart-app-launch#2.0.0" ":us.nlm.vsac#0.7.0" ":hl7.fhir.uv.sdc#3.0.0"]

;; :fhirVersions ["4.0.1"]
;; :name "hl7.fhir.us.mcode"
;; :type "IG"
;; :version "3.0.0"
;; :dependencies [":hl7.fhir.r4.core#4.0.1" ":hl7.terminology.r4#5.3.0" ":hl7.fhir.us.core#5.0.1" ":hl7.fhir.uv.genomics-reporting#2.0.0" ":hl7.fhir.uv.extensions#1.0.0"]

37 changes: 37 additions & 0 deletions src/python-generator/profile-resources.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
(ns python-generator.profile-resources
(:require
[cheshire.core]
[clojure.string :as str]
[python-generator.extractor :as extractors]
[python-generator.profile-helpers :as help]))

(defn filter-backbone-elements [data]
(filter (fn [item] (> (count item) 0)) (:backbone-elements data)))

(defn compile-backbone [parent_name property_name definition]
(let [name (str parent_name "_" (help/uppercase-first-letter (name property_name)))
data (help/get-typings-and-imports name (or (:required definition) []) (help/elements-to-vector definition))
backbone-elements (filter-backbone-elements data)]
(->> (str (clojure.string/join "\n" (:elements data)) "\n\n")
(str "class " name "(BackboneElement):\n")
(str (if (= (count backbone-elements) 0) (str) (str/join (map (fn [[k, v]] (str (compile-backbone name k v))) backbone-elements)))))))

(defn test [name data]
(->> (filter (fn [item] (> (count item) 0)) (:backbone-elements data))
(map (fn [[k, v]] (str (compile-backbone name k v))))
(str/join)))

(defn combine-file [definition, data]
(->> (str (clojure.string/join "\n" (:elements data)) "\n\n")
(str "class " (help/get-resource-name (:type definition)) (help/get-parent (:base definition)) ":\n")
#_(str (test (help/get-resource-name (:type definition)) data))
#_(str "from ..base import *\n\n")
#_(str "from typing import Optional\n")))

(defn compile-single-class [directory]
(fn [[_ definition]]
(->> (help/elements-to-vector definition)
(help/get-typings-and-imports (help/get-resource-name (:type definition)) (or (:required definition) []))
#_(combine-file definition)
#_(str/join))))

0 comments on commit a782b3e

Please sign in to comment.