Fix: Add logger and adjustment to update Column and stripes
Browse files- callbackmanager.py +189 -40
callbackmanager.py
CHANGED
@@ -4,6 +4,12 @@ import json
|
|
4 |
import os
|
5 |
import tempfile
|
6 |
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
# Import PDF utilities
|
9 |
from pdfutils import PDFGenerator, generate_discharge_summary
|
@@ -31,12 +37,81 @@ class CallbackManager:
|
|
31 |
return "Authentication failed. Please check the code."
|
32 |
|
33 |
def get_patient_data(self) -> str:
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
def get_patient_documents(self, patient_id: str = None):
|
42 |
"""Fetch patient documents from MeldRx"""
|
@@ -156,13 +231,13 @@ def create_patient_stripe(patient):
|
|
156 |
def update_components(selected_status):
|
157 |
visible = selected_status in ["Reviewed", "Approved"]
|
158 |
return (
|
159 |
-
|
160 |
-
|
161 |
)
|
162 |
|
163 |
def show_documents(patient_id):
|
164 |
docs = CALLBACK_MANAGER.get_patient_documents(patient_id)
|
165 |
-
return
|
166 |
|
167 |
status.change(
|
168 |
update_components,
|
@@ -190,22 +265,48 @@ def update_patient_display(patient_data_json):
|
|
190 |
"""
|
191 |
try:
|
192 |
# Handle the case where patient_data_json is an error message
|
193 |
-
if patient_data_json.startswith("Not authenticated") or patient_data_json.startswith("Failed"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
return (
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
None
|
199 |
)
|
200 |
|
201 |
-
fhir_bundle = json.loads(patient_data_json) if isinstance(patient_data_json, str) else patient_data_json
|
202 |
patients = process_fhir_bundle(fhir_bundle)
|
203 |
|
204 |
if not patients:
|
|
|
205 |
return (
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
None
|
210 |
)
|
211 |
|
@@ -213,26 +314,36 @@ def update_patient_display(patient_data_json):
|
|
213 |
has_prev = any(link.get("relation") == "previous" for link in fhir_bundle.get("link", []))
|
214 |
has_next = any(link.get("relation") == "next" for link in fhir_bundle.get("link", []))
|
215 |
|
216 |
-
# Create stripes
|
217 |
-
stripes = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
218 |
|
219 |
return (
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
**Bundle Metadata**
|
224 |
Type: {fhir_bundle.get('type', 'Unknown')}
|
225 |
Total Patients: {fhir_bundle.get('total', len(patients))}
|
226 |
Last Updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
227 |
-
"""
|
228 |
{"patients": patients, "bundle": fhir_bundle}
|
229 |
)
|
230 |
except Exception as e:
|
231 |
-
|
|
|
232 |
return (
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
None
|
237 |
)
|
238 |
|
@@ -479,20 +590,58 @@ with gr.Blocks() as demo:
|
|
479 |
outputs=pdf_output
|
480 |
)
|
481 |
|
482 |
-
# Define a function to handle fetching and displaying patients
|
483 |
def fetch_and_display_patients():
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
496 |
|
497 |
# Connect the patient data button to both display the raw JSON and update the patient display
|
498 |
patient_data_button.click(
|
|
|
4 |
import os
|
5 |
import tempfile
|
6 |
from datetime import datetime
|
7 |
+
import traceback
|
8 |
+
import logging
|
9 |
+
|
10 |
+
# Set up logging
|
11 |
+
logging.basicConfig(level=logging.INFO)
|
12 |
+
logger = logging.getLogger(__name__)
|
13 |
|
14 |
# Import PDF utilities
|
15 |
from pdfutils import PDFGenerator, generate_discharge_summary
|
|
|
37 |
return "Authentication failed. Please check the code."
|
38 |
|
39 |
def get_patient_data(self) -> str:
|
40 |
+
"""Fetch patient data from MeldRx"""
|
41 |
+
try:
|
42 |
+
if not self.access_token:
|
43 |
+
logger.warning("Not authenticated when getting patient data")
|
44 |
+
return "Not authenticated. Please provide a valid authorization code first."
|
45 |
+
|
46 |
+
# For demo purposes, if there's no actual API connected, return mock data
|
47 |
+
# Remove this in production and use the real API call
|
48 |
+
if not hasattr(self.api, 'get_patients') or self.api.get_patients is None:
|
49 |
+
logger.info("Using mock patient data (no API connection)")
|
50 |
+
# Return mock FHIR bundle with patient data
|
51 |
+
mock_data = {
|
52 |
+
"resourceType": "Bundle",
|
53 |
+
"type": "searchset",
|
54 |
+
"total": 2,
|
55 |
+
"link": [],
|
56 |
+
"entry": [
|
57 |
+
{
|
58 |
+
"resource": {
|
59 |
+
"resourceType": "Patient",
|
60 |
+
"id": "patient1",
|
61 |
+
"name": [
|
62 |
+
{
|
63 |
+
"use": "official",
|
64 |
+
"family": "Smith",
|
65 |
+
"given": ["John"]
|
66 |
+
}
|
67 |
+
],
|
68 |
+
"gender": "male",
|
69 |
+
"birthDate": "1970-01-01",
|
70 |
+
"address": [
|
71 |
+
{
|
72 |
+
"city": "Boston",
|
73 |
+
"state": "MA",
|
74 |
+
"postalCode": "02108"
|
75 |
+
}
|
76 |
+
]
|
77 |
+
}
|
78 |
+
},
|
79 |
+
{
|
80 |
+
"resource": {
|
81 |
+
"resourceType": "Patient",
|
82 |
+
"id": "patient2",
|
83 |
+
"name": [
|
84 |
+
{
|
85 |
+
"use": "official",
|
86 |
+
"family": "Johnson",
|
87 |
+
"given": ["Jane"]
|
88 |
+
}
|
89 |
+
],
|
90 |
+
"gender": "female",
|
91 |
+
"birthDate": "1985-05-15",
|
92 |
+
"address": [
|
93 |
+
{
|
94 |
+
"city": "Cambridge",
|
95 |
+
"state": "MA",
|
96 |
+
"postalCode": "02139"
|
97 |
+
}
|
98 |
+
]
|
99 |
+
}
|
100 |
+
}
|
101 |
+
]
|
102 |
+
}
|
103 |
+
return json.dumps(mock_data, indent=2)
|
104 |
+
|
105 |
+
# Real implementation with API call
|
106 |
+
logger.info("Calling Meldrx API to get patients")
|
107 |
+
patients = self.api.get_patients()
|
108 |
+
if patients is not None:
|
109 |
+
return json.dumps(patients, indent=2) if patients else "No patient data returned."
|
110 |
+
return "Failed to retrieve patient data."
|
111 |
+
except Exception as e:
|
112 |
+
error_msg = f"Error in get_patient_data: {str(e)}"
|
113 |
+
logger.error(error_msg)
|
114 |
+
return f"Error retrieving patient data: {str(e)}"
|
115 |
|
116 |
def get_patient_documents(self, patient_id: str = None):
|
117 |
"""Fetch patient documents from MeldRx"""
|
|
|
231 |
def update_components(selected_status):
|
232 |
visible = selected_status in ["Reviewed", "Approved"]
|
233 |
return (
|
234 |
+
{"visible": visible}, # For generate_btn
|
235 |
+
{"visible": visible, "value": {"Low": 0.3, "Medium": 0.6, "High": 0.9}.get(selected_status, 0.5)} # For probability
|
236 |
)
|
237 |
|
238 |
def show_documents(patient_id):
|
239 |
docs = CALLBACK_MANAGER.get_patient_documents(patient_id)
|
240 |
+
return {"value": docs, "visible": True} # For doc_output
|
241 |
|
242 |
status.change(
|
243 |
update_components,
|
|
|
265 |
"""
|
266 |
try:
|
267 |
# Handle the case where patient_data_json is an error message
|
268 |
+
if isinstance(patient_data_json, str) and (patient_data_json.startswith("Not authenticated") or patient_data_json.startswith("Failed")):
|
269 |
+
logger.warning(f"Error in patient data: {patient_data_json}")
|
270 |
+
return (
|
271 |
+
{"visible": False}, # For pagination_row
|
272 |
+
[], # For patient_stripes
|
273 |
+
{"value": f"Error: {patient_data_json}"}, # For metadata_md
|
274 |
+
None # For patient_data_state
|
275 |
+
)
|
276 |
+
|
277 |
+
# Parse JSON if it's a string
|
278 |
+
if isinstance(patient_data_json, str):
|
279 |
+
try:
|
280 |
+
fhir_bundle = json.loads(patient_data_json)
|
281 |
+
except json.JSONDecodeError as e:
|
282 |
+
logger.error(f"JSON decode error: {str(e)} - Data: {patient_data_json[:100]}...")
|
283 |
+
return (
|
284 |
+
{"visible": False},
|
285 |
+
[],
|
286 |
+
{"value": f"Invalid JSON data: {str(e)}"},
|
287 |
+
None
|
288 |
+
)
|
289 |
+
else:
|
290 |
+
fhir_bundle = patient_data_json
|
291 |
+
|
292 |
+
# Safety check
|
293 |
+
if not isinstance(fhir_bundle, dict):
|
294 |
+
logger.error(f"Unexpected data type: {type(fhir_bundle)}")
|
295 |
return (
|
296 |
+
{"visible": False},
|
297 |
+
[],
|
298 |
+
{"value": f"Expected dictionary but got {type(fhir_bundle)}"},
|
299 |
None
|
300 |
)
|
301 |
|
|
|
302 |
patients = process_fhir_bundle(fhir_bundle)
|
303 |
|
304 |
if not patients:
|
305 |
+
logger.warning("No patients found in data")
|
306 |
return (
|
307 |
+
{"visible": False},
|
308 |
+
[],
|
309 |
+
{"value": "No patients found in the data"},
|
310 |
None
|
311 |
)
|
312 |
|
|
|
314 |
has_prev = any(link.get("relation") == "previous" for link in fhir_bundle.get("link", []))
|
315 |
has_next = any(link.get("relation") == "next" for link in fhir_bundle.get("link", []))
|
316 |
|
317 |
+
# Create stripes - handle list vs single patient
|
318 |
+
stripes = []
|
319 |
+
for patient in patients:
|
320 |
+
try:
|
321 |
+
stripe = create_patient_stripe(patient)
|
322 |
+
stripes.append(stripe)
|
323 |
+
except Exception as e:
|
324 |
+
logger.error(f"Error creating patient stripe: {str(e)}")
|
325 |
+
# Continue with other patients if one fails
|
326 |
+
|
327 |
+
logger.info(f"Created {len(stripes)} patient stripes successfully")
|
328 |
|
329 |
return (
|
330 |
+
{"visible": has_prev or has_next},
|
331 |
+
stripes,
|
332 |
+
{"value": f"""
|
333 |
**Bundle Metadata**
|
334 |
Type: {fhir_bundle.get('type', 'Unknown')}
|
335 |
Total Patients: {fhir_bundle.get('total', len(patients))}
|
336 |
Last Updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
337 |
+
"""},
|
338 |
{"patients": patients, "bundle": fhir_bundle}
|
339 |
)
|
340 |
except Exception as e:
|
341 |
+
error_msg = f"Error loading patient data: {str(e)}\n{traceback.format_exc()}"
|
342 |
+
logger.error(error_msg)
|
343 |
return (
|
344 |
+
{"visible": False},
|
345 |
+
[],
|
346 |
+
{"value": error_msg},
|
347 |
None
|
348 |
)
|
349 |
|
|
|
590 |
outputs=pdf_output
|
591 |
)
|
592 |
|
593 |
+
# Define a function to handle fetching and displaying patients with better error handling
|
594 |
def fetch_and_display_patients():
|
595 |
+
try:
|
596 |
+
logger.info("Fetching patient data...")
|
597 |
+
# Get data from API
|
598 |
+
data = CALLBACK_MANAGER.get_patient_data()
|
599 |
+
logger.info(f"Received patient data: {data[:100]}..." if isinstance(data, str) else "Received non-string data")
|
600 |
+
|
601 |
+
# Handle case when the data is just an error message
|
602 |
+
if data.startswith("Not authenticated") or data.startswith("Failed"):
|
603 |
+
logger.warning(f"Authentication issue: {data}")
|
604 |
+
return [
|
605 |
+
data, # For the raw JSON output
|
606 |
+
{"visible": False}, # For pagination_row
|
607 |
+
[], # For patient_stripes
|
608 |
+
{"value": f"Error: {data}"}, # For metadata_md
|
609 |
+
None # For patient_data_state
|
610 |
+
]
|
611 |
+
|
612 |
+
# Process the results for UI display
|
613 |
+
try:
|
614 |
+
pagination_update, stripes_update, metadata_update, state_update = update_patient_display(data)
|
615 |
+
logger.info("Successfully processed patient display data")
|
616 |
+
|
617 |
+
return [
|
618 |
+
data, # For the raw JSON output
|
619 |
+
pagination_update,
|
620 |
+
stripes_update,
|
621 |
+
metadata_update,
|
622 |
+
state_update
|
623 |
+
]
|
624 |
+
except Exception as e:
|
625 |
+
logger.error(f"Error in update_patient_display: {str(e)}\n{traceback.format_exc()}")
|
626 |
+
# Return at least the raw data, even if display processing fails
|
627 |
+
return [
|
628 |
+
data,
|
629 |
+
{"visible": False},
|
630 |
+
[],
|
631 |
+
{"value": f"Error processing patient data: {str(e)}"},
|
632 |
+
None
|
633 |
+
]
|
634 |
+
|
635 |
+
except Exception as e:
|
636 |
+
error_msg = f"Unexpected error fetching patient data: {str(e)}\n{traceback.format_exc()}"
|
637 |
+
logger.error(error_msg)
|
638 |
+
return [
|
639 |
+
f"Error: {str(e)}",
|
640 |
+
{"visible": False},
|
641 |
+
[],
|
642 |
+
{"value": error_msg},
|
643 |
+
None
|
644 |
+
]
|
645 |
|
646 |
# Connect the patient data button to both display the raw JSON and update the patient display
|
647 |
patient_data_button.click(
|