|
7 | 7 | (:import
|
8 | 8 | [aidbox_sdk.generator CodeGenerator]))
|
9 | 9 |
|
10 |
| -(defn fhir-type->lang-type [fhir-type] |
| 10 | +(defn ->lang-type [fhir-type] |
11 | 11 | (case fhir-type
|
12 | 12 | ;; Primitive Types
|
13 | 13 | "boolean" "bool"
|
14 | 14 | "instant" "str"
|
15 | 15 | "time" "str"
|
16 | 16 | "date" "str"
|
17 | 17 | "dateTime" "str"
|
18 |
| - "decimal" "str" |
| 18 | + "decimal" "float" |
19 | 19 |
|
20 | 20 | "integer" "integer"
|
21 | 21 | "unsignedInt" "integer"
|
22 | 22 | "positiveInt" "integer"
|
23 | 23 |
|
24 |
| - "integer64" "str" |
| 24 | + "integer64" "integer" |
25 | 25 | "base64Binary" "str"
|
26 | 26 |
|
27 | 27 | "uri" "str"
|
|
38 | 38 | ;; else
|
39 | 39 | fhir-type))
|
40 | 40 |
|
41 |
| -(defn url->resource-type [reference] |
| 41 | +(defn url->resource-name [reference] |
42 | 42 | (last (str/split (str reference) #"/")))
|
43 | 43 |
|
44 | 44 | (defn class-name
|
45 | 45 | "Generate class name from schema url."
|
46 | 46 | [url]
|
47 |
| - (uppercase-first-letter (url->resource-type url))) |
| 47 | + (uppercase-first-letter (url->resource-name url))) |
48 | 48 |
|
49 | 49 | (defn generate-deps [deps]
|
50 | 50 | (->> deps
|
|
55 | 55 | (str/join "\n")))
|
56 | 56 |
|
57 | 57 | (defn package->directory
|
58 |
| - "Generate directory name from package name. |
59 |
| - hl7.fhir.r4.core#4.0.1 -> hl7-fhir-r4-core" |
| 58 | + "Generates directory name from package name. |
| 59 | +
|
| 60 | + Example: |
| 61 | + hl7.fhir.r4.core -> hl7-fhir-r4-core" |
60 | 62 | [x]
|
61 |
| - (-> x |
62 |
| - (str/replace #"[\.#]" "-"))) |
| 63 | + (str/replace x #"[\.#]" "-")) |
63 | 64 |
|
64 | 65 | (defn resource-file-path [ir-schema]
|
65 | 66 | (io/file (package->directory (:package ir-schema))
|
|
69 | 70 | "")
|
70 | 71 |
|
71 | 72 | (defn generate-property
|
72 |
| - "Generate class property from schema element." |
| 73 | + "Generates class property from schema element." |
73 | 74 | [element]
|
74 |
| - (let [type (str |
75 |
| - () |
76 |
| - (fhir-type->lang-type |
77 |
| - (:original-type element)) |
78 |
| - (when (:array element) "[]") |
79 |
| - (when (and (not (:required element)) |
80 |
| - (not (:literal element))) "?")) |
81 |
| - name (->snake-case (:name element))] |
| 75 | + (let [name (->snake-case (:name element)) |
| 76 | + lang-type (->lang-type (:type element)) |
| 77 | + type (str |
| 78 | + (cond |
| 79 | + ;; required and array |
| 80 | + (and (:required element) |
| 81 | + (:array element)) |
| 82 | + (format "List[%s]" lang-type) |
| 83 | + |
| 84 | + ;; not required and array |
| 85 | + (and (not (:required element)) |
| 86 | + (:array element)) |
| 87 | + (format "Optional[List[%s]]" lang-type) |
| 88 | + |
| 89 | + ;; required and not array |
| 90 | + (and (:required element) |
| 91 | + (not (:array element))) |
| 92 | + lang-type |
| 93 | + |
| 94 | + ;; not required and not array |
| 95 | + (and (not (:required element)) |
| 96 | + (not (:array element))) |
| 97 | + (format "Optional[%s]" lang-type))) |
| 98 | + |
| 99 | + default-value (cond |
| 100 | + (not (:required element)) |
| 101 | + "None" |
| 102 | + |
| 103 | + (and (:required element) |
| 104 | + (:array element)) |
| 105 | + "[]" |
| 106 | + |
| 107 | + :else nil)] |
| 108 | + |
82 | 109 | (if (contains? element :choices)
|
83 | 110 | (generate-polymorphic-property element)
|
84 |
| - (str name ": " type (when-not (:required element) " = None"))))) |
85 |
| - |
86 |
| -(defn generate-class [schema & [inner-classes]] |
87 |
| - (let [base-class (url->resource-type (:base schema)) |
88 |
| - schema-name (or (:url schema) (:name schema)) |
89 |
| - generic (when (= (:type schema) "Bundle") "<T>") |
| 111 | + (str name ": " type (when default-value (str " = " default-value)))))) |
| 112 | + |
| 113 | +(defn generate-class |
| 114 | + "Generates Python class from IR (intermediate representation) schema." |
| 115 | + [ir-schema & [inner-classes]] |
| 116 | + (let [base-class (url->resource-name (:base ir-schema)) |
| 117 | + schema-name (or (:url ir-schema) (:name ir-schema)) |
| 118 | + generic (when (= (:type ir-schema) "Bundle") "<T>") |
90 | 119 | class-name' (class-name (str schema-name generic))
|
91 |
| - elements (->> (:elements schema) |
| 120 | + elements (->> (:elements ir-schema) |
92 | 121 | (map #(if (and (= (:base %) "Bundle_Entry")
|
93 | 122 | (= (:name %) "resource"))
|
94 | 123 | (assoc % :value "T")
|
95 | 124 | %)))
|
96 | 125 | properties (->> elements
|
| 126 | + (sort-by :name) |
97 | 127 | (map generate-property)
|
98 | 128 | (map u/add-indent)
|
99 | 129 | (str/join "\n"))
|
100 |
| - base-class (cond |
101 |
| - (= base-class "DomainResource") "DomainResource, IResource" |
102 |
| - :else base-class) |
103 | 130 | base-class-name (when-not (str/blank? base-class)
|
104 | 131 | (uppercase-first-letter base-class))]
|
105 |
| - |
106 |
| - (str "class " class-name' "(" base-class-name "):" |
107 |
| - (when-not (str/blank? properties) |
108 |
| - "\n") |
109 |
| - properties |
110 |
| - (when (and inner-classes |
111 |
| - (seq inner-classes)) |
112 |
| - "\n\n") |
113 |
| - (str/join "\n\n" (map #(->> % str/split-lines (map u/add-indent) (str/join "\n")) inner-classes)) |
114 |
| - "\n}"))) |
| 132 | + (str |
| 133 | + (str/join "\n\n" (map #(->> % str/split-lines (map u/add-indent) (str/join "\n")) inner-classes)) |
| 134 | + "class " class-name' "(" base-class-name "):" |
| 135 | + (when-not (str/blank? properties) |
| 136 | + "\n") |
| 137 | + properties |
| 138 | + (when (and inner-classes |
| 139 | + (seq inner-classes)) |
| 140 | + "\n\n")))) |
115 | 141 |
|
116 | 142 | (defn generate-module
|
117 | 143 | [& {:keys [deps classes]
|
|
122 | 148 | (flatten)
|
123 | 149 | (str/join "\n")))
|
124 | 150 |
|
125 |
| -(defn generate-backbone-classes [ir-schema] |
| 151 | +(defn generate-backbone-classes |
| 152 | + "Generates classes from schema's backbone elements." |
| 153 | + [ir-schema] |
126 | 154 | (->> (ir-schema :backbone-elements)
|
127 | 155 | (map #(assoc % :base "BackboneElement"))
|
128 | 156 | (map generate-class)))
|
|
146 | 174 | (generate-resource-module [_ ir-schema]
|
147 | 175 | {:path (resource-file-path ir-schema)
|
148 | 176 | :content (generate-module
|
149 |
| - :deps [{:module "..base" :members ["*"]}] |
| 177 | + :deps [{:module "pydantic" :members ["*"]} |
| 178 | + {:module "typing" :members ["Optional" "List"]} |
| 179 | + {:module "..base" :members ["*"]}] |
150 | 180 | :classes [(generate-class ir-schema
|
151 | 181 | (generate-backbone-classes ir-schema))])})
|
152 | 182 | (generate-search-params [_ search-schemas fhir-schemas])
|
|
0 commit comments