Skip to content

Commit 2d3d347

Browse files
committed
Handle subject parameter separately
1 parent 1460b7f commit 2d3d347

4 files changed

Lines changed: 94 additions & 61 deletions

File tree

app/aidbox/operations.py

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,11 @@
1212
resolve_expression,
1313
)
1414
from ..sdc.exception import MissingParamOperationOutcome
15-
from ..sdc.utils import parameter_to_env, parse_parameter_value, validate_context
15+
from ..sdc.utils import get_external_fhir_base_url_from_resource, parameter_to_env, validate_context
1616
from ..utils import get_extract_services
1717
from .settings import settings
1818
from .utils import AidboxSdcRequest, aidbox_operation, get_user_sdk_client, prepare_args
1919

20-
# TODO: it's outside from spec, if it's only for data fetching,
21-
# TODO: better to use dataEndpoint and it should be handled in `parameter_to_env` function
22-
EXTERNAL_FHIR_BASE_URL_PARAM_KEY = "externalFhirBaseUrl"
23-
2420

2521
@aidbox_operation(["GET"], ["Questionnaire", {"name": "id"}, "$assemble"])
2622
@prepare_args
@@ -52,7 +48,7 @@ async def constraint_check_operation(request: AidboxSdcRequest):
5248
client = get_user_sdk_client(
5349
request.request,
5450
request.client,
55-
_get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
51+
get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
5652
)
5753
env = await parameter_to_env(client, request.resource, request.is_fhir)
5854

@@ -79,7 +75,7 @@ async def get_questionnaire_context_operation(request: AidboxSdcRequest):
7975
client = get_user_sdk_client(
8076
request.request,
8177
request.client,
82-
_get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
78+
get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
8379
)
8480
env = await parameter_to_env(client, request.resource, request.is_fhir)
8581

@@ -100,7 +96,7 @@ async def extract_questionnaire_operation(request: AidboxSdcRequest):
10096
client = get_user_sdk_client(
10197
request.request,
10298
request.client,
103-
_get_external_fhir_base_url_from_resource(resource, request.is_fhir),
99+
get_external_fhir_base_url_from_resource(resource, request.is_fhir),
104100
)
105101
if resource["resourceType"] == "QuestionnaireResponse":
106102
env = {}
@@ -159,7 +155,7 @@ async def extract_questionnaire_instance_operation(request: AidboxSdcRequest):
159155
extract_client = get_user_sdk_client(
160156
request.request,
161157
request.client,
162-
_get_external_fhir_base_url_from_resource(resource, request.is_fhir),
158+
get_external_fhir_base_url_from_resource(resource, request.is_fhir),
163159
)
164160
fhir_questionnaire = (
165161
await request.fhir_client.resources("Questionnaire")
@@ -233,7 +229,7 @@ async def populate_questionnaire(request: AidboxSdcRequest):
233229
client = get_user_sdk_client(
234230
request.request,
235231
request.client,
236-
_get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
232+
get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
237233
)
238234
env = await parameter_to_env(client, request.resource, request.is_fhir)
239235

@@ -250,7 +246,7 @@ async def populate_questionnaire_instance(request: AidboxSdcRequest):
250246
client = get_user_sdk_client(
251247
request.request,
252248
request.client,
253-
_get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
249+
get_external_fhir_base_url_from_resource(request.resource, request.is_fhir),
254250
)
255251
fhir_questionnaire = (
256252
await request.fhir_client.resources("Questionnaire")
@@ -269,16 +265,3 @@ async def populate_questionnaire_instance(request: AidboxSdcRequest):
269265
@aidbox_operation(["POST"], ["Questionnaire", "$resolve-expression"], public=True)
270266
def resolve_expression_operation(_operation, request):
271267
return web.json_response(resolve_expression(request["resource"]), dumps=json.dumps)
272-
273-
274-
def _get_external_fhir_base_url_from_resource(resource: dict | None, is_fhir: bool):
275-
if not resource or resource.get("resourceType") != "Parameters":
276-
return None
277-
for param in resource.get("parameter", []):
278-
if param.get("name") != EXTERNAL_FHIR_BASE_URL_PARAM_KEY:
279-
continue
280-
if "resource" in param:
281-
continue
282-
value = parse_parameter_value(param, is_fhir)
283-
return value or None
284-
return None

app/sdc/utils.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919

2020
r4 = models["r4"]
2121

22+
# TODO: it's outside from spec, if it's only for data fetching,
23+
# TODO: better to use dataEndpoint and it should be handled in `parameter_to_env` function
24+
EXTERNAL_FHIR_BASE_URL_PARAM_KEY = "externalFhirBaseUrl"
25+
2226

2327
def get_type(item, data):
2428
type = item["type"]
@@ -172,9 +176,15 @@ async def parameter_to_env(
172176
elif "resource" in param:
173177
env[param["name"]] = param["resource"]
174178
else:
175-
value = parse_parameter_value(param, is_fhir)
179+
value, kind = parse_parameter_value(param, is_fhir)
176180
if value:
177-
env[param["name"]] = value
181+
if param["name"] == "subject" and kind == "Reference":
182+
env[param["name"]] = await client.reference(
183+
reference=value["reference"]
184+
).to_resource()
185+
env["useSDCAPI"] = True
186+
else:
187+
env[param["name"]] = value
178188
# Mapping parameters to fhir resource names
179189
questionnaire = env.get("questionnaire")
180190
if questionnaire:
@@ -185,14 +195,27 @@ async def parameter_to_env(
185195
return env
186196

187197

188-
def parse_parameter_value(parameter, is_fhir: bool):
198+
def parse_parameter_value(parameter, is_fhir: bool) -> tuple[Any, str]:
189199
if is_fhir:
190200
_name_key, value_key = parameter.keys()
191-
return parameter[value_key]
192-
else:
193-
value = parameter["value"]
194-
polimorphic_key = first(value.keys())
195-
return value[polimorphic_key] if polimorphic_key else None
201+
return parameter[value_key], value_key.removeprefix("value")
202+
203+
value = parameter["value"]
204+
polimorphic_key = first(value.keys())
205+
return value[polimorphic_key] if polimorphic_key else None, polimorphic_key
206+
207+
208+
def get_external_fhir_base_url_from_resource(resource: dict | None, is_fhir: bool):
209+
if not resource or resource.get("resourceType") != "Parameters":
210+
return None
211+
for param in resource.get("parameter", []):
212+
if param.get("name") != EXTERNAL_FHIR_BASE_URL_PARAM_KEY:
213+
continue
214+
if "resource" in param:
215+
continue
216+
value, _key = parse_parameter_value(param, is_fhir)
217+
return value or None
218+
return None
196219

197220

198221
async def load_source_queries(client, fce_questionnaire, env):

tests/sdc/test_populate.py

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,6 +1949,7 @@ async def test_named_item_population_context_parent_child_repeating_populate(fhi
19491949
],
19501950
}
19511951

1952+
19521953
@pytest.mark.asyncio
19531954
async def test_initial_expression_populate_sdc_api(fhir_client, safe_db):
19541955
q = await create_questionnaire(
@@ -1957,51 +1958,53 @@ async def test_initial_expression_populate_sdc_api(fhir_client, safe_db):
19571958
"status": "active",
19581959
"extension": [
19591960
make_launch_context_ext("LaunchPatient", "Patient"),
1961+
make_launch_context_ext("subject", "Patient"),
19601962
],
19611963
"item": [
19621964
{
19631965
"type": "string",
19641966
"linkId": "patientGiven",
19651967
"extension": [make_initial_expression_ext("%LaunchPatient.name.given")],
19661968
},
1969+
{
1970+
"type": "string",
1971+
"linkId": "patientId",
1972+
"extension": [make_initial_expression_ext("%subject.id")],
1973+
},
19671974
],
19681975
},
19691976
)
19701977

1971-
launch_patient = fhir_client.resource('Patient',
1972-
name= [{"given": ["John"]}]
1978+
launch_patient = fhir_client.resource(
1979+
"Patient",
1980+
name=[{"given": ["John"]}],
19731981
)
19741982
await launch_patient.save()
19751983

19761984
patient_id = launch_patient["id"]
19771985

19781986
patient_ref = {
19791987
"reference": f"Patient/{patient_id}",
1980-
"display": "Dow, John"
1988+
"display": "Dow, John",
19811989
}
19821990

1983-
p = await fhir_client.execute("Questionnaire/$populate", data={
1984-
"resourceType": "Parameters",
1985-
"parameter": [
1986-
{
1987-
"name": "questionnaire",
1988-
"resource": q
1989-
},
1990-
{
1991-
"name": "context",
1992-
"part": [
1993-
{
1994-
"name": "name",
1995-
"valueString": "LaunchPatient"
1996-
},
1997-
{
1998-
"name": "content",
1999-
"valueReference": patient_ref
2000-
}
2001-
]
2002-
}
2003-
]
2004-
})
1991+
p = await fhir_client.execute(
1992+
"Questionnaire/$populate",
1993+
data={
1994+
"resourceType": "Parameters",
1995+
"parameter": [
1996+
{"name": "questionnaire", "resource": q},
1997+
{
1998+
"name": "context",
1999+
"part": [
2000+
{"name": "name", "valueString": "LaunchPatient"},
2001+
{"name": "content", "valueReference": patient_ref},
2002+
],
2003+
},
2004+
{"name": "subject", "valueReference": patient_ref},
2005+
],
2006+
},
2007+
)
20052008

20062009
assert p == {
20072010
"resourceType": "Parameters",
@@ -2016,9 +2019,13 @@ async def test_initial_expression_populate_sdc_api(fhir_client, safe_db):
20162019
{
20172020
"linkId": "patientGiven",
20182021
"answer": [{"valueString": "John"}],
2019-
}
2022+
},
2023+
{
2024+
"linkId": "patientId",
2025+
"answer": [{"valueString": patient_id}],
2026+
},
20202027
],
2021-
}
2022-
}
2023-
]
2028+
},
2029+
},
2030+
],
20242031
}

tests/test_utils.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,26 @@ async def test_parameter_to_env_resolves_context_reference(fhir_client, safe_db)
313313
assert resolved["code"]["text"] == "parameter-to-env-integration"
314314

315315

316+
@pytest.mark.asyncio
317+
async def test_parameter_to_env_subject_resolves(fhir_client, safe_db):
318+
patient = fhir_client.resource("Patient")
319+
await patient.save()
320+
patient_ref = {"reference": f"Patient/{patient.id}"}
321+
322+
parameters = {
323+
"resourceType": "Parameters",
324+
"parameter": [
325+
{"name": "subject", "valueReference": patient_ref},
326+
],
327+
}
328+
329+
env = await parameter_to_env(fhir_client, parameters, is_fhir=True)
330+
331+
assert env["useSDCAPI"] is True
332+
assert env["subject"]["resourceType"] == "Patient"
333+
assert env["subject"]["id"] == patient.id
334+
335+
316336
async def test_sdc_api_params():
317337
resolved_patient = {
318338
"resourceType": "Patient",

0 commit comments

Comments
 (0)