Skip to content

Commit e888220

Browse files
authored
Merge pull request #141 from beda-software/migrate-to-fhir-format
Migrate to using strictly FHIR-format: - Remove FCE converter: Drop app/converter/ (FCE↔FHIR conversion layer) along with all associated test fixtures and converter tests — the codebase now works exclusively with FHIR format - Refactor app/sdc/assemble.py: Rewrite extension merging logic with explicit whitelisting and ordered deduplication (_merge_sub_ext / _merge_root_ext); add ItemConstraint type to typings.py - Add tests/fhir_server/ test suite: 17 integration tests covering all route handlers in app/fhir_server/operations.py and app/fhir_server/settings.py (100% coverage on both) - Extend tests/sdc/test_assemble.py: Add tests for mapper/cqf-library propagation through nested sub-questionnaires; add make_cqf_library_ext factory
2 parents ebd0d48 + 0b68c72 commit e888220

99 files changed

Lines changed: 907 additions & 14552 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.git-blame-ignore-revs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
6f6b0466467e
12
391b450eb9d4
23
26ddfd887c3f
34

app/aidbox/operations.py

Lines changed: 37 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import simplejson as json
22
from aiohttp import web
33

4-
from app.converter.aidbox import from_first_class_extension, to_first_class_extension
4+
from app.sdc.getters import get_launch_context, get_questionnaire_mapper
55

66
from ..sdc import (
77
assemble,
@@ -26,24 +26,14 @@
2626
@aidbox_operation(["GET"], ["Questionnaire", {"name": "id"}, "$assemble"])
2727
@prepare_args
2828
async def assemble_op(request: AidboxSdcRequest):
29-
def get_to_first_class_extension(fhir_resource):
30-
return to_first_class_extension(fhir_resource, request.aidbox_client)
31-
3229
fhir_questionnaire = (
3330
await request.fhir_client.resources("Questionnaire")
3431
.search(_id=request.route_params["id"])
3532
.get()
3633
)
37-
fce_questionnaire = await get_to_first_class_extension(fhir_questionnaire)
3834

39-
assembled_questionnaire_lazy = await assemble(
40-
request.fhir_client, fce_questionnaire, get_to_first_class_extension
41-
)
35+
assembled_questionnaire_lazy = await assemble(request.fhir_client, dict(fhir_questionnaire))
4236
assembled_questionnaire = json.loads(json.dumps(assembled_questionnaire_lazy, default=list))
43-
if request.is_fhir:
44-
assembled_questionnaire = await from_first_class_extension(
45-
assembled_questionnaire, request.aidbox_client
46-
)
4737
return web.json_response(assembled_questionnaire, dumps=json.dumps)
4838

4939

@@ -53,20 +43,14 @@ async def constraint_check_operation(request: AidboxSdcRequest):
5343
client = get_user_sdk_client(
5444
request.request,
5545
request.client,
56-
get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
57-
)
58-
env = await parameter_to_env(client, request.resource, request.is_fhir)
59-
60-
fce_questionnaire = (
61-
await to_first_class_extension(env["Questionnaire"], request.aidbox_client)
62-
if request.is_fhir
63-
else env["Questionnaire"]
46+
get_external_fhir_base_url_from_resource(request.resource),
6447
)
48+
env = await parameter_to_env(client, request.resource)
6549

6650
return web.json_response(
6751
await constraint_check(
6852
client,
69-
fce_questionnaire,
53+
env["Questionnaire"],
7054
env,
7155
legacy_behavior=settings.CONSTRAINT_LEGACY_BEHAVIOR,
7256
),
@@ -80,16 +64,11 @@ async def get_questionnaire_context_operation(request: AidboxSdcRequest):
8064
client = get_user_sdk_client(
8165
request.request,
8266
request.client,
83-
get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
67+
get_external_fhir_base_url_from_resource(request.resource),
8468
)
85-
env = await parameter_to_env(client, request.resource, request.is_fhir)
69+
env = await parameter_to_env(client, request.resource)
8670

87-
fce_questionnaire = (
88-
await to_first_class_extension(env["Questionnaire"], request.aidbox_client)
89-
if request.is_fhir
90-
else env["Questionnaire"]
91-
)
92-
result = await get_questionnaire_context(client, fce_questionnaire, env)
71+
result = await get_questionnaire_context(client, env["Questionnaire"], env)
9372

9473
return web.json_response(result, dumps=json.dumps)
9574

@@ -101,49 +80,43 @@ async def extract_questionnaire_operation(request: AidboxSdcRequest):
10180
client = get_user_sdk_client(
10281
request.request,
10382
request.client,
104-
get_external_fhir_base_url_from_resource(resource, request.is_fhir),
83+
get_external_fhir_base_url_from_resource(resource),
10584
)
10685
if resource["resourceType"] == "QuestionnaireResponse":
10786
env = {}
10887
env_questionnaire_response = resource
109-
fhir_questionnaire = (
88+
questionnaire = (
11089
await request.fhir_client.resources("Questionnaire")
11190
.search(_id=resource["questionnaire"])
11291
.get()
11392
)
114-
fce_questionnaire = await to_first_class_extension(
115-
fhir_questionnaire, request.aidbox_client
116-
)
117-
env_questionnaire = fhir_questionnaire if request.is_fhir else fce_questionnaire
11893
elif resource["resourceType"] == "Parameters":
119-
env = await parameter_to_env(client, resource, request.is_fhir)
94+
env = await parameter_to_env(client, resource)
12095
if "Questionnaire" not in env:
12196
raise MissingParamOperationOutcome("`Questionnaire` parameter is required")
12297
if "QuestionnaireResponse" not in env:
12398
raise MissingParamOperationOutcome("`QuestionnaireResponse` parameter is required")
12499

125-
fce_questionnaire = (
126-
await to_first_class_extension(env["Questionnaire"], request.aidbox_client)
127-
if request.is_fhir
128-
else env["Questionnaire"]
129-
)
130-
env_questionnaire = env["Questionnaire"]
100+
questionnaire = env["Questionnaire"]
131101
env_questionnaire_response = env["QuestionnaireResponse"]
132102

103+
mapper_refs = get_questionnaire_mapper(questionnaire.get("extension", []))
133104
mappings = [
134-
await request.aidbox_client.resources("Mapping").search(_id=m["id"]).get()
135-
for m in fce_questionnaire.get("mapping", [])
105+
await request.aidbox_client.resources("Mapping")
106+
.search(_id=ref["reference"].split("/")[-1])
107+
.get()
108+
for ref in mapper_refs
136109
]
137110

138111
context = {
139-
"Questionnaire": env_questionnaire,
112+
"Questionnaire": questionnaire,
140113
"QuestionnaireResponse": env_questionnaire_response,
141114
**env,
142115
}
143116

144117
await constraint_check(
145118
client,
146-
fce_questionnaire,
119+
questionnaire,
147120
context,
148121
legacy_behavior=settings.CONSTRAINT_LEGACY_BEHAVIOR,
149122
)
@@ -160,25 +133,21 @@ async def extract_questionnaire_instance_operation(request: AidboxSdcRequest):
160133
extract_client = get_user_sdk_client(
161134
request.request,
162135
request.client,
163-
get_external_fhir_base_url_from_resource(resource, request.is_fhir),
136+
get_external_fhir_base_url_from_resource(resource),
164137
)
165-
fhir_questionnaire = (
138+
questionnaire = (
166139
await request.fhir_client.resources("Questionnaire")
167140
.search(_id=request.route_params["id"])
168141
.get()
169142
)
170-
fce_questionnaire = await to_first_class_extension(fhir_questionnaire, request.aidbox_client)
171-
env_questionnaire = fhir_questionnaire if request.is_fhir else fce_questionnaire
172143

173144
return web.json_response(
174145
await extract_questionnaire_instance(
175146
request.aidbox_client,
176147
extract_client,
177-
fce_questionnaire,
178-
env_questionnaire,
148+
dict(questionnaire),
179149
resource,
180150
get_extract_services(request.request["app"]),
181-
request.is_fhir,
182151
),
183152
dumps=json.dumps,
184153
)
@@ -187,17 +156,15 @@ async def extract_questionnaire_instance_operation(request: AidboxSdcRequest):
187156
async def extract_questionnaire_instance(
188157
aidbox_client,
189158
extract_client,
190-
fce_questionnaire,
191-
env_questionnaire,
159+
questionnaire,
192160
resource,
193161
extract_services,
194-
is_fhir,
195162
):
196163
if resource["resourceType"] == "QuestionnaireResponse":
197164
env = {}
198165
env_questionnaire_response = extract_client.resource("QuestionnaireResponse", **resource)
199166
elif resource["resourceType"] == "Parameters":
200-
env = await parameter_to_env(extract_client, resource, is_fhir)
167+
env = await parameter_to_env(extract_client, resource)
201168
if "QuestionnaireResponse" not in env:
202169
raise MissingParamOperationOutcome("`QuestionnaireResponse` parameter is required")
203170

@@ -207,20 +174,23 @@ async def extract_questionnaire_instance(
207174
"Either `QuestionnaireResponse` resource or Parameters containing QuestionnaireResponse are required",
208175
)
209176

210-
if "launchContext" in fce_questionnaire:
211-
validate_context(fce_questionnaire["launchContext"], env)
177+
launch_context = get_launch_context(questionnaire.get("extension", []))
178+
if launch_context:
179+
validate_context(launch_context, env)
180+
212181
context = {
213182
"QuestionnaireResponse": env_questionnaire_response,
214-
"Questionnaire": env_questionnaire,
183+
"Questionnaire": questionnaire,
215184
**env,
216185
}
186+
mapper_refs = get_questionnaire_mapper(questionnaire.get("extension", []))
217187
mappings = [
218-
await aidbox_client.resources("Mapping").search(_id=m["id"]).get()
219-
for m in fce_questionnaire.get("mapping", [])
188+
await aidbox_client.resources("Mapping").search(_id=ref["reference"].split("/")[-1]).get()
189+
for ref in mapper_refs
220190
]
221191
await constraint_check(
222192
extract_client,
223-
fce_questionnaire,
193+
questionnaire,
224194
context,
225195
legacy_behavior=settings.CONSTRAINT_LEGACY_BEHAVIOR,
226196
)
@@ -234,9 +204,9 @@ async def populate_questionnaire(request: AidboxSdcRequest):
234204
client = get_user_sdk_client(
235205
request.request,
236206
request.client,
237-
get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
207+
get_external_fhir_base_url_from_resource(request.resource),
238208
)
239-
env = await parameter_to_env(client, request.resource, request.is_fhir)
209+
env = await parameter_to_env(client, request.resource)
240210

241211
if "Questionnaire" not in env:
242212
raise MissingParamOperationOutcome("`Questionnaire` parameter is required")
@@ -253,15 +223,15 @@ async def populate_questionnaire_instance(request: AidboxSdcRequest):
253223
client = get_user_sdk_client(
254224
request.request,
255225
request.client,
256-
get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
226+
get_external_fhir_base_url_from_resource(request.resource),
257227
)
258228
fhir_questionnaire = (
259229
await request.fhir_client.resources("Questionnaire")
260230
.search(_id=request.route_params["id"])
261231
.get()
262232
)
263233

264-
env = await parameter_to_env(client, request.resource, request.is_fhir)
234+
env = await parameter_to_env(client, request.resource)
265235
env["Questionnaire"] = fhir_questionnaire
266236

267237
populated_qr = await populate(

app/converter/__init__.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

app/converter/aidbox.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)