fhir-sheets 2.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fhir-sheets might be problematic. Click here for more details.

File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,69 @@
1
+ from ..core.config.FhirSheetsConfiguration import FhirSheetsConfiguration
2
+ from ..core import read_input
3
+ from ..core import conversion
4
+
5
+ import argparse
6
+ import orjson
7
+ import json
8
+ from pathlib import Path
9
+ from pprint import pprint
10
+
11
+ def find_sets(d, path=""):
12
+ if isinstance(d, dict):
13
+ for key, value in d.items():
14
+ new_path = f"{path}.{key}" if path else str(key)
15
+ find_sets(value, new_path)
16
+ elif isinstance(d, list): # Handle lists of dictionaries
17
+ for idx, item in enumerate(d):
18
+ find_sets(item, f"{path}[{idx}]")
19
+ elif isinstance(d, set):
20
+ print(f"Set found at path: {path}")
21
+
22
+ def main(input_file, output_folder, config=FhirSheetsConfiguration({})):
23
+ # Step 1: Read the input file using read_input module
24
+
25
+ # Check if the output folder exists, and create it if not
26
+
27
+ output_folder_path = Path(output_folder)
28
+ if not output_folder_path.is_absolute():
29
+ output_folder_path = Path().cwd() / Path(output_folder)
30
+ if not output_folder_path.exists():
31
+ output_folder_path.mkdir(parents=True, exist_ok=True) # Create the folder if it doesn't exist
32
+ resource_definition_entities, resource_link_entities, cohort_data = read_input.read_xlsx_and_process(input_file)
33
+ #For each index of patients
34
+ for i in range(0,cohort_data.get_num_patients()):
35
+ # Construct the file path for each JSON file
36
+ file_path = output_folder_path / f"{i}.json"
37
+ #Create a bundle
38
+ fhir_bundle = conversion.create_transaction_bundle(resource_definition_entities, resource_link_entities, cohort_data, i, config)
39
+ # Step 3: Write the processed data to the output file
40
+ find_sets(fhir_bundle)
41
+ json_string = orjson.dumps(fhir_bundle)
42
+ with open(file_path, 'wb') as json_file:
43
+ json_file.write(json_string)
44
+ with open(file_path, 'r') as json_file:
45
+ json_string = json.load(json_file)
46
+ with open(file_path, 'w') as json_file:
47
+ json.dump(json_string, json_file, indent = 4)
48
+
49
+ if __name__ == "__main__":
50
+ # Create the argparse CLI
51
+ parser = argparse.ArgumentParser(description="Process input, convert data, and write output.")
52
+
53
+ # Define the input file argument
54
+ parser.add_argument('--input_file', type=str, help="Path to the input xlsx ", default="src/resources/Synthetic_Input_Baseline.xlsx")
55
+
56
+ # Define the output file argument
57
+ parser.add_argument('--output_folder', type=str, help="Path to save the output files", default="output/")
58
+
59
+ # Config object arguments
60
+ parser.add_argument('--preview_mode', type=str, help="Configuration option to generate resources as 'preview mode' references will reference the entity name. Will primarily be used to render a singular resource for preview.", default=False)
61
+
62
+ # Define the output file argument
63
+ parser.add_argument('--medications_as_reference', type=str, help="Configuration option to create medication references. You may still provide medicationCodeableConcept, but a post process will convert the codeableconcepts to medication resources", default=False)
64
+ # Parse the arguments
65
+ args = parser.parse_args()
66
+
67
+ # Call the main function with the provided arguments
68
+ config = FhirSheetsConfiguration(vars(args))
69
+ main(args.input_file, args.output_folder, config)
@@ -0,0 +1,6 @@
1
+ from ..core import conversion as conversion, read_input as read_input
2
+ from ..core.config.FhirSheetsConfiguration import FhirSheetsConfiguration as FhirSheetsConfiguration
3
+ from pprint import pprint as pprint
4
+
5
+ def find_sets(d, path: str = '') -> None: ...
6
+ def main(input_file, output_folder, config=...) -> None: ...
File without changes
File without changes
@@ -0,0 +1,12 @@
1
+ from typing import Any, Dict
2
+
3
+
4
+ class FhirSheetsConfiguration():
5
+ def __init__(self, data: Dict[str, Any]):
6
+ self.preview_mode = data.get('preview_mode', False)
7
+ self.medications_as_reference = data.get('medications_as_reference', False)
8
+
9
+ def __repr__(self) -> str:
10
+ return (f"FhirSheetsConfiguration("
11
+ f"preview_mode={self.preview_mode}, "
12
+ f"medications_as_reference={self.medications_as_reference})")
@@ -0,0 +1,390 @@
1
+ from typing import Any, Dict, List
2
+ import uuid
3
+ from jsonpath_ng.jsonpath import Fields, Slice, Where
4
+ from jsonpath_ng.ext import parse as parse_ext
5
+
6
+ from .config.FhirSheetsConfiguration import FhirSheetsConfiguration
7
+
8
+ from .model.cohort_data_entity import CohortData, CohortData
9
+ from .model.resource_definition_entity import ResourceDefinition
10
+ from .model.resource_link_entity import ResourceLink
11
+ from . import fhir_formatting
12
+ from . import special_values
13
+
14
+ #Main top level function
15
+ #Creates a full transaction bundle for a patient at index
16
+ def create_transaction_bundle(resource_definition_entities: List[ResourceDefinition], resource_link_entities: List[ResourceLink], cohort_data: CohortData, index = 0, config: FhirSheetsConfiguration = FhirSheetsConfiguration({})):
17
+ root_bundle = initialize_bundle()
18
+ created_resources = {}
19
+ for resource_definition in resource_definition_entities:
20
+ entityName = resource_definition.entityName
21
+ #Create and collect fhir resources
22
+ fhir_resource = create_fhir_resource(resource_definition, cohort_data, index, config)
23
+ created_resources[entityName] = fhir_resource
24
+ #Link resources after creation
25
+ add_default_resource_links(created_resources, resource_link_entities)
26
+ create_resource_links(created_resources, resource_link_entities, config.preview_mode)
27
+ #Construct into fhir bundle
28
+ for fhir_resource in created_resources.values():
29
+ add_resource_to_transaction_bundle(root_bundle, fhir_resource)
30
+ if config.medications_as_reference:
31
+ post_process_create_medication_references(root_bundle)
32
+ return root_bundle
33
+
34
+ def create_singular_resource(singleton_entityName: str, resource_definition_entities: List[ResourceDefinition], resource_link_entities: List[ResourceLink], cohort_data: CohortData, index = 0):
35
+ created_resources = {}
36
+ singleton_fhir_resource = {}
37
+ for resource_definition in resource_definition_entities:
38
+ entityName = resource_definition.entityName
39
+ #Create and collect fhir resources
40
+ fhir_resource = create_fhir_resource(resource_definition, cohort_data, index)
41
+ created_resources[entityName] = fhir_resource
42
+ if entityName == singleton_entityName:
43
+ singleton_fhir_resource = fhir_resource
44
+ add_default_resource_links(created_resources, resource_link_entities)
45
+ create_resource_links(created_resources, resource_link_entities, preview_mode=True)
46
+ return singleton_fhir_resource
47
+
48
+ #Initialize root bundle definition
49
+ def initialize_bundle():
50
+ root_bundle = {}
51
+ root_bundle['resourceType'] = 'Bundle'
52
+ root_bundle['id'] = str(uuid.uuid4())
53
+ root_bundle['meta'] = {
54
+ 'security': [{
55
+ 'system': 'http://terminology.hl7.org/CodeSystem/v3-ActReason',
56
+ 'code': 'HTEST',
57
+ 'display': 'test health data'
58
+ }]
59
+ }
60
+ root_bundle['type'] = 'transaction'
61
+ root_bundle['entry'] = []
62
+ return root_bundle
63
+
64
+ #Initialize a resource from a resource definition. Adding basic information all resources need
65
+ def initialize_resource(resource_definition):
66
+ initial_resource = {}
67
+ initial_resource['resourceType'] = resource_definition.resourceType.strip()
68
+ initial_resource['id'] = str(uuid.uuid4()).strip()
69
+ if resource_definition.profiles:
70
+ initial_resource['meta'] = {
71
+ 'profile': resource_definition.profiles,
72
+ 'security': [{
73
+ 'system': 'http://terminology.hl7.org/CodeSystem/v3-ActReason',
74
+ 'code': 'HTEST',
75
+ 'display': 'test health data'
76
+ }]
77
+ }
78
+ return initial_resource
79
+
80
+ # Creates a fhir-json structure from a resource definition entity and the patient_data_sheet
81
+ def create_fhir_resource(resource_definition: ResourceDefinition, cohort_data: CohortData, index: int = 0, config: FhirSheetsConfiguration = FhirSheetsConfiguration({})) -> dict:
82
+ resource_dict = initialize_resource(resource_definition)
83
+ #Get field entries for this entity
84
+ header_entries_for_resourcename = [
85
+ headerEntry
86
+ for headerEntry in cohort_data.headers
87
+ if headerEntry.entityName == resource_definition.entityName
88
+ ]
89
+ dataelements_for_resourcename = {
90
+ field_name: value
91
+ for (entityName, field_name), value in cohort_data.patients[index].entries.items()
92
+ if entityName == resource_definition.entityName
93
+ }
94
+ if len(dataelements_for_resourcename.keys()) == 0:
95
+ print(f"WARNING: Patient index {index} - Create Fhir Resource Error - {resource_definition.entityName} - No columns for entity '{resource_definition.entityName}' found for resource in 'PatientData' sheet")
96
+ return resource_dict
97
+ all_field_entries = cohort_data.entities[resource_definition.entityName].fields
98
+ #For each field within the entity
99
+ for fieldName, value in dataelements_for_resourcename.items():
100
+ header_element = next((header for header in header_entries_for_resourcename if header.fieldName == fieldName), None)
101
+ if header_element is None:
102
+ print(f"WARNING: Field Name {fieldName} - No Header Entry found.")
103
+ continue
104
+ jsonPath = header_element.jsonPath
105
+ if jsonPath is None:
106
+ print(f"WARNING: Field Name {fieldName} - Header Entry found, but jsonPath attribute is None. Skipping.")
107
+ continue
108
+ valueType = header_element.valueType
109
+ if valueType is None:
110
+ print(f"WARNING: Field Name {fieldName} - Header Entry found, but valueType attribute is None. Skipping.")
111
+ continue
112
+ create_structure_from_jsonpath(resource_dict, jsonPath, resource_definition, valueType, value)
113
+ return resource_dict
114
+
115
+ #Create a resource_link for default references in the cases where only 1 resourceType of the source and destination exist
116
+ def add_default_resource_links(created_resources: dict, resource_link_entities: List[ResourceLink]):
117
+ default_references = [
118
+ ('allergyintolerance', 'patient', 'patient'),
119
+ ('allergyintolerance', 'practitioner', 'asserter'),
120
+ ('careplan', 'goal', 'goal'),
121
+ ('careplan', 'patient', 'subject'),
122
+ ('careplan', 'practitioner', 'performer'),
123
+ ('diagnosticreport', 'careteam', 'performer'),
124
+ ('diagnosticreport', 'imagingStudy', 'imagingStudy'),
125
+ ('diagnosticreport', 'observation', 'result'),
126
+ ('diagnosticreport', 'organization', 'performer'),
127
+ ('diagnosticreport', 'practitioner', 'performer'),
128
+ ('diagnosticreport', 'practitionerrole', 'performer'),
129
+ ('diagnosticreport', 'specimen', 'specimen'),
130
+ ('encounter', 'location', 'location'),
131
+ ('encounter', 'organization', 'serviceProvider'),
132
+ ('encounter', 'patient', 'subject'),
133
+ ('encounter', 'practitioner', 'participant'),
134
+ ('goal', 'condition', 'addresses'),
135
+ ('goal', 'patient', 'subject'),
136
+ ('immunization', 'patient', 'patient'),
137
+ ('immunization', 'practitioner', 'performer'),
138
+ ('immunization', 'organization', 'manufacturer'),
139
+ ('medicationrequest', 'medication', 'medicationReference'),
140
+ ('medicationrequest', 'patient', 'subject'),
141
+ ('medicationrequest', 'practitioner', 'requester'),
142
+ ('observation', 'device', 'device'),
143
+ ('observation', 'patient', 'subject'),
144
+ ('observation', 'practitioner', 'performer'),
145
+ ('observation', 'specimen', 'specimen'),
146
+ ('procedure', 'device', 'usedReference'),
147
+ ('procedure', 'location', 'location'),
148
+ ('procedure', 'patient', 'subject'),
149
+ ('procedure', 'practitioner', 'performer'),
150
+ ]
151
+
152
+ resource_counts = {}
153
+ for resourceName, resource in created_resources.items():
154
+ resourceType = resource['resourceType'].lower().strip()
155
+ if resourceType not in resource_counts:
156
+ resource_counts[resourceType]= {'count': 1, 'singletonEntityName': resourceName, 'singleResource': resource}
157
+ else:
158
+ resource_counts[resourceType]['count'] += 1
159
+ resource_counts[resourceType]['singletonResource'] = resource
160
+ resource_counts[resourceType]['singletonEntityName'] = resourceName
161
+
162
+ for default_reference in default_references:
163
+ sourceType = default_reference[0]
164
+ destinationType = default_reference[1]
165
+ fieldName = default_reference[2]
166
+ if sourceType in resource_counts and destinationType in resource_counts and \
167
+ resource_counts[sourceType]['count'] == 1 and resource_counts[destinationType]['count'] == 1:
168
+ originResourceEntityName = resource_counts[sourceType]['singletonEntityName']
169
+ destinationResourceEntityName = resource_counts[destinationType]['singletonEntityName']
170
+ resource_link_entities.append(
171
+ ResourceLink(originResourceEntityName,fieldName,destinationResourceEntityName)
172
+ )
173
+ return
174
+
175
+
176
+ #List function to create resource references/links with created entities
177
+ def create_resource_links(created_resources, resource_link_entites, preview_mode = False):
178
+ #TODO: Build resource links
179
+ print("Building resource links")
180
+ for resource_link_entity in resource_link_entites:
181
+ create_resource_link(created_resources, resource_link_entity, preview_mode)
182
+ return
183
+
184
+ #Singular function to create a resource link.
185
+ def create_resource_link(created_resources, resource_link_entity, preview_mode = False):
186
+ # template scaffolding
187
+ reference_json_block = {
188
+ "reference" : "$value"
189
+ }
190
+ #Special reference handling blocks, in the form of (originResource, destinationResource, referencePath)
191
+ arrayType_references = [
192
+ ('diagnosticreport', 'specimen', 'specimen'),
193
+ ('diagnosticreport', 'practitioner', 'performer'),
194
+ ('diagnosticreport', 'practitionerrole', 'performer'),
195
+ ('diagnosticreport', 'organization', 'performer'),
196
+ ('diagnosticreport', 'careteam', 'performer'),
197
+ ('diagnosticreport', 'observation', 'result'),
198
+ ('diagnosticreport', 'imagingStudy', 'imagingStudy'),
199
+ ]
200
+ #Find the origin and destination resource from the link
201
+ try:
202
+ originResource = created_resources[resource_link_entity.originResource]
203
+ except KeyError:
204
+ print(f"WARNING: In ResourceLinks tab, found a Origin Resource of : {resource_link_entity.originResource} but no such entity found in PatientData")
205
+ return
206
+ try:
207
+ destinationResource = created_resources[resource_link_entity.destinationResource]
208
+ except KeyError:
209
+ print(f"WARNING: In ResourceLinks tab, found a Desitnation Resource of : {resource_link_entity.destinationResource} but no such entity found in PatientData")
210
+ return
211
+ #Estable the value of the refence
212
+ if preview_mode:
213
+ reference_value = destinationResource['resourceType'] + "/" + resource_link_entity.destinationResource
214
+ else:
215
+ reference_value = destinationResource['resourceType'] + "/" + destinationResource['id']
216
+ link_tuple = (originResource['resourceType'].strip().lower(),
217
+ destinationResource['resourceType'].strip().lower(),
218
+ resource_link_entity.referencePath.strip().lower())
219
+ if link_tuple in arrayType_references:
220
+ if resource_link_entity.referencePath.strip().lower() not in originResource:
221
+ originResource[resource_link_entity.referencePath.strip().lower()] = []
222
+ new_reference = reference_json_block.copy()
223
+ new_reference['reference'] = reference_value
224
+ originResource[resource_link_entity.referencePath.strip().lower()].append(new_reference)
225
+ else:
226
+ originResource[resource_link_entity.referencePath.strip().lower()] = reference_json_block.copy()
227
+ originResource[resource_link_entity.referencePath.strip().lower()]["reference"] = reference_value
228
+ return
229
+
230
+ def add_resource_to_transaction_bundle(root_bundle, fhir_resource):
231
+ entry = {}
232
+ entry['fullUrl'] = "urn:uuid:"+fhir_resource['id']
233
+ entry['resource'] = fhir_resource
234
+ entry['request'] = {
235
+ "method": "PUT",
236
+ "url": fhir_resource['resourceType'] + "/" + fhir_resource['id']
237
+ }
238
+ root_bundle['entry'].append(entry)
239
+ return root_bundle
240
+
241
+ #Drill down and create a structure from a json path with a simple recurisve process
242
+ # Supports 2 major features:
243
+ # 1) dot notation such as $.codeableconcept.coding[0].value = 1234
244
+ # 2) simple qualifiers such as $.name[use=official].family = Dickerson
245
+ # rootStruct: top level structure to drill into
246
+ # json_path: dotnotation path to follow
247
+ # resource_definition: resource description model from import
248
+ # entity_definition: specific field entry information for this function
249
+ # value: Actual value to assign
250
+ def create_structure_from_jsonpath(root_struct: Dict, json_path: str, resource_definition: ResourceDefinition, dataType: str, value: Any):
251
+ #Get all dot notation components as seperate
252
+ if dataType is not None and dataType.strip().lower() == 'string':
253
+ value = str(value)
254
+
255
+ if value == None:
256
+ print(f"WARNING: Full jsonpath: {json_path} - Expected to find a value but found None instead")
257
+ return root_struct
258
+ #Start of top-level function which calls the enclosed recursive function
259
+ parts = json_path.split('.')
260
+ return build_structure(root_struct, json_path, resource_definition, dataType, parts, value, [])
261
+
262
+ # main recursive function to drill into the json structure, assign paths, and create structure where needed
263
+ def build_structure(current_struct: Any, json_path: str, resource_definition: ResourceDefinition, dataType: str, parts: List[str], value: Any, previous_parts: List[str]):
264
+ if len(parts) == 0:
265
+ return current_struct
266
+ #Grab current part
267
+ part = parts[0]
268
+ #SPECIAL HANDLING CLAUSE
269
+ matching_handler = next((handler for handler in special_values.custom_handlers if (json_path.startswith(handler) or json_path == handler)), None)
270
+ if matching_handler is not None:
271
+ return special_values.custom_handlers[matching_handler].assign_value(json_path, resource_definition, dataType, current_struct, parts[-1], value)
272
+ #Ignore dollar sign ($) and drill farther down
273
+ if part == '$' or part == resource_definition.resourceType.strip():
274
+ #Ignore the dollar sign and the resourcetype
275
+ return build_structure_recurse(current_struct, json_path, resource_definition, dataType, parts, value, previous_parts, part)
276
+
277
+ # If parts length is one then this is the final key to access and pair
278
+ if len(parts) == 1:
279
+ #Check for numeic qualifier '[0]' and '[1]'
280
+ if '[' in part and ']' in part:
281
+ #Seperate the key from the qualifier
282
+ key_part = part[:part.index('[')]
283
+ qualifier = part[part.index('[')+1:part.index(']')]
284
+ qualifier_condition = qualifier.split('=')
285
+
286
+ #If there is no key part, aka '[0]', '[1]' etc, then it's a simple accessor
287
+ if key_part is None or key_part == '':
288
+ if not qualifier.isdigit():
289
+ raise TypeError(f"ERROR: Full jsonpath: {json_path} - current path - {'.'.join(previous_parts + parts[:1])} - qualifier - {qualifier} - standalone qualifier expected to be a single index numeric ([0], [1], etc)")
290
+ if current_struct == {}:
291
+ current_struct = []
292
+ if not isinstance(current_struct, list):
293
+ raise TypeError(f"ERROR: Full jsonpath: {json_path} - current path - {'.'.join(previous_parts + parts[:1])} - Expected a list, but got {type(current_struct).__name__} instead.")
294
+ part = int(qualifier)
295
+ if part + 1 > len(current_struct):
296
+ current_struct.extend({} for x in range (part + 1 - len(current_struct)))
297
+ #Actual assigning to the path
298
+ fhir_formatting.assign_value(current_struct, part, value, dataType)
299
+ return current_struct
300
+
301
+ # If there is a simple qualifier with '['and ']'
302
+ elif '[' in part and ']' in part:
303
+ #Seperate the key from the qualifier
304
+ key_part = part[:part.index('[')]
305
+ qualifier = part[part.index('[')+1:part.index(']')]
306
+ qualifier_condition = qualifier.split('=')
307
+
308
+ #If there is no key part, aka '[0]', '[1]' etc, then it's a simple accessor
309
+ if key_part is None or key_part == '':
310
+ if not qualifier.isdigit():
311
+ raise TypeError(f"ERROR: Full jsonpath: {json_path} - current path - {'.'.join(previous_parts + parts[:1])} - qualifier - {qualifier} - standalone qualifier expected to be a single index numeric ([0], [1], etc)")
312
+ if current_struct == {}:
313
+ current_struct = []
314
+ if not isinstance(current_struct, list):
315
+ raise TypeError(f"ERROR: Full jsonpath: {json_path} - current path - {'.'.join(previous_parts + parts[:1])} - Expected a list, but got {type(current_struct).__name__} instead.")
316
+ qualifier_as_number = int(qualifier)
317
+ if qualifier_as_number + 1 > len(current_struct):
318
+ current_struct.extend({} for x in range (qualifier_as_number + 1 - len(current_struct)))
319
+ inner_struct = current_struct[qualifier_as_number]
320
+ inner_struct = build_structure_recurse(inner_struct, json_path, resource_definition, dataType, parts, value, previous_parts, part)
321
+ current_struct[qualifier_as_number] = inner_struct
322
+ return current_struct
323
+ # Create the key part in the structure
324
+ if (not key_part in current_struct) or (isinstance(current_struct[key_part], dict)):
325
+ current_struct[key_part] = []
326
+ #If there is a key_part and the If the qualifier condition is defined
327
+ if len(qualifier_condition) == 2:
328
+ #special handling for code
329
+ if key_part != "coding" and (qualifier_condition[0] in ('code', 'system')):
330
+ #Move into the coding section if a qualifier asks for 'code' or 'system'
331
+ if 'coding' not in current_struct:
332
+ current_struct['coding'] = []
333
+ current_struct = current_struct['coding']
334
+ qualifier_key, qualifier_value = qualifier_condition
335
+ # Retrieve an inner structure if it exists allready that matches the criteria
336
+ inner_struct = next((innerElement for innerElement in current_struct[key_part] if isinstance(innerElement, dict) and innerElement.get(qualifier_key) == qualifier_value), None)
337
+ #If no inner structure exists, create one instead
338
+ if inner_struct is None:
339
+ inner_struct = {qualifier_key: qualifier_value}
340
+ current_struct[key_part].append(inner_struct)
341
+ #Recurse into that innerstructure where the qualifier matched to continue the part traversal
342
+ inner_struct = build_structure_recurse(inner_struct, json_path, resource_definition, dataType, parts, value, previous_parts, part)
343
+ return current_struct
344
+ #If there's no qualifier condition, but an index aka '[0]', '[1]' etc, then it's a simple accessor
345
+ elif qualifier.isdigit():
346
+ if not isinstance(current_struct[key_part], list):
347
+ raise TypeError(f"ERROR: Full jsonpath: {json_path} - current path - {'.'.join(previous_parts + [parts[0]])} - Expected a list, but got {type(current_struct).__name__} instead.")
348
+ qualifier_as_number = int(qualifier)
349
+ if qualifier_as_number > len(current_struct):
350
+ current_struct[key_part].extend({} for x in range (qualifier_as_number - len(current_struct)))
351
+ inner_struct = current_struct[key_part][qualifier_as_number]
352
+ inner_struct = build_structure_recurse(inner_struct, json_path, resource_definition, dataType, parts, value, previous_parts, part)
353
+ current_struct[key_part][qualifier_as_number] = inner_struct
354
+ return current_struct
355
+ #None qualifier accessor
356
+ else:
357
+ if(part not in current_struct):
358
+ current_struct[part] = {}
359
+ inner_struct = build_structure_recurse(current_struct[part], json_path, resource_definition, dataType, parts, value, previous_parts, part)
360
+ current_struct[part] = inner_struct
361
+ return current_struct
362
+
363
+ #Helper function to quickly recurse and return the next level of structure. Used by main recursive function
364
+ def build_structure_recurse(current_struct, json_path, resource_definition, dataType, parts, value, previous_parts, part):
365
+ previous_parts.append(part)
366
+ return_struct = build_structure(current_struct, json_path, resource_definition, dataType, parts[1:], value, previous_parts)
367
+ return return_struct
368
+
369
+ #Post-process function to add medication reference in specific references
370
+ def post_process_create_medication_references( root_bundle: dict):
371
+ medication_resources = [resource['resource'] for resource in root_bundle['entry'] if resource['resource']['resourceType'] == "Medication"]
372
+ medication_request_resources = [resource['resource'] for resource in root_bundle['entry'] if resource['resource']['resourceType'] == "MedicationRequest"]
373
+ for medication_request_resource in medication_request_resources:
374
+ #Get candidates
375
+ medication_candidates = [resource for resource in medication_resources if resource['code'] == medication_request_resource['medicationCodeableConcept']]
376
+ if not medication_candidates: #If no candidates, create, else get the first candidate
377
+ medication_target = target_medication = createMedicationResource(root_bundle, medication_request_resource['medicationCodeableConcept'])
378
+ medication_resources.append(target_medication)
379
+ else:
380
+ target_medication = medication_candidates[0]
381
+
382
+ del(medication_request_resource['medicationCodeableConcept'])
383
+ medication_request_resource['medicationReference'] = target_medication['resourceType'] + "/" + target_medication['id']
384
+ return
385
+
386
+ def createMedicationResource(root_bundle, medicationCodeableConcept):
387
+ target_medication = initialize_resource(ResourceDefinition.from_dict({'ResourceType': 'Medication'}))
388
+ target_medication['code'] = medicationCodeableConcept
389
+ add_resource_to_transaction_bundle(root_bundle, target_medication)
390
+ return target_medication
@@ -0,0 +1,22 @@
1
+ from . import fhir_formatting as fhir_formatting, special_values as special_values
2
+ from .config.FhirSheetsConfiguration import FhirSheetsConfiguration as FhirSheetsConfiguration
3
+ from .model.cohort_data_entity import CohortData as CohortData
4
+ from .model.resource_definition_entity import ResourceDefinition as ResourceDefinition
5
+ from .model.resource_link_entity import ResourceLink as ResourceLink
6
+ from jsonpath_ng.jsonpath import Fields as Fields, Slice as Slice, Where as Where
7
+ from typing import Any
8
+
9
+ def create_transaction_bundle(resource_definition_entities: list[ResourceDefinition], resource_link_entities: list[ResourceLink], cohort_data: CohortData, index: int = 0, config: FhirSheetsConfiguration = ...): ...
10
+ def create_singular_resource(singleton_entityName: str, resource_definition_entities: list[ResourceDefinition], resource_link_entities: list[ResourceLink], cohort_data: CohortData, index: int = 0): ...
11
+ def initialize_bundle(): ...
12
+ def initialize_resource(resource_definition): ...
13
+ def create_fhir_resource(resource_definition: ResourceDefinition, cohort_data: CohortData, index: int = 0, config: FhirSheetsConfiguration = ...) -> dict: ...
14
+ def add_default_resource_links(created_resources: dict, resource_link_entities: list[ResourceLink]): ...
15
+ def create_resource_links(created_resources, resource_link_entites, preview_mode: bool = False) -> None: ...
16
+ def create_resource_link(created_resources, resource_link_entity, preview_mode: bool = False) -> None: ...
17
+ def add_resource_to_transaction_bundle(root_bundle, fhir_resource): ...
18
+ def create_structure_from_jsonpath(root_struct: dict, json_path: str, resource_definition: ResourceDefinition, dataType: str, value: Any): ...
19
+ def build_structure(current_struct: Any, json_path: str, resource_definition: ResourceDefinition, dataType: str, parts: list[str], value: Any, previous_parts: list[str]): ...
20
+ def build_structure_recurse(current_struct, json_path, resource_definition, dataType, parts, value, previous_parts, part): ...
21
+ def post_process_create_medication_references(root_bundle: dict): ...
22
+ def createMedicationResource(root_bundle, medicationCodeableConcept): ...