Skip to content

Commit

Permalink
[WIP] Implement ValueSet References
Browse files Browse the repository at this point in the history
Closes: #110
  • Loading branch information
alexanderkiel committed Nov 28, 2024
1 parent 6342ed6 commit ecf8bda
Show file tree
Hide file tree
Showing 60 changed files with 1,894 additions and 221 deletions.
1 change: 1 addition & 0 deletions .clj-kondo/root/config.edn
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
blaze.middleware.fhir.db db
blaze.rest-api.header header
blaze.scheduler sched
blaze.terminology-service ts
blaze.test-util tu
blaze.util u
buddy.auth auth
Expand Down
21 changes: 21 additions & 0 deletions .github/scripts/value-set-expand/expand.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash -e

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
. "$SCRIPT_DIR/../util.sh"

BASE="http://localhost:8080/fhir"

expand() {
curl -sH "Accept: application/fhir+json" "$BASE/ValueSet/\$expand?url=$1" | jq -r '[.expansion.contains[].code] | join(",")'
}

test "Abrechnungsart" "$(expand "http://fhir.de/ValueSet/dkgev/Abrechnungsart")" "AOP,HSA,PIA,SPZ,ASV,KIA,DRG,PEPP,VNSB,AP,SPB,WLU,WLA,PS,SZ,KV,BG,SL,KEK,IA,MVZ,IV,DMP,REHA,PSY"
test "AbrechnungsDiagnoseProzedur" "$(expand "http://fhir.de/ValueSet/AbrechnungsDiagnoseProzedur")" "hospital-main-diagnosis,principle-DRG,secondary-DRG"
test "Diagnosesubtyp" "$(expand "http://fhir.de/ValueSet/Diagnosesubtyp")" "surgery-diagnosis,department-main-diagnosis,infection-control-diagnosis,cause-of-death,AD,DD"

test "mii-vs-fall-diagnosis-use" "$(expand "https://www.medizininformatik-initiative.de/fhir/core/modul-fall/ValueSet/mii-vs-fall-diagnosis-use")" "referral-diagnosis,treatment-diagnosis,surgery-diagnosis,department-main-diagnosis,infection-control-diagnosis,cause-of-death,AD,DD,CC,CM,pre-op,post-op,billing"
test "identifier-type-codes" "$(expand "https://www.medizininformatik-initiative.de/fhir/core/modul-fall/ValueSet/identifier-type-codes")" "ACSN,BRN,DL,DR,EN,FILL,JHN,MCN,MD,MR,NIIP,PLAC,PPN,PRN,SB,SNO,TAX,UDI,VN"
test "location-physical-type" "$(expand "https://www.medizininformatik-initiative.de/fhir/core/modul-fall/ValueSet/location-physical-type")" "wa,ro,bd"
test "mii-vs-consent-answer" "$(expand "https://www.medizininformatik-initiative.de/fhir/modul-consent/ValueSet/mii-vs-consent-answer")" "2.16.840.1.113883.3.1937.777.24.5.2.3,2.16.840.1.113883.3.1937.777.24.5.2.2,2.16.840.1.113883.3.1937.777.24.5.2.1"
test "mii-vs-consent-policy" "$(expand "https://www.medizininformatik-initiative.de/fhir/modul-consent/ValueSet/mii-vs-consent-policy")" "2.16.840.1.113883.3.1937.777.24.5.3.1,2.16.840.1.113883.3.1937.777.24.5.3.44,2.16.840.1.113883.3.1937.777.24.5.3.48,2.16.840.1.113883.3.1937.777.24.5.3.10,2.16.840.1.113883.3.1937.777.24.5.3.14,2.16.840.1.113883.3.1937.777.24.5.3.18,2.16.840.1.113883.3.1937.777.24.5.3.24,2.16.840.1.113883.3.1937.777.24.5.3.50,2.16.840.1.113883.3.1937.777.24.5.3.54,2.16.840.1.113883.3.1937.777.24.5.3.26,2.16.840.1.113883.3.1937.777.24.5.3.30,2.16.840.1.113883.3.1937.777.24.5.3.32,2.16.840.1.113883.3.1937.777.24.5.3.35"
test "mii-vs-consent-signaturetypes" "$(expand "https://www.medizininformatik-initiative.de/fhir/modul-consent/ValueSet/mii-vs-consent-signaturetypes")" "1.2.840.10065.1.12.1.7,1.2.840.10065.1.12.1.11"
17 changes: 17 additions & 0 deletions .github/scripts/value-set-expand/upload.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash -e

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"

BASE="http://localhost:8080/fhir"

# Upload CodeSystem resources
for file in "$SCRIPT_DIR"/CodeSystem-*.json; do
echo "Uploading $file..."
curl -H "Content-Type: application/fhir+json" -H "Prefer: return=minimal" -d @"$file" "$BASE/CodeSystem"
done

# Upload ValueSet resources
for file in "$SCRIPT_DIR"/ValueSet-*.json; do
echo "Uploading $file..."
curl -H "Content-Type: application/fhir+json" -H "Prefer: return=minimal" -d @"$file" "$BASE/ValueSet"
done
35 changes: 35 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,40 @@ jobs:
- name: Docker Stats
run: docker stats --no-stream

integration-test-value-set-expand:
needs: build
runs-on: ubuntu-24.04

steps:
- name: Check out Git repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4

- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v4

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 9: GitHub-owned GitHubAction not pinned by hash
Click Remediation section below to solve this issue
with:
dotnet-version: 8.0.x

- name: Install Firely Terminal
run: dotnet tool install -g firely.terminal

- name: Install Consent
run: fhir install de.medizininformatikinitiative.kerndatensatz.consent 1.0.7

- name: Download Blaze Image
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
with:
name: blaze-image
path: /tmp

- name: Load Blaze Image
run: docker load --input /tmp/blaze.tar

- name: Run Blaze
run: docker run --name blaze -d -e JAVA_TOOL_OPTIONS=-Xmx2g -p 8080:8080 --read-only --tmpfs /tmp:exec -v blaze-data:/app/data blaze:latest

- name: Wait for Blaze
run: .github/scripts/wait-for-url.sh http://localhost:8080/health

not-enforcing-referential-integrity-test:
needs: build
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -2001,6 +2035,7 @@ jobs:
- integration-test
- integration-test-synthea-1000
- integration-test-patient-purge
- integration-test-value-set-expand
- not-enforcing-referential-integrity-test
- admin-api-test
- small-transactions-test
Expand Down
10 changes: 8 additions & 2 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
blaze/interaction
{:local/root "modules/interaction"}

blaze/openid-auth
{:local/root "modules/openid-auth"}

blaze.operation/compact
{:local/root "modules/operation-compact"}

Expand All @@ -31,8 +34,8 @@
blaze.operation/totals
{:local/root "modules/operation-totals"}

blaze/openid-auth
{:local/root "modules/openid-auth"}
blaze.operation/value-set-expand
{:local/root "modules/operation-value-set-expand"}

blaze/page-store-cassandra
{:local/root "modules/page-store-cassandra"}
Expand All @@ -46,6 +49,9 @@
blaze/server
{:local/root "modules/server"}

blaze/terminology-service
{:local/root "modules/terminology-service"}

blaze/thread-pool-executor-collector
{:local/root "modules/thread-pool-executor-collector"}

Expand Down
1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ The following Operations are implemented:
* [Measure $evaluate-measure](api/operation-measure-evaluate-measure.md)
* [Patient $everything](api/operation-patient-everything.md)
* [Patient $purge](api/operation-patient-purge.md)
* [ValueSet $expand](api/operation-value-set-expand.md)

## Asynchronous Requests

Expand Down
15 changes: 15 additions & 0 deletions docs/api/operation-value-set-expand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Operation \$expand on ValueSet

> [!CAUTION]
> The operation \$expand on ValueSet is currently **beta**. Only a basic functionality is implemented.
The \$expand operation can be used to expand all codes of a ValueSet.

```
GET [base]/ValueSet/$expand
GET [base]/ValueSet/[id]/$expand
```

The official documentation can be found [here][1].

[1]: <http://hl7.org/fhir/R4/valueset-operation-expand.html>
6 changes: 3 additions & 3 deletions docs/conformance/cql.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ The section numbers refer to the documentation of the [ELM Specification](https:
| 3.8. | ConceptRef || | |
| 3.9. | Quantity || | |
| 3.10. | Ratio || | |
| 3.11. | ValueSetDef | | | |
| 3.12. | ValueSetRef | | | |
| 3.11. | ValueSetDef | | | |
| 3.12. | ValueSetRef | | | |

### 4. Type Specifiers

Expand Down Expand Up @@ -373,7 +373,7 @@ The section numbers refer to the documentation of the [ELM Specification](https:
| 23.5. | Equal ||
| 23.6. | Equivalent ||
| 23.7. | InCodeSystem ||
| 23.8. | InValueSet | |
| 23.8. | InValueSet | |
| 23.9. | ExpandValueSet ||
| 23.10. | Not Equal ||
| 23.11. | SubsumedBy ||
Expand Down
3 changes: 3 additions & 0 deletions modules/cql/deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
{blaze/db
{:local/root "../db"}

blaze/terminology-service
{:local/root "../terminology-service"}

com.fasterxml.jackson.module/jackson-module-jaxb-annotations
{:mvn/version "2.18.1"
:exclusions [javax.xml.bind/jaxb-api]}
Expand Down
32 changes: 31 additions & 1 deletion modules/cql/src/blaze/elm/compiler/clinical_operators.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
Section numbers are according to
https://cql.hl7.org/04-logicalspecification.html."
(:require
[blaze.elm.compiler.clinical-operators.impl :as impl]
[blaze.elm.compiler.core :as core]
[blaze.elm.compiler.macros :refer [reify-expr]]
[blaze.elm.protocols :as p]))
[blaze.elm.concept :as concept]
[blaze.elm.protocols :as p]
[blaze.elm.value-set :as value-set]))

;; 23.3. CalculateAge
;;
Expand Down Expand Up @@ -42,3 +45,30 @@
(when-let [date (core/compile* context date)]
(let [chrono-precision (some-> precision core/to-chrono-unit)]
(calculate-age-at-op birth-date date chrono-precision precision)))))

;; 23.8. InValueSet
(defn- in-value-set [code value-set]
(reify-expr core/Expression
(-attach-cache [_ cache]
(core/attach-cache-helper in-value-set cache code value-set))
(-resolve-refs [_ expression-defs]
(core/resolve-refs-helper in-value-set expression-defs code value-set))
(-resolve-params [_ parameters]
(core/resolve-params-helper in-value-set parameters code value-set))
(-optimize [_ db]
(core/optimize-helper in-value-set db code value-set))
(-eval [_ context resource scope]
(let [value-set (core/-eval value-set context resource scope)
code (core/-eval code context resource scope)]
(cond
(string? code) (contains? (into #{} (map :code) value-set) code)
(concept/concept? code) (impl/contains-any? value-set (:codes code))
:else (contains? value-set (value-set/from-code code)))))
(-form [_]
(list 'in-value-set (core/-form code) (core/-form value-set)))))

(defmethod core/compile* :elm.compiler.type/in-value-set
[context {:keys [code] value-set :valueset}]
(let [code (core/compile* context code)]
(when-let [value-set (core/compile* context value-set)]
(in-value-set code value-set))))
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(ns blaze.elm.compiler.clinical-operators.impl
(:require
[blaze.elm.value-set :as value-set]))

(defn contains-any? [value-set [code & codes]]
(cond
(nil? code) false
(contains? value-set (value-set/from-code code)) true
:else (recur value-set codes)))
36 changes: 33 additions & 3 deletions modules/cql/src/blaze/elm/compiler/clinical_values.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
[blaze.anomaly :as ba :refer [throw-anom]]
[blaze.elm.code :as code]
[blaze.elm.compiler.core :as core]
[blaze.elm.compiler.macros :refer [reify-expr]]
[blaze.elm.concept :refer [concept]]
[blaze.elm.date-time :as date-time]
[blaze.elm.quantity :refer [quantity]]
[blaze.elm.ratio :refer [ratio]]))
[blaze.elm.ratio :refer [ratio]]
[blaze.elm.value-set :as value-set]
[blaze.fhir.spec.type :as type]
[blaze.terminology-service :as ts]))

(defn- find-code-system-def
"Returns the code-system-def with `name` from `library` or nil if not found."
Expand Down Expand Up @@ -113,5 +117,31 @@
;; Not needed because it's not an expression.

;; 3.12. ValueSetRef
;;
;; TODO
(defn- find-value-set-def
"Returns the value-set-def with `name` from `library` or nil if not found."
{:arglists '([library name])}
[{{value-set-defs :def} :valueSets} name]
(some #(when (= name (:name %)) %) value-set-defs))

(defn- to-code [{:keys [system code]}]
(value-set/->Code (type/value system) (type/value code)))

(defn- value-set [{{:keys [contains]} :expansion}]
(into #{} (map to-code) contains))

(defn- expand-value-set [terminology-service request]
(try
@(ts/expand-value-set terminology-service request)
(catch Exception e
(throw (ex-cause e)))))

(defmethod core/compile* :elm.compiler.type/value-set-ref
[{:keys [library terminology-service]} {:keys [name]}]
(when-let [{:keys [id]} (find-value-set-def library name)]
(reify-expr core/Expression
(-optimize [_ db]
(value-set (expand-value-set terminology-service {:db db :url id})))
(-eval [_ _ _ _]
(throw-anom (ba/fault "Can't eval value-set expression without optimization.")))
(-form [_]
(list 'value-set id)))))
22 changes: 20 additions & 2 deletions modules/cql/src/blaze/elm/compiler/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[blaze.fhir.spec.type.system :as system]
[clojure.string :as str])
(:import
[clojure.lang IReduceInit]
[clojure.lang IPersistentSet IReduceInit]
[java.time.temporal ChronoUnit]))

(set! *warn-on-reflection* true)
Expand Down Expand Up @@ -200,7 +200,25 @@
(-eval [expr _ _ _]
expr)
(-form [expr]
(mapv -form expr)))
(mapv -form expr))

IPersistentSet
(-static [_]
true)
(-attach-cache [expr _]
[(fn [] [expr])])
(-patient-count [_]
nil)
(-resolve-refs [expr _]
expr)
(-resolve-params [expr _]
expr)
(-optimize [expr _]
expr)
(-eval [expr _ _ _]
expr)
(-form [expr]
`(~'set ~@(mapv -form expr))))

(defmulti compile*
"Compiles `expression` in `context`.
Expand Down
2 changes: 2 additions & 0 deletions modules/cql/src/blaze/elm/compiler/external_data.clj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
data-type clauses)]
(reify-expr core/Expression
(-optimize [expr db]
;; if there is no resource, regardless of the individual patient,
;; available, just return an empty list for further optimizations
(if (coll/empty? (d/execute-query db type-query))
[]
expr))
Expand Down
4 changes: 2 additions & 2 deletions modules/cql/src/blaze/elm/compiler/library.clj
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@
:function-defs and :parameter-default-values.
There are currently no options."
[node library opts]
[context library opts]
(let [library (normalizer/normalize-library library)
context (assoc opts :node node :library library)]
context (merge opts context {:library library})]
(when-ok [{:keys [function-defs] :as context} (compile-function-defs context library)
expression-defs (expression-defs context library)
expression-defs (resolve-refs (unfiltered-expr-names expression-defs) expression-defs)
Expand Down
4 changes: 4 additions & 0 deletions modules/cql/src/blaze/elm/compiler/library/spec.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[blaze.elm.compiler.function-def :as-alias function-def]
[blaze.elm.compiler.spec]
[blaze.fhir.spec.spec]
[blaze.terminology-service.spec]
[clojure.spec.alpha :as s]))

(s/def ::expression-def/name
Expand Down Expand Up @@ -40,5 +41,8 @@
(s/def ::c/library
(s/keys :req-un [::c/expression-defs ::c/function-defs ::c/parameter-default-values]))

(s/def ::c/context
(s/keys :req-un [:blaze.db/node :blaze/terminology-service]))

(s/def ::c/options
map?)
2 changes: 1 addition & 1 deletion modules/cql/src/blaze/elm/compiler/library_spec.clj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
[cognitect.anomalies :as-alias anom]))

(s/fdef library/compile-library
:args (s/cat :node :blaze.db/node :library :elm/library :opts ::c/options)
:args (s/cat :context ::c/context :library :elm/library :opts ::c/options)
:ret (s/or :library ::c/library :anomaly ::anom/anomaly))

(s/fdef library/resolve-all-refs
Expand Down
3 changes: 3 additions & 0 deletions modules/cql/src/blaze/elm/concept.clj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
(-form [_]
`(~'concept ~@(map core/-form codes))))

(defn concept? [x]
(instance? Concept x))

(defn concept
"Returns a CQL concept"
[codes]
Expand Down
Loading

0 comments on commit ecf8bda

Please sign in to comment.