Skip to content

Commit b7417f6

Browse files
JNygaard-SkylightJosh Nygaard
andauthored
TCRS now finds Conditions on its own. (#2238)
* TCRS now finds condtions on its own. * Remove old validator * Remove unit test for removed validator * Look for conditions in all resources * Fix unit test --------- Co-authored-by: Josh Nygaard <[email protected]>
1 parent 741ab2c commit b7417f6

File tree

8 files changed

+75
-62
lines changed

8 files changed

+75
-62
lines changed

.vscode/launch.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@
1010
"cwd": "${workspaceFolder}/containers/orchestration",
1111
"justMyCode": false,
1212
"python": "${workspaceFolder}/containers/orchestration/.venv/bin/python"
13+
},
14+
{
15+
"name": "Python Debugger: Trigger Code Reference",
16+
"type": "debugpy",
17+
"request": "launch",
18+
"program": "${workspaceFolder}/containers/trigger-code-reference/app/main.py",
19+
"console": "integratedTerminal",
20+
"cwd": "${workspaceFolder}/containers/trigger-code-reference",
21+
"justMyCode": false,
22+
"python": "${workspaceFolder}/containers/trigger-code-reference/.venv/bin/python",
23+
"env": {
24+
"PYTHONPATH": "${workspaceFolder}/containers/trigger-code-reference"
25+
}
1326
}
1427
]
1528
}

containers/orchestration/app/custom_configs/seed-ecr-viewer-config.json

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,7 @@
2323
{
2424
"name": "stamped_ecr",
2525
"service": "trigger_code_reference",
26-
"endpoint": "/stamp-condition-extensions",
27-
"params": {
28-
"conditions": [
29-
"840539006"
30-
]
31-
}
26+
"endpoint": "/stamp-condition-extensions"
3227
},
3328
{
3429
"name": "message_parser_values",

containers/trigger-code-reference/app/main.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from app.models import InsertConditionInput
1010
from app.utils import _find_codes_by_resource_type
1111
from app.utils import _stamp_resource_with_code_extension
12+
from app.utils import find_conditions
1213
from app.utils import get_clean_snomed_code
1314
from app.utils import get_clinical_services_dict
1415
from app.utils import get_clinical_services_list
@@ -61,21 +62,22 @@ async def stamp_condition_extensions(
6162
) -> Response:
6263
"""
6364
Extends the resources of a supplied FHIR bundle with extension tags
64-
related to one or more supplied conditions. For each condition in the
65-
given list of conditions, each resource in the bundle is appended with
66-
an extension structure indicating which SNOMED condition code the
65+
related to one or more supplied conditions. For each condition found in
66+
the bundle, each resource in the bundle is appended with an extension
67+
structure indicating which SNOMED condition code the
6768
resource is linked to.
6869
6970
:param input: A request formatted as an InsertConditionInput, containing a
70-
FHIR bundle whose resources to extend and one or more SNOMED condition
71-
code strings to extend by.
71+
FHIR bundle whose resources to extend.
7272
:return: HTTP Response containing the bundle with resources extended by
7373
any linked conditions.
7474
"""
75-
# Collate all clinical services for each of the supplied conditions to
76-
# extend, collected by service type
75+
# Collate all clinical services for each of the found conditions to extend,
76+
# collected by service type
7777
stamp_codes_to_service_codes = {}
78-
for cond in input.conditions:
78+
conditions = find_conditions(input.bundle)
79+
80+
for cond in conditions:
7981
cond_list = get_clinical_services_list([cond])
8082
cond_dict = get_clinical_services_dict(cond_list)
8183
stamp_codes_to_service_codes[cond] = cond_dict
@@ -92,7 +94,7 @@ async def stamp_condition_extensions(
9294
continue
9395

9496
# Want to check each queried condition for extension codes
95-
for cond in input.conditions:
97+
for cond in conditions:
9698
# Only need a single instance of service type lookup to contain
9799
# the resource's code
98100
should_stamp = False
@@ -161,3 +163,15 @@ async def get_value_sets_for_condition(
161163
clinical_services_list, filter_clinical_services
162164
)
163165
return values
166+
167+
168+
# This block is only executed if the script is run directly, for local development and debugging.
169+
if "__main__" == __name__:
170+
import uvicorn
171+
172+
uvicorn.run(
173+
app="app.main:app",
174+
host="0.0.0.0",
175+
port=8080,
176+
reload=True,
177+
)
Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
from typing import List
2-
31
from pydantic import BaseModel
42
from pydantic import Field
5-
from pydantic import root_validator
63

74

85
class InsertConditionInput(BaseModel):
@@ -12,31 +9,7 @@ class InsertConditionInput(BaseModel):
129

1310
bundle: dict = Field(
1411
description="The FHIR bundle to modify. Each resource in the bundle related "
15-
"to one or more of the conditions in the other supplied parameter will have "
12+
"to one or more of the conditions found in the bundle will have "
1613
"an extension added to the resource noting the SNOMED code relating to the "
1714
"associated condition(s)."
1815
)
19-
conditions: List[str] = Field(
20-
description="The list of SNOMED codes to insert as extensions into any "
21-
"associated resources in the supplied FHIR bundle."
22-
)
23-
24-
@root_validator
25-
def require_non_empty_condition_list(cls, values):
26-
"""
27-
Ensures that the supplied conditions list parameter is both a list
28-
and has at least one element by which to extend the supplied FHIR
29-
bundle.
30-
31-
:param cls: The InsertConditionInput class.
32-
:param values: The condition list supplied by the caller.
33-
:raises ValueError: Errors when supplied condition list contains no
34-
elements, since this can't be used to extend a bundle.
35-
:return: The endpoint's values, if the condition list is valid.
36-
"""
37-
if len(values.get("conditions")) == 0:
38-
raise ValueError(
39-
"Supplied list of SNOMED conditions must contain "
40-
"one or more elements; given list was empty."
41-
)
42-
return values

containers/trigger-code-reference/app/utils.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,35 @@ def read_json_from_assets(filename: str) -> dict:
197197
:return: A dictionary containing the contents of the file.
198198
"""
199199
return json.load(open((Path(__file__).parent.parent / "assets" / filename)))
200+
201+
202+
def find_conditions(bundle: dict) -> set[str]:
203+
"""
204+
Finds conditions in a bundle of resources.
205+
206+
:param bundle: The bundle of resources to search.
207+
:return: A set of SNOMED codes representing the conditions found.
208+
"""
209+
CONDITION_CODE = "64572001"
210+
SNOMED_URL = "http://snomed.info/sct"
211+
212+
# Get all resources
213+
resources = [resource["resource"] for resource in bundle["entry"]]
214+
215+
# Filter observations that have the SNOMED code for "Condition".
216+
resources_with_conditions = [
217+
obs
218+
for obs in resources
219+
if "code" in obs
220+
and any(coding["code"] == CONDITION_CODE for coding in obs["code"]["coding"])
221+
]
222+
223+
# Extract unique SNOMED codes from the observations
224+
snomed_codes = {
225+
coding["code"]
226+
for obs in resources_with_conditions
227+
for coding in obs.get("valueCodeableConcept", {}).get("coding", [])
228+
if coding["system"] == SNOMED_URL
229+
}
230+
231+
return snomed_codes

containers/trigger-code-reference/assets/sample_stamp_condition_extensions_requests.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
"summary": "Stamping a FHIR bundle consisting of an eICR merged with its RR",
44
"description": "This is an example bundle demonstrating condition-stamping on an input eICR that's checking for COVID-related condition codes.",
55
"value": {
6-
"conditions": [
7-
"840539006"
8-
],
96
"bundle": {
107
"resourceType": "Bundle",
118
"type": "batch",

containers/trigger-code-reference/tests/integration/test_trigger_code_reference.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def test_openapi():
2525
@pytest.mark.integration
2626
def test_tcr_stamping(setup, fhir_bundle):
2727
reportable_condition_code = "840539006"
28-
request = {"bundle": fhir_bundle, "conditions": [reportable_condition_code]}
28+
request = {"bundle": fhir_bundle}
2929
stamp_response = httpx.post(STAMP_ENDPOINT, json=request)
3030
assert stamp_response.status_code == 200
3131

containers/trigger-code-reference/tests/test_condition_endpoints.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,6 @@ def test_get_value_sets_for_condition(mock_db):
7777
assert response.json() == expected_result
7878

7979

80-
def test_stamp_conditions_bad_input():
81-
input = {"bundle": {"test_key": "test_val"}, "conditions": []}
82-
response = client.post("/stamp-condition-extensions", json=input)
83-
assert response.status_code == 422
84-
assert (
85-
response.json()["detail"][0]["msg"]
86-
== "Supplied list of SNOMED conditions "
87-
+ "must contain one or more elements; given list was empty."
88-
)
89-
90-
9180
# Note: This function is defined in utils, but we mock it in the namespace
9281
# coming from main because that's where the endpoint is invoking it from
9382
@patch("app.main.get_clinical_services_list")
@@ -143,25 +132,25 @@ def test_stamp_condition_extensions(patched_get_services_list):
143132
("dxtc", "8971234987123", "code-sys-2"),
144133
("lotc", "72391283|8916394-2|24", "code-sys-1"),
145134
]
146-
input = {"bundle": message, "conditions": ["99999-9"]}
135+
input = {"bundle": message}
147136
response = client.post("/stamp-condition-extensions", json=input)
148137
assert response.status_code == 200
149138
stamped_message = response.json()["extended_bundle"]
150139

151140
# Check observation: diagnostic code value came back successful
152141
found_matching_extension = _check_for_stamped_resource_in_bundle(
153-
stamped_message, "99999-9", "Observation"
142+
stamped_message, "840539006", "Observation"
154143
)
155144
assert found_matching_extension
156145

157146
# Check condition: no value set cross-referenced for this bundle, no stamp
158147
found_matching_extension = _check_for_stamped_resource_in_bundle(
159-
stamped_message, "99999-9", "Condition"
148+
stamped_message, "840539006", "Condition"
160149
)
161150
assert not found_matching_extension
162151

163152
# Check immunization: we did find a referenced immunization code, so it should be there
164153
found_matching_extension = _check_for_stamped_resource_in_bundle(
165-
stamped_message, "99999-9", "Immunization"
154+
stamped_message, "840539006", "Immunization"
166155
)
167156
assert found_matching_extension

0 commit comments

Comments
 (0)