complete response parser
Browse files- responseparser.py +222 -18
responseparser.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
import json
|
2 |
from datetime import datetime
|
3 |
-
from typing import List, Dict, Optional
|
4 |
|
5 |
class PatientDataExtractor:
|
6 |
-
"""Class to extract
|
7 |
|
8 |
def __init__(self, patient_data: str):
|
9 |
"""Initialize with patient data in JSON string format."""
|
@@ -36,13 +36,35 @@ class PatientDataExtractor:
|
|
36 |
"""Get the currently selected patient resource."""
|
37 |
return self.patients[self.current_patient_idx]
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
def get_first_name(self) -> str:
|
40 |
"""Extract patient's first name."""
|
41 |
patient = self._get_current_patient()
|
42 |
names = patient.get("name", [])
|
43 |
for name in names:
|
44 |
if name.get("use") == "official" and "given" in name:
|
45 |
-
return name["given"][0]
|
46 |
return ""
|
47 |
|
48 |
def get_last_name(self) -> str:
|
@@ -60,13 +82,31 @@ class PatientDataExtractor:
|
|
60 |
names = patient.get("name", [])
|
61 |
for name in names:
|
62 |
if name.get("use") == "official" and "given" in name and len(name["given"]) > 1:
|
63 |
-
return name["given"][1][0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
return ""
|
65 |
|
|
|
66 |
def get_dob(self) -> str:
|
67 |
"""Extract patient's date of birth."""
|
68 |
-
|
69 |
-
return patient.get("birthDate", "")
|
70 |
|
71 |
def get_age(self) -> str:
|
72 |
"""Calculate patient's age based on birth date."""
|
@@ -78,12 +118,24 @@ class PatientDataExtractor:
|
|
78 |
age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
|
79 |
return str(age)
|
80 |
|
81 |
-
def
|
82 |
-
"""Extract patient's
|
|
|
|
|
|
|
|
|
83 |
patient = self._get_current_patient()
|
84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
86 |
-
|
|
|
87 |
"""Extract patient's street address."""
|
88 |
patient = self._get_current_patient()
|
89 |
addresses = patient.get("address", [])
|
@@ -107,20 +159,173 @@ class PatientDataExtractor:
|
|
107 |
addresses = patient.get("address", [])
|
108 |
return addresses[0]["postalCode"] if addresses and "postalCode" in addresses[0] else ""
|
109 |
|
110 |
-
def
|
111 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
return {
|
|
|
|
|
|
|
|
|
|
|
113 |
"first_name": self.get_first_name(),
|
114 |
"last_name": self.get_last_name(),
|
115 |
"middle_initial": self.get_middle_initial(),
|
|
|
|
|
116 |
"dob": self.get_dob(),
|
117 |
"age": self.get_age(),
|
118 |
-
"
|
119 |
-
"
|
|
|
|
|
120 |
"city": self.get_city(),
|
121 |
"state": self.get_state(),
|
122 |
"zip_code": self.get_zip_code(),
|
123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
"doctor_first_name": "",
|
125 |
"doctor_last_name": "",
|
126 |
"doctor_middle_initial": "",
|
@@ -143,20 +348,19 @@ class PatientDataExtractor:
|
|
143 |
}
|
144 |
|
145 |
def get_all_patients(self) -> List[Dict[str, str]]:
|
146 |
-
"""Return a list of dictionaries for all patients."""
|
147 |
original_idx = self.current_patient_idx
|
148 |
all_patients = []
|
149 |
for i in range(len(self.patients)):
|
150 |
self.set_patient_by_index(i)
|
151 |
all_patients.append(self.get_patient_dict())
|
152 |
-
self.set_patient_by_index(original_idx)
|
153 |
return all_patients
|
154 |
|
155 |
def get_patient_ids(self) -> List[str]:
|
156 |
"""Return a list of all patient IDs in the Bundle."""
|
157 |
return [patient["id"] for patient in self.patients]
|
158 |
|
159 |
-
|
160 |
# # Example usage with integration into app.py
|
161 |
# def integrate_with_app(patient_data: str):
|
162 |
# """Integrate PatientDataExtractor with the Gradio app."""
|
|
|
1 |
import json
|
2 |
from datetime import datetime
|
3 |
+
from typing import List, Dict, Optional, Union
|
4 |
|
5 |
class PatientDataExtractor:
|
6 |
+
"""Class to extract all fields from a FHIR Patient resource in a Bundle response."""
|
7 |
|
8 |
def __init__(self, patient_data: str):
|
9 |
"""Initialize with patient data in JSON string format."""
|
|
|
36 |
"""Get the currently selected patient resource."""
|
37 |
return self.patients[self.current_patient_idx]
|
38 |
|
39 |
+
# Basic Identification Fields
|
40 |
+
def get_id(self) -> str:
|
41 |
+
"""Extract FHIR Patient ID."""
|
42 |
+
return self._get_current_patient().get("id", "")
|
43 |
+
|
44 |
+
def get_resource_type(self) -> str:
|
45 |
+
"""Extract resource type (should always be 'Patient')."""
|
46 |
+
return self._get_current_patient().get("resourceType", "")
|
47 |
+
|
48 |
+
def get_meta_last_updated(self) -> str:
|
49 |
+
"""Extract last updated timestamp from meta."""
|
50 |
+
return self._get_current_patient().get("meta", {}).get("lastUpdated", "")
|
51 |
+
|
52 |
+
def get_meta_profile(self) -> List[str]:
|
53 |
+
"""Extract profile URIs from meta."""
|
54 |
+
return self._get_current_patient().get("meta", {}).get("profile", [])
|
55 |
+
|
56 |
+
def get_text_div(self) -> str:
|
57 |
+
"""Extract generated text narrative (div content)."""
|
58 |
+
return self._get_current_patient().get("text", {}).get("div", "")
|
59 |
+
|
60 |
+
# Name Fields
|
61 |
def get_first_name(self) -> str:
|
62 |
"""Extract patient's first name."""
|
63 |
patient = self._get_current_patient()
|
64 |
names = patient.get("name", [])
|
65 |
for name in names:
|
66 |
if name.get("use") == "official" and "given" in name:
|
67 |
+
return name["given"][0]
|
68 |
return ""
|
69 |
|
70 |
def get_last_name(self) -> str:
|
|
|
82 |
names = patient.get("name", [])
|
83 |
for name in names:
|
84 |
if name.get("use") == "official" and "given" in name and len(name["given"]) > 1:
|
85 |
+
return name["given"][1][0]
|
86 |
+
return ""
|
87 |
+
|
88 |
+
def get_name_prefix(self) -> str:
|
89 |
+
"""Extract patient's name prefix (e.g., Mr., Mrs.)."""
|
90 |
+
patient = self._get_current_patient()
|
91 |
+
names = patient.get("name", [])
|
92 |
+
for name in names:
|
93 |
+
if name.get("use") == "official" and "prefix" in name:
|
94 |
+
return name["prefix"][0]
|
95 |
+
return ""
|
96 |
+
|
97 |
+
def get_maiden_name(self) -> str:
|
98 |
+
"""Extract patient's maiden name if available."""
|
99 |
+
patient = self._get_current_patient()
|
100 |
+
names = patient.get("name", [])
|
101 |
+
for name in names:
|
102 |
+
if name.get("use") == "maiden" and "family" in name:
|
103 |
+
return name["family"]
|
104 |
return ""
|
105 |
|
106 |
+
# Demographic Fields
|
107 |
def get_dob(self) -> str:
|
108 |
"""Extract patient's date of birth."""
|
109 |
+
return self._get_current_patient().get("birthDate", "")
|
|
|
110 |
|
111 |
def get_age(self) -> str:
|
112 |
"""Calculate patient's age based on birth date."""
|
|
|
118 |
age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
|
119 |
return str(age)
|
120 |
|
121 |
+
def get_gender(self) -> str:
|
122 |
+
"""Extract patient's gender."""
|
123 |
+
return self._get_current_patient().get("gender", "").capitalize()
|
124 |
+
|
125 |
+
def get_birth_sex(self) -> str:
|
126 |
+
"""Extract patient's birth sex from extensions."""
|
127 |
patient = self._get_current_patient()
|
128 |
+
for ext in patient.get("extension", []):
|
129 |
+
if ext.get("url") == "http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex":
|
130 |
+
return ext.get("valueCode", "")
|
131 |
+
return ""
|
132 |
+
|
133 |
+
def get_multiple_birth(self) -> Union[bool, None]:
|
134 |
+
"""Extract multiple birth status."""
|
135 |
+
return self._get_current_patient().get("multipleBirthBoolean", None)
|
136 |
|
137 |
+
# Address Fields
|
138 |
+
def get_address_line(self) -> str:
|
139 |
"""Extract patient's street address."""
|
140 |
patient = self._get_current_patient()
|
141 |
addresses = patient.get("address", [])
|
|
|
159 |
addresses = patient.get("address", [])
|
160 |
return addresses[0]["postalCode"] if addresses and "postalCode" in addresses[0] else ""
|
161 |
|
162 |
+
def get_country(self) -> str:
|
163 |
+
"""Extract patient's country."""
|
164 |
+
patient = self._get_current_patient()
|
165 |
+
addresses = patient.get("address", [])
|
166 |
+
return addresses[0]["country"] if addresses and "country" in addresses[0] else ""
|
167 |
+
|
168 |
+
def get_geolocation(self) -> Dict[str, float]:
|
169 |
+
"""Extract geolocation (latitude and longitude) from address extension."""
|
170 |
+
patient = self._get_current_patient()
|
171 |
+
addresses = patient.get("address", [])
|
172 |
+
if not addresses:
|
173 |
+
return {"latitude": None, "longitude": None}
|
174 |
+
for ext in addresses[0].get("extension", []):
|
175 |
+
if ext.get("url") == "http://hl7.org/fhir/StructureDefinition/geolocation":
|
176 |
+
geo = {}
|
177 |
+
for sub_ext in ext.get("extension", []):
|
178 |
+
if sub_ext.get("url") == "latitude":
|
179 |
+
geo["latitude"] = sub_ext.get("valueDecimal")
|
180 |
+
elif sub_ext.get("url") == "longitude":
|
181 |
+
geo["longitude"] = sub_ext.get("valueDecimal")
|
182 |
+
return geo
|
183 |
+
return {"latitude": None, "longitude": None}
|
184 |
+
|
185 |
+
# Contact Fields
|
186 |
+
def get_phone(self) -> str:
|
187 |
+
"""Extract patient's phone number."""
|
188 |
+
patient = self._get_current_patient()
|
189 |
+
telecoms = patient.get("telecom", [])
|
190 |
+
for telecom in telecoms:
|
191 |
+
if telecom.get("system") == "phone" and telecom.get("use") == "home":
|
192 |
+
return telecom.get("value", "")
|
193 |
+
return ""
|
194 |
+
|
195 |
+
# Identifiers
|
196 |
+
def get_identifiers(self) -> Dict[str, str]:
|
197 |
+
"""Extract all identifiers (e.g., SSN, MRN, Driver's License)."""
|
198 |
+
patient = self._get_current_patient()
|
199 |
+
identifiers = patient.get("identifier", [])
|
200 |
+
id_dict = {}
|
201 |
+
for id_entry in identifiers:
|
202 |
+
id_type = id_entry.get("type", {}).get("text", "Unknown")
|
203 |
+
id_dict[id_type] = id_entry.get("value", "")
|
204 |
+
return id_dict
|
205 |
+
|
206 |
+
# Extensions
|
207 |
+
def get_race(self) -> str:
|
208 |
+
"""Extract patient's race from extensions."""
|
209 |
+
patient = self._get_current_patient()
|
210 |
+
for ext in patient.get("extension", []):
|
211 |
+
if ext.get("url") == "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race":
|
212 |
+
for sub_ext in ext.get("extension", []):
|
213 |
+
if sub_ext.get("url") == "text":
|
214 |
+
return sub_ext.get("valueString", "")
|
215 |
+
return ""
|
216 |
+
|
217 |
+
def get_ethnicity(self) -> str:
|
218 |
+
"""Extract patient's ethnicity from extensions."""
|
219 |
+
patient = self._get_current_patient()
|
220 |
+
for ext in patient.get("extension", []):
|
221 |
+
if ext.get("url") == "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity":
|
222 |
+
for sub_ext in ext.get("extension", []):
|
223 |
+
if sub_ext.get("url") == "text":
|
224 |
+
return sub_ext.get("valueString", "")
|
225 |
+
return ""
|
226 |
+
|
227 |
+
def get_mothers_maiden_name(self) -> str:
|
228 |
+
"""Extract patient's mother's maiden name from extensions."""
|
229 |
+
patient = self._get_current_patient()
|
230 |
+
for ext in patient.get("extension", []):
|
231 |
+
if ext.get("url") == "http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName":
|
232 |
+
return ext.get("valueString", "")
|
233 |
+
return ""
|
234 |
+
|
235 |
+
def get_birth_place(self) -> Dict[str, str]:
|
236 |
+
"""Extract patient's birth place from extensions."""
|
237 |
+
patient = self._get_current_patient()
|
238 |
+
for ext in patient.get("extension", []):
|
239 |
+
if ext.get("url") == "http://hl7.org/fhir/StructureDefinition/patient-birthPlace":
|
240 |
+
addr = ext.get("valueAddress", {})
|
241 |
+
return {
|
242 |
+
"city": addr.get("city", ""),
|
243 |
+
"state": addr.get("state", ""),
|
244 |
+
"country": addr.get("country", "")
|
245 |
+
}
|
246 |
+
return {"city": "", "state": "", "country": ""}
|
247 |
+
|
248 |
+
def get_disability_adjusted_life_years(self) -> Optional[float]:
|
249 |
+
"""Extract disability-adjusted life years from extensions."""
|
250 |
+
patient = self._get_current_patient()
|
251 |
+
for ext in patient.get("extension", []):
|
252 |
+
if ext.get("url") == "http://synthetichealth.github.io/synthea/disability-adjusted-life-years":
|
253 |
+
return ext.get("valueDecimal")
|
254 |
+
return None
|
255 |
+
|
256 |
+
def get_quality_adjusted_life_years(self) -> Optional[float]:
|
257 |
+
"""Extract quality-adjusted life years from extensions."""
|
258 |
+
patient = self._get_current_patient()
|
259 |
+
for ext in patient.get("extension", []):
|
260 |
+
if ext.get("url") == "http://synthetichealth.github.io/synthea/quality-adjusted-life-years":
|
261 |
+
return ext.get("valueDecimal")
|
262 |
+
return None
|
263 |
+
|
264 |
+
# Marital Status
|
265 |
+
def get_marital_status(self) -> str:
|
266 |
+
"""Extract patient's marital status."""
|
267 |
+
patient = self._get_current_patient()
|
268 |
+
status = patient.get("maritalStatus", {}).get("text", "")
|
269 |
+
return status if status else patient.get("maritalStatus", {}).get("coding", [{}])[0].get("display", "")
|
270 |
+
|
271 |
+
# Communication
|
272 |
+
def get_language(self) -> str:
|
273 |
+
"""Extract patient's preferred language."""
|
274 |
+
patient = self._get_current_patient()
|
275 |
+
comms = patient.get("communication", [])
|
276 |
+
return comms[0]["language"]["text"] if comms and "language" in comms[0] else ""
|
277 |
+
|
278 |
+
# Comprehensive Extraction
|
279 |
+
def get_all_patient_data(self) -> Dict[str, Union[str, Dict, List, float, bool, None]]:
|
280 |
+
"""Extract all available data for the current patient."""
|
281 |
return {
|
282 |
+
"id": self.get_id(),
|
283 |
+
"resource_type": self.get_resource_type(),
|
284 |
+
"meta_last_updated": self.get_meta_last_updated(),
|
285 |
+
"meta_profile": self.get_meta_profile(),
|
286 |
+
"text_div": self.get_text_div(),
|
287 |
"first_name": self.get_first_name(),
|
288 |
"last_name": self.get_last_name(),
|
289 |
"middle_initial": self.get_middle_initial(),
|
290 |
+
"name_prefix": self.get_name_prefix(),
|
291 |
+
"maiden_name": self.get_maiden_name(),
|
292 |
"dob": self.get_dob(),
|
293 |
"age": self.get_age(),
|
294 |
+
"gender": self.get_gender(),
|
295 |
+
"birth_sex": self.get_birth_sex(),
|
296 |
+
"multiple_birth": self.get_multiple_birth(),
|
297 |
+
"address_line": self.get_address_line(),
|
298 |
"city": self.get_city(),
|
299 |
"state": self.get_state(),
|
300 |
"zip_code": self.get_zip_code(),
|
301 |
+
"country": self.get_country(),
|
302 |
+
"geolocation": self.get_geolocation(),
|
303 |
+
"phone": self.get_phone(),
|
304 |
+
"identifiers": self.get_identifiers(),
|
305 |
+
"race": self.get_race(),
|
306 |
+
"ethnicity": self.get_ethnicity(),
|
307 |
+
"mothers_maiden_name": self.get_mothers_maiden_name(),
|
308 |
+
"birth_place": self.get_birth_place(),
|
309 |
+
"disability_adjusted_life_years": self.get_disability_adjusted_life_years(),
|
310 |
+
"quality_adjusted_life_years": self.get_quality_adjusted_life_years(),
|
311 |
+
"marital_status": self.get_marital_status(),
|
312 |
+
"language": self.get_language()
|
313 |
+
}
|
314 |
+
|
315 |
+
def get_patient_dict(self) -> Dict[str, str]:
|
316 |
+
"""Return a dictionary of patient data mapped to discharge form fields (for app.py compatibility)."""
|
317 |
+
patient_data = self.get_all_patient_data()
|
318 |
+
return {
|
319 |
+
"first_name": patient_data["first_name"],
|
320 |
+
"last_name": patient_data["last_name"],
|
321 |
+
"middle_initial": patient_data["middle_initial"],
|
322 |
+
"dob": patient_data["dob"],
|
323 |
+
"age": patient_data["age"],
|
324 |
+
"sex": patient_data["gender"],
|
325 |
+
"address": patient_data["address_line"],
|
326 |
+
"city": patient_data["city"],
|
327 |
+
"state": patient_data["state"],
|
328 |
+
"zip_code": patient_data["zip_code"],
|
329 |
"doctor_first_name": "",
|
330 |
"doctor_last_name": "",
|
331 |
"doctor_middle_initial": "",
|
|
|
348 |
}
|
349 |
|
350 |
def get_all_patients(self) -> List[Dict[str, str]]:
|
351 |
+
"""Return a list of dictionaries for all patients (for app.py)."""
|
352 |
original_idx = self.current_patient_idx
|
353 |
all_patients = []
|
354 |
for i in range(len(self.patients)):
|
355 |
self.set_patient_by_index(i)
|
356 |
all_patients.append(self.get_patient_dict())
|
357 |
+
self.set_patient_by_index(original_idx)
|
358 |
return all_patients
|
359 |
|
360 |
def get_patient_ids(self) -> List[str]:
|
361 |
"""Return a list of all patient IDs in the Bundle."""
|
362 |
return [patient["id"] for patient in self.patients]
|
363 |
|
|
|
364 |
# # Example usage with integration into app.py
|
365 |
# def integrate_with_app(patient_data: str):
|
366 |
# """Integrate PatientDataExtractor with the Gradio app."""
|