Skip to content

Commit 16edf3c

Browse files
committed
refactor fhir_parsers
1 parent d5041a8 commit 16edf3c

File tree

3 files changed

+78
-57
lines changed

3 files changed

+78
-57
lines changed

lambdas/shared/src/common/validator/parsers/fhir_parser.py

Lines changed: 70 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,126 @@
1-
"""
2-
A parser for navigating and extracting data from FHIR JSON resources.
3-
4-
Identifiy a path to a node using '|' to separate levels and
5-
"""
6-
71
import json
82

93

104
class FHIRParser:
11-
# parser variables
125
def __init__(self):
13-
self.fhir_resource = {}
6+
self.fhir_resources = {}
147

15-
# -------------------------------------------
16-
# File Management
17-
# used for files
18-
def parse_fhir_file(self, fhir_file_name):
8+
# This opens a file with FHIR resource data
9+
def parse_fhir_file(self, fhir_file_name: str) -> None:
1910
with open(fhir_file_name) as json_file:
20-
self.fhir_resource = json.load(json_file)
11+
self.fhir_resources = json.load(json_file)
2112

22-
# used for JSON FHIR Resource data
23-
def parse_fhir_data(self, fhir_data):
24-
self.fhir_resource = fhir_data
13+
# This is used for JSON FHIR Resource data events
14+
def parse_fhir_data(self, fhir_data: dict) -> None:
15+
self.fhir_resources = fhir_data
2516

26-
# ------------------------------------------------
27-
# Scan and Identify
28-
# scan for a key name or a value
29-
def _scan_values_for_match(self, parent, match_value):
17+
def _locate_fhir_item_in_dict(self, fhir_resource: dict, fhir_field: str) -> bool:
18+
"""
19+
Checks whether a given FHIR field name or value exists in a FHIR dictionary.
20+
:param fhir_resource: The FHIR resource as a dictionary.
21+
:param fhir_field: The FHIR field name or value to search for.
22+
:return: True if the field name or value matches a key item in the resource, False otherwise.
23+
"""
3024
try:
31-
for key in parent:
32-
if parent[key] == match_value:
25+
for key in fhir_resource:
26+
if fhir_resource[key] == fhir_field:
3327
return True
3428
return False
3529
except Exception:
3630
return False
3731

38-
# locate an index for an item in a list
39-
def _locate_list_id(self, parent, locator):
40-
field_list = locator.split(":")
32+
def _locate_fhir_item_in_list(self, fhir_resource: list, fhir_field: str) -> dict:
33+
"""
34+
Locates and returns the first FHIR item (dictionary) in a list that contains
35+
the specified FHIR field name or value.
36+
:param fhir_resource: The FHIR resource as a list of dictionaries.
37+
:param fhir_field: The FHIR field name or value to search for.
38+
:return: The first matching FHIR item (dictionary) if found, otherwise an empty string.
39+
"""
40+
field_list = fhir_field.split(":")
4141
node_id = 0
4242
index = 0
4343
try:
44-
while index < len(parent):
45-
for key in parent[index]:
46-
if (parent[index][key] == field_list[1]) or (key == field_list[1]):
44+
while index < len(fhir_resource):
45+
for key in fhir_resource[index]:
46+
if (fhir_resource[index][key] == field_list[1]) or (key == field_list[1]):
4747
node_id = index
4848
break
4949
else:
50-
if self._scan_values_for_match(parent[index][key], field_list[1]):
50+
if self._locate_fhir_item_in_dict(fhir_resource[index][key], field_list[1]):
5151
node_id = index
5252
break
5353
index += 1
5454
except Exception:
5555
return ""
56-
return parent[node_id]
56+
return fhir_resource[node_id]
5757

5858
# identify a node in the FHIR data
59-
def _get_node(self, parent, child):
60-
# check for indices
59+
def _extract_fhir_node_value(self, fhir_resource: dict | list, fhir_field_key: str) -> str:
60+
"""
61+
Safely retrieves a value from a FHIR resource by key or index.
62+
:param fhir_resource: The FHIR resource, which can be a dictionary or a list.
63+
:param fhir_field_key: The key (string) or index (integer as string) to retrieve the value for.
64+
:return: The value associated with the key or index, or an empty string if not found.
65+
"""
6166
try:
62-
result = parent[child]
67+
result = fhir_resource[fhir_field_key]
6368
except Exception:
6469
try:
65-
child = int(child)
66-
result = parent[child]
70+
child = int(fhir_field_key)
71+
result = fhir_resource[child]
6772
except Exception:
6873
result = ""
6974
return result
7075

71-
# locate a value for a key
72-
def _scan_for_value(self, fhir_fields):
73-
field_list = fhir_fields.split("|")
76+
def _resolve_fhir_path(self, fhir_field_path: str) -> str:
77+
"""
78+
Resolves a FHIR value from a pipe-delimited FHIR path string.
79+
This function navigates through FHIR resources stored in `self.fhir_resources`
80+
using the provided path, which may include nested dictionaries or lists.
81+
Fields prefixed with "#" indicate list-based lookups.
82+
"""
83+
field_list = fhir_field_path.split("|")
84+
7485
# get root field before we iterate
75-
rootfield = self.fhir_resource[field_list[0]]
86+
resource_per_field = self.fhir_resources[field_list[0]]
7687
del field_list[0]
7788
try:
7889
for field in field_list:
7990
if field.startswith("#"):
80-
rootfield = self._locate_list_id(rootfield, field) # check here for default index??
91+
resource_per_field = self._locate_fhir_item_in_list(
92+
resource_per_field, field
93+
) # check here for default index??
8194
else:
82-
rootfield = self._get_node(rootfield, field)
95+
resource_per_field = self._extract_fhir_node_value(resource_per_field, field)
8396
except Exception:
84-
rootfield = ""
85-
return rootfield
97+
resource_per_field = ""
98+
return resource_per_field
8699

87-
# get the value list for a key
88-
def get_key_value(self, field_name):
100+
def get_fhir_value_list(self, field_path: str) -> list[str]:
101+
"""
102+
Retrieves one or more values from a FHIR resource and returns them as a list.
103+
:param field_path: The FHIR field path to retrieve the values for.
104+
:return: A list of values found at the specified FHIR field path.
105+
"""
89106
value = []
90107
try:
91-
response_value = self._scan_for_value(field_name)
108+
response_value = self._resolve_fhir_path(field_path)
92109
except Exception:
93110
response_value = ""
94111

95112
value.append(response_value)
96113
return value
97114

98-
# get the value list for a key
99-
def get_key_single_value(self, field_name):
115+
def get_fhir_value(self, field_path: str) -> str:
116+
"""
117+
Retrieves a single value from a FHIR resource.
118+
:param field_path: The FHIR field path to retrieve the value for.
119+
:return: The value as a string, or an empty string if not found.
120+
"""
100121
value = ""
101122
try:
102-
response_value = self._scan_for_value(field_name)
123+
response_value = self._resolve_fhir_path(field_path)
103124
except Exception:
104125
response_value = ""
105126

lambdas/shared/src/common/validator/validator.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ def _validate_expression(
9898
if "parentExpression" in expression:
9999
parent_expression = expression["parentExpression"]
100100
if self._check_error_record_for_fail(parent_expression):
101-
error_record = {
102-
"code": ExceptionLevels.PARENT_FAILED,
103-
"message": MESSAGES[ExceptionLevels.PARENT_FAILED] + ", Parent ID: " + parent_expression,
104-
}
101+
error_record = ErrorReport(
102+
code=ExceptionLevels.PARENT_FAILED,
103+
message=MESSAGES[ExceptionLevels.PARENT_FAILED] + ", Parent ID: " + parent_expression,
104+
)
105105
self._add_error_record(
106106
error_record, expression_error_group, expression_name, expression_id, error_level
107107
)
@@ -217,7 +217,7 @@ def run_validation(
217217
# Report Generation
218218
# Build the error Report
219219
def build_error_report(self, event_id):
220-
occurrence_date_time = self.data_parser.get_key_single_value("occurrenceDateTime")
220+
occurrence_date_time = self.data_parser.get_fhir_value("occurrenceDateTime")
221221
dq_reporter = DQReporter()
222222
dq_report = dq_reporter.generate_error_report(event_id, occurrence_date_time, self.error_records)
223223

lambdas/shared/tests/test_common/validator/test_parser.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ def test_parse_fhir_key_exists(self):
1414

1515
fhir_parser = FHIRParser()
1616
fhir_parser.parse_fhir_file(fhirFilePath)
17-
my_value = fhir_parser.get_key_value("vaccineCode|coding|0|code")
17+
my_value = fhir_parser.get_fhir_value_list("vaccineCode|coding|0|code")
1818
self.assertEqual(my_value, ["42223111000001107"])
1919

2020
def test_parse_fhir_key_not_exists(self):
2121
fhirFilePath = self.fhir_data_folder / "vaccination.json"
2222

2323
fhir_parser = FHIRParser()
2424
fhir_parser.parse_fhir_file(fhirFilePath)
25-
my_value = fhir_parser.get_key_value("vaccineCode|coding|1")
25+
my_value = fhir_parser.get_fhir_value_list("vaccineCode|coding|1")
2626
self.assertEqual(my_value, [""])
27-
my_value = fhir_parser.get_key_value("vaccineCode|coding|1|codes")
27+
my_value = fhir_parser.get_fhir_value_list("vaccineCode|coding|1|codes")
2828
self.assertEqual(my_value, [""])

0 commit comments

Comments
 (0)