Update callbackmanager.py
Browse files- callbackmanager.py +161 -152
callbackmanager.py
CHANGED
|
@@ -121,15 +121,14 @@ def analyze_dicom_content_ai(dicom_metadata_json): # Copied from your code
|
|
| 121 |
top_p=0.9,
|
| 122 |
)
|
| 123 |
the_response = response.choices[0].message.content
|
| 124 |
-
return the_response,
|
| 125 |
|
| 126 |
except Exception as e:
|
| 127 |
error_message = f"AI Analysis Error in analyze_dicom_content_ai (DICOM Metadata): {e}"
|
| 128 |
trace_data_detail_dicom_analysis["error"] = f"AI Analysis Error: {e}"
|
| 129 |
-
return error_message,
|
| 130 |
|
| 131 |
|
| 132 |
-
# ... (Paste other AI analysis functions: analyze_hl7_file_with_ai, analyze_cda_xml_file_with_ai, analyze_pdf_file_with_ai, analyze_csv_file_with_ai here - ensure to adapt file reading for Gradio file paths if necessary) ...
|
| 133 |
def analyze_hl7_file_with_ai(hl7_file_path):
|
| 134 |
"""Analyzes HL7 file content using Discharge Guard AI."""
|
| 135 |
try:
|
|
@@ -293,7 +292,7 @@ def analyze_pdf_content_ai(pdf_text_content): # Copied from your code
|
|
| 293 |
```text
|
| 294 |
{pdf_text_content}
|
| 295 |
```
|
| 296 |
-
* Remember, this analysis is for conceptual informational purposes only and **NOT medical advice.** Focus on
|
| 297 |
"""
|
| 298 |
|
| 299 |
trace_data_detail_pdf_analysis = {
|
|
@@ -387,7 +386,6 @@ def analyze_csv_content_ai(csv_content_string): # Copied from your code
|
|
| 387 |
return error_message, trace_data_detail_csv_analysis
|
| 388 |
|
| 389 |
|
| 390 |
-
# ... (CallbackManager, display_form, generate_pdf_from_form, generate_pdf_from_meldrx, generate_discharge_paper_one_click, client initialization remain the same) ...
|
| 391 |
class CallbackManager:
|
| 392 |
def __init__(self, redirect_uri: str, client_secret: str = None):
|
| 393 |
client_id = os.getenv("APPID")
|
|
@@ -408,71 +406,16 @@ class CallbackManager:
|
|
| 408 |
if self.api.authenticate_with_code(code):
|
| 409 |
self.access_token = self.api.access_token
|
| 410 |
return (
|
| 411 |
-
f"<span style='color:#00FF7F;'>Authentication successful!</span> Access Token: {self.access_token[:10]}... (truncated)"
|
| 412 |
)
|
| 413 |
-
return "<span style='color:#FF4500;'>Authentication failed. Please check the code.</span>"
|
| 414 |
|
| 415 |
def get_patient_data(self) -> str:
|
| 416 |
"""Fetch patient data from MeldRx"""
|
| 417 |
try:
|
| 418 |
if not self.access_token:
|
| 419 |
logger.warning("Not authenticated when getting patient data")
|
| 420 |
-
return "<span style='color:#FF8C00;'>Not authenticated. Please provide a valid authorization code first.</span>"
|
| 421 |
-
|
| 422 |
-
# For demo purposes, if there's no actual API connected, return mock data
|
| 423 |
-
# Remove this in production and use the real API call
|
| 424 |
-
if not hasattr(self.api, "get_patients") or self.api.get_patients is None:
|
| 425 |
-
logger.info("Using mock patient data (no API connection)")
|
| 426 |
-
# Return mock FHIR bundle with patient data
|
| 427 |
-
mock_data = {
|
| 428 |
-
"resourceType": "Bundle",
|
| 429 |
-
"type": "searchset",
|
| 430 |
-
"total": 2,
|
| 431 |
-
"link": [],
|
| 432 |
-
"entry": [
|
| 433 |
-
{
|
| 434 |
-
"resource": {
|
| 435 |
-
"resourceType": "Patient",
|
| 436 |
-
"id": "patient1",
|
| 437 |
-
"name": [
|
| 438 |
-
{
|
| 439 |
-
"use": "official",
|
| 440 |
-
"family": "Smith",
|
| 441 |
-
"given": ["John"],
|
| 442 |
-
}
|
| 443 |
-
],
|
| 444 |
-
"gender": "male",
|
| 445 |
-
"birthDate": "1970-01-01",
|
| 446 |
-
"address": [
|
| 447 |
-
{"city": "Boston", "state": "MA", "postalCode": "02108"}
|
| 448 |
-
],
|
| 449 |
-
}
|
| 450 |
-
},
|
| 451 |
-
{
|
| 452 |
-
"resource": {
|
| 453 |
-
"resourceType": "Patient",
|
| 454 |
-
"id": "patient2",
|
| 455 |
-
"name": [
|
| 456 |
-
{
|
| 457 |
-
"use": "official",
|
| 458 |
-
"family": "Johnson",
|
| 459 |
-
"given": ["Jane"],
|
| 460 |
-
}
|
| 461 |
-
],
|
| 462 |
-
"gender": "female",
|
| 463 |
-
"birthDate": "1985-05-15",
|
| 464 |
-
"address": [
|
| 465 |
-
{
|
| 466 |
-
"city": "Cambridge",
|
| 467 |
-
"state": "MA",
|
| 468 |
-
"postalCode": "02139",
|
| 469 |
-
}
|
| 470 |
-
],
|
| 471 |
-
}
|
| 472 |
-
},
|
| 473 |
-
],
|
| 474 |
-
}
|
| 475 |
-
return json.dumps(mock_data, indent=2)
|
| 476 |
|
| 477 |
# Real implementation with API call
|
| 478 |
logger.info("Calling Meldrx API to get patients")
|
|
@@ -481,19 +424,18 @@ class CallbackManager:
|
|
| 481 |
return (
|
| 482 |
json.dumps(patients, indent=2)
|
| 483 |
if patients
|
| 484 |
-
else "<span style='color:#FFFF00;'>No patient data returned.</span>"
|
| 485 |
)
|
| 486 |
-
return "<span style='color:#DC143C;'>Failed to retrieve patient data.</span>"
|
| 487 |
except Exception as e:
|
| 488 |
error_msg = f"Error in get_patient_data: {str(e)}"
|
| 489 |
logger.error(error_msg)
|
| 490 |
-
return f"<span style='color:#FF6347;'>Error retrieving patient data: {str(e)}</span> {str(e)}"
|
| 491 |
-
|
| 492 |
|
| 493 |
def get_patient_documents(self, patient_id: str = None):
|
| 494 |
"""Fetch patient documents from MeldRx"""
|
| 495 |
if not self.access_token:
|
| 496 |
-
return "<span style='color:#FF8C00;'>Not authenticated. Please provide a valid authorization code first.</span>"
|
| 497 |
|
| 498 |
try:
|
| 499 |
# This would call the actual MeldRx API to get documents for a specific patient
|
|
@@ -515,7 +457,7 @@ class CallbackManager:
|
|
| 515 |
},
|
| 516 |
]
|
| 517 |
except Exception as e:
|
| 518 |
-
return f"<span style='color:#FF6347;'>Error retrieving patient documents: {str(e)}</span>: {str(e)}"
|
| 519 |
|
| 520 |
|
| 521 |
def display_form(
|
|
@@ -867,47 +809,76 @@ cyberpunk_theme = gr.themes.Monochrome(
|
|
| 867 |
primary_hue="cyan",
|
| 868 |
secondary_hue="pink",
|
| 869 |
neutral_hue="slate",
|
| 870 |
-
font=["Source Code Pro", "monospace"],
|
| 871 |
-
font_mono=["Source Code Pro", "monospace"]
|
| 872 |
)
|
| 873 |
|
| 874 |
# Create the UI with the cyberpunk theme
|
| 875 |
-
with gr.Blocks(theme=cyberpunk_theme) as demo:
|
| 876 |
-
gr.Markdown(
|
| 877 |
-
|
| 878 |
-
|
| 879 |
-
|
| 880 |
-
|
| 881 |
-
gr.Markdown(
|
| 882 |
-
|
| 883 |
-
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 891 |
patient_data_output = gr.Textbox(label="Patient Data", lines=10)
|
| 892 |
|
| 893 |
# Add button to generate PDF from MeldRx data (No AI)
|
| 894 |
-
meldrx_pdf_button = gr.Button(
|
| 895 |
-
|
| 896 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 897 |
|
| 898 |
def process_redirected_url(redirected_url):
|
| 899 |
"""Processes the redirected URL to extract and display the authorization code."""
|
| 900 |
auth_code, error_message = extract_auth_code_from_url(redirected_url)
|
| 901 |
if auth_code:
|
| 902 |
-
return auth_code, "<span style='color:#00FF7F;'>Authorization code extracted!</span>"
|
| 903 |
else:
|
| 904 |
-
return "", f"<span style='color:#FF4500;'>Could not extract authorization code.</span> {error_message or ''}"
|
| 905 |
-
|
| 906 |
|
| 907 |
extract_code_button.click(
|
| 908 |
fn=process_redirected_url,
|
| 909 |
inputs=redirected_url_input,
|
| 910 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
| 911 |
)
|
| 912 |
|
| 913 |
auth_submit.click(
|
|
@@ -916,11 +887,19 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 916 |
outputs=auth_result,
|
| 917 |
)
|
| 918 |
|
| 919 |
-
with gr.Tab(
|
| 920 |
-
|
| 921 |
-
|
| 922 |
-
|
| 923 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 924 |
|
| 925 |
# Simple function to update dashboard based on fetched data
|
| 926 |
def update_dashboard():
|
|
@@ -931,7 +910,7 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 931 |
or data.startswith("<span style='color:#DC143C;'>Failed")
|
| 932 |
or data.startswith("<span style='color:#FF6347;'>Error")
|
| 933 |
):
|
| 934 |
-
return f"<p style='color:#FF8C00;'>{data}</p>"
|
| 935 |
|
| 936 |
try:
|
| 937 |
# Parse the data
|
|
@@ -945,7 +924,7 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 945 |
patients.append(resource)
|
| 946 |
|
| 947 |
# Generate HTML card
|
| 948 |
-
html = "<h3 style='color:#00FFFF; text-shadow: 0 0 2px #00FFFF;'>Patients</h3>"
|
| 949 |
for patient in patients:
|
| 950 |
# Extract name
|
| 951 |
name = patient.get("name", [{}])[0]
|
|
@@ -968,13 +947,16 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 968 |
|
| 969 |
return html
|
| 970 |
except Exception as e:
|
| 971 |
-
return f"<p style='color:#FF6347;'>Error parsing patient data: {str(e)}</p>"
|
| 972 |
except Exception as e:
|
| 973 |
-
return f"<p style='color:#FF6347;'>Error fetching patient data: {str(e)}</p>"
|
| 974 |
|
|
|
|
| 975 |
|
| 976 |
-
with gr.Tab("Discharge Form", elem_classes="cyberpunk-tab"):
|
| 977 |
-
gr.Markdown(
|
|
|
|
|
|
|
| 978 |
with gr.Row():
|
| 979 |
first_name = gr.Textbox(label="First Name")
|
| 980 |
last_name = gr.Textbox(label="Last Name")
|
|
@@ -988,7 +970,9 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 988 |
city = gr.Textbox(label="City")
|
| 989 |
state = gr.Textbox(label="State")
|
| 990 |
zip_code = gr.Textbox(label="Zip Code")
|
| 991 |
-
gr.Markdown(
|
|
|
|
|
|
|
| 992 |
with gr.Row():
|
| 993 |
doctor_first_name = gr.Textbox(label="Doctor's First Name")
|
| 994 |
doctor_last_name = gr.Textbox(label="Doctor's Last Name")
|
|
@@ -999,7 +983,9 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 999 |
doctor_city = gr.Textbox(label="City")
|
| 1000 |
doctor_state = gr.Textbox(label="State")
|
| 1001 |
doctor_zip = gr.Textbox(label="Zip Code")
|
| 1002 |
-
gr.Markdown(
|
|
|
|
|
|
|
| 1003 |
with gr.Row():
|
| 1004 |
admission_date = gr.Textbox(label="Date of Admission")
|
| 1005 |
referral_source = gr.Textbox(label="Source of Referral")
|
|
@@ -1011,24 +997,36 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 1011 |
label="Discharge Reason",
|
| 1012 |
)
|
| 1013 |
date_of_death = gr.Textbox(label="Date of Death (if applicable)")
|
| 1014 |
-
gr.Markdown(
|
|
|
|
|
|
|
| 1015 |
diagnosis = gr.Textbox(label="Diagnosis")
|
| 1016 |
procedures = gr.Textbox(label="Operation & Procedures")
|
| 1017 |
-
gr.Markdown(
|
|
|
|
|
|
|
| 1018 |
medications = gr.Textbox(label="Medication on Discharge")
|
| 1019 |
-
gr.Markdown(
|
|
|
|
|
|
|
| 1020 |
with gr.Row():
|
| 1021 |
preparer_name = gr.Textbox(label="Name")
|
| 1022 |
preparer_job_title = gr.Textbox(label="Job Title")
|
| 1023 |
|
| 1024 |
# Add buttons for both display form and generate PDF
|
| 1025 |
with gr.Row():
|
| 1026 |
-
submit_display = gr.Button(
|
| 1027 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1028 |
|
| 1029 |
# Output areas
|
| 1030 |
-
form_output = gr.HTML()
|
| 1031 |
-
pdf_output = gr.File(
|
|
|
|
|
|
|
| 1032 |
|
| 1033 |
# Connect the display form button
|
| 1034 |
submit_display.click(
|
|
@@ -1064,7 +1062,7 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 1064 |
preparer_name,
|
| 1065 |
preparer_job_title,
|
| 1066 |
],
|
| 1067 |
-
outputs=form_output
|
| 1068 |
)
|
| 1069 |
|
| 1070 |
# Connect the generate PDF button (No AI version)
|
|
@@ -1101,35 +1099,44 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 1101 |
preparer_name,
|
| 1102 |
preparer_job_title,
|
| 1103 |
],
|
| 1104 |
-
outputs=pdf_output
|
| 1105 |
)
|
| 1106 |
|
| 1107 |
-
with gr.Tab(
|
| 1108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1109 |
with gr.Column():
|
| 1110 |
dicom_file = gr.File(
|
| 1111 |
file_types=[".dcm"], label="Upload DICOM File (.dcm)"
|
| 1112 |
)
|
| 1113 |
dicom_ai_output = gr.Textbox(label="DICOM Analysis Report", lines=5)
|
| 1114 |
-
analyze_dicom_button = gr.Button(
|
|
|
|
|
|
|
| 1115 |
|
| 1116 |
-
hl7_file = gr.File(
|
| 1117 |
-
file_types=[".hl7"], label="Upload HL7 File (.hl7)"
|
| 1118 |
-
)
|
| 1119 |
hl7_ai_output = gr.Textbox(label="HL7 Analysis Report", lines=5)
|
| 1120 |
-
analyze_hl7_button = gr.Button(
|
|
|
|
|
|
|
| 1121 |
|
| 1122 |
-
xml_file = gr.File(
|
| 1123 |
-
file_types=[".xml"], label="Upload XML File (.xml)"
|
| 1124 |
-
)
|
| 1125 |
xml_ai_output = gr.Textbox(label="XML Analysis Report", lines=5)
|
| 1126 |
-
analyze_xml_button = gr.Button(
|
|
|
|
|
|
|
| 1127 |
|
| 1128 |
ccda_file = gr.File(
|
| 1129 |
-
file_types=[".xml", ".cda", ".ccd"],
|
|
|
|
| 1130 |
)
|
| 1131 |
ccda_ai_output = gr.Textbox(label="CCDA Analysis Report", lines=5)
|
| 1132 |
-
analyze_ccda_button = gr.Button(
|
|
|
|
|
|
|
| 1133 |
|
| 1134 |
ccd_file = gr.File(
|
| 1135 |
file_types=[".ccd"],
|
|
@@ -1138,44 +1145,46 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 1138 |
ccd_ai_output = gr.Textbox(
|
| 1139 |
label="CCD Analysis Report", lines=5
|
| 1140 |
) # Redundant
|
| 1141 |
-
analyze_ccd_button = gr.Button(
|
| 1142 |
-
|
| 1143 |
-
|
| 1144 |
-
)
|
| 1145 |
pdf_ai_output = gr.Textbox(label="PDF Analysis Report", lines=5)
|
| 1146 |
-
analyze_pdf_button = gr.Button(
|
|
|
|
|
|
|
| 1147 |
|
| 1148 |
-
csv_file = gr.File(
|
| 1149 |
-
file_types=[".csv"], label="Upload CSV File (.csv)"
|
| 1150 |
-
)
|
| 1151 |
csv_ai_output = gr.Textbox(label="CSV Analysis Report", lines=5)
|
| 1152 |
-
analyze_csv_button = gr.Button(
|
|
|
|
|
|
|
| 1153 |
|
| 1154 |
# Connect AI Analysis Buttons - using REAL AI functions now
|
| 1155 |
analyze_dicom_button.click(
|
| 1156 |
analyze_dicom_file_with_ai, # Call REAL AI function
|
| 1157 |
inputs=dicom_file,
|
| 1158 |
-
outputs=dicom_ai_output
|
| 1159 |
)
|
| 1160 |
analyze_hl7_button.click(
|
| 1161 |
analyze_hl7_file_with_ai, # Call REAL AI function
|
| 1162 |
inputs=hl7_file,
|
| 1163 |
-
outputs=hl7_ai_output
|
| 1164 |
)
|
| 1165 |
analyze_xml_button.click(
|
| 1166 |
analyze_cda_xml_file_with_ai, # Call REAL AI function
|
| 1167 |
inputs=xml_file,
|
| 1168 |
-
outputs=xml_ai_output
|
| 1169 |
)
|
| 1170 |
analyze_ccda_button.click(
|
| 1171 |
analyze_cda_xml_file_with_ai, # Call REAL AI function
|
| 1172 |
inputs=ccda_file,
|
| 1173 |
-
outputs=ccda_ai_output
|
| 1174 |
)
|
| 1175 |
analyze_ccd_button.click( # Redundant button, but kept for UI if needed
|
| 1176 |
analyze_cda_xml_file_with_ai, # Call REAL AI function
|
| 1177 |
inputs=ccd_file,
|
| 1178 |
-
outputs=ccd_ai_output
|
| 1179 |
)
|
| 1180 |
analyze_pdf_button.click(
|
| 1181 |
analyze_pdf_file_with_ai, inputs=pdf_file, outputs=pdf_ai_output
|
|
@@ -1187,9 +1196,12 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 1187 |
with gr.Tab(
|
| 1188 |
"One-Click Discharge Paper (AI)", elem_classes="cyberpunk-tab"
|
| 1189 |
): # New Tab for One-Click Discharge Paper with AI, styled
|
| 1190 |
-
gr.Markdown(
|
|
|
|
|
|
|
| 1191 |
one_click_ai_pdf_button = gr.Button(
|
| 1192 |
-
"Generate Discharge Paper with AI (One-Click)",
|
|
|
|
| 1193 |
) # Updated button label and styled
|
| 1194 |
one_click_ai_pdf_status = gr.Textbox(
|
| 1195 |
label="Discharge Paper Generation Status (AI)"
|
|
@@ -1206,21 +1218,17 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 1206 |
|
| 1207 |
# Connect the patient data buttons
|
| 1208 |
patient_data_button.click(
|
| 1209 |
-
fn=CALLBACK_MANAGER.get_patient_data,
|
| 1210 |
-
inputs=None,
|
| 1211 |
-
outputs=patient_data_output
|
| 1212 |
)
|
| 1213 |
|
| 1214 |
# Connect refresh button to update dashboard
|
| 1215 |
-
refresh_btn.click(
|
| 1216 |
-
fn=update_dashboard, inputs=None, outputs=dashboard_output
|
| 1217 |
-
)
|
| 1218 |
|
| 1219 |
# Corrected the button click function name here to `generate_pdf_from_meldrx` (No AI PDF)
|
| 1220 |
meldrx_pdf_button.click(
|
| 1221 |
fn=generate_pdf_from_meldrx,
|
| 1222 |
inputs=patient_data_output,
|
| 1223 |
-
outputs=[meldrx_pdf_download, meldrx_pdf_status]
|
| 1224 |
)
|
| 1225 |
|
| 1226 |
# Connect patient data updates to dashboard
|
|
@@ -1228,5 +1236,6 @@ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
|
| 1228 |
fn=update_dashboard, inputs=None, outputs=dashboard_output
|
| 1229 |
)
|
| 1230 |
|
|
|
|
| 1231 |
# Launch with sharing enabled for public access
|
| 1232 |
demo.launch(ssr_mode=False)
|
|
|
|
| 121 |
top_p=0.9,
|
| 122 |
)
|
| 123 |
the_response = response.choices[0].message.content
|
| 124 |
+
return the_response, trace_data_detail_dicom_ai
|
| 125 |
|
| 126 |
except Exception as e:
|
| 127 |
error_message = f"AI Analysis Error in analyze_dicom_content_ai (DICOM Metadata): {e}"
|
| 128 |
trace_data_detail_dicom_analysis["error"] = f"AI Analysis Error: {e}"
|
| 129 |
+
return error_message, trace_data_detail_dicom_image_analysis
|
| 130 |
|
| 131 |
|
|
|
|
| 132 |
def analyze_hl7_file_with_ai(hl7_file_path):
|
| 133 |
"""Analyzes HL7 file content using Discharge Guard AI."""
|
| 134 |
try:
|
|
|
|
| 292 |
```text
|
| 293 |
{pdf_text_content}
|
| 294 |
```
|
| 295 |
+
* Remember, this analysis is for conceptual informational purposes only and **NOT medical advice.** Focus on deep summarization and structuring the extracted data in a clinically relevant way based on the PDF content.
|
| 296 |
"""
|
| 297 |
|
| 298 |
trace_data_detail_pdf_analysis = {
|
|
|
|
| 386 |
return error_message, trace_data_detail_csv_analysis
|
| 387 |
|
| 388 |
|
|
|
|
| 389 |
class CallbackManager:
|
| 390 |
def __init__(self, redirect_uri: str, client_secret: str = None):
|
| 391 |
client_id = os.getenv("APPID")
|
|
|
|
| 406 |
if self.api.authenticate_with_code(code):
|
| 407 |
self.access_token = self.api.access_token
|
| 408 |
return (
|
| 409 |
+
f"<span style='color:#00FF7F;'>Authentication successful!</span> Access Token: {self.access_token[:10]}... (truncated)" # Neon Green Success
|
| 410 |
)
|
| 411 |
+
return "<span style='color:#FF4500;'>Authentication failed. Please check the code.</span>" # Neon Orange Error
|
| 412 |
|
| 413 |
def get_patient_data(self) -> str:
|
| 414 |
"""Fetch patient data from MeldRx"""
|
| 415 |
try:
|
| 416 |
if not self.access_token:
|
| 417 |
logger.warning("Not authenticated when getting patient data")
|
| 418 |
+
return "<span style='color:#FF8C00;'>Not authenticated. Please provide a valid authorization code first.</span>" # Neon Dark Orange
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 419 |
|
| 420 |
# Real implementation with API call
|
| 421 |
logger.info("Calling Meldrx API to get patients")
|
|
|
|
| 424 |
return (
|
| 425 |
json.dumps(patients, indent=2)
|
| 426 |
if patients
|
| 427 |
+
else "<span style='color:#FFFF00;'>No patient data returned.</span>" # Neon Yellow
|
| 428 |
)
|
| 429 |
+
return "<span style='color:#DC143C;'>Failed to retrieve patient data.</span>" # Crimson Error
|
| 430 |
except Exception as e:
|
| 431 |
error_msg = f"Error in get_patient_data: {str(e)}"
|
| 432 |
logger.error(error_msg)
|
| 433 |
+
return f"<span style='color:#FF6347;'>Error retrieving patient data: {str(e)}</span> {str(e)}" # Tomato Error
|
|
|
|
| 434 |
|
| 435 |
def get_patient_documents(self, patient_id: str = None):
|
| 436 |
"""Fetch patient documents from MeldRx"""
|
| 437 |
if not self.access_token:
|
| 438 |
+
return "<span style='color:#FF8C00;'>Not authenticated. Please provide a valid authorization code first.</span>" # Neon Dark Orange
|
| 439 |
|
| 440 |
try:
|
| 441 |
# This would call the actual MeldRx API to get documents for a specific patient
|
|
|
|
| 457 |
},
|
| 458 |
]
|
| 459 |
except Exception as e:
|
| 460 |
+
return f"<span style='color:#FF6347;'>Error retrieving patient documents: {str(e)}</span>: {str(e)}" # Tomato Error
|
| 461 |
|
| 462 |
|
| 463 |
def display_form(
|
|
|
|
| 809 |
primary_hue="cyan",
|
| 810 |
secondary_hue="pink",
|
| 811 |
neutral_hue="slate",
|
| 812 |
+
font=["Source Code Pro", "monospace"], # Retro monospace font
|
| 813 |
+
font_mono=["Source Code Pro", "monospace"],
|
| 814 |
)
|
| 815 |
|
| 816 |
# Create the UI with the cyberpunk theme
|
| 817 |
+
with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
|
| 818 |
+
gr.Markdown(
|
| 819 |
+
"<h1 style='color:#00FFFF; text-shadow: 0 0 5px #00FFFF;'>Discharge Guard <span style='color:#FF00FF; text-shadow: 0 0 5px #FF00FF;'>Cyber</span></h1>"
|
| 820 |
+
) # Cyberpunk Title
|
| 821 |
+
|
| 822 |
+
with gr.Tab("Authenticate with MeldRx", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
|
| 823 |
+
gr.Markdown(
|
| 824 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>SMART on FHIR Authentication</h2>"
|
| 825 |
+
) # Neon Tab Header
|
| 826 |
+
auth_url_output = gr.Textbox(
|
| 827 |
+
label="Authorization URL",
|
| 828 |
+
value=CALLBACK_MANAGER.get_auth_url(),
|
| 829 |
+
interactive=False,
|
| 830 |
+
)
|
| 831 |
+
gr.Markdown(
|
| 832 |
+
"<p style='color:#A9A9A9;'>Copy the URL above, open it in a browser, log in, and paste the <span style='color:#00FFFF;'>entire redirected URL</span> from your browser's address bar below.</p>"
|
| 833 |
+
) # Subdued instructions with neon highlight
|
| 834 |
+
redirected_url_input = gr.Textbox(label="Redirected URL") # New textbox for redirected URL
|
| 835 |
+
extract_code_button = gr.Button(
|
| 836 |
+
"Extract Authorization Code", elem_classes="cyberpunk-button"
|
| 837 |
+
) # Cyberpunk button style
|
| 838 |
+
extracted_code_output = gr.Textbox(
|
| 839 |
+
label="Extracted Authorization Code", interactive=False
|
| 840 |
+
) # Textbox to show extracted code
|
| 841 |
+
|
| 842 |
+
auth_code_input = gr.Textbox(
|
| 843 |
+
label="Authorization Code (from above, or paste manually if extraction fails)",
|
| 844 |
+
interactive=True,
|
| 845 |
+
) # Updated label to be clearer
|
| 846 |
+
auth_submit = gr.Button(
|
| 847 |
+
"Submit Code for Authentication", elem_classes="cyberpunk-button"
|
| 848 |
+
) # Cyberpunk button style
|
| 849 |
+
auth_result = gr.HTML(label="Authentication Result") # Use HTML for styled result
|
| 850 |
+
|
| 851 |
+
patient_data_button = gr.Button(
|
| 852 |
+
"Fetch Patient Data", elem_classes="cyberpunk-button"
|
| 853 |
+
) # Cyberpunk button style
|
| 854 |
patient_data_output = gr.Textbox(label="Patient Data", lines=10)
|
| 855 |
|
| 856 |
# Add button to generate PDF from MeldRx data (No AI)
|
| 857 |
+
meldrx_pdf_button = gr.Button(
|
| 858 |
+
"Generate PDF from MeldRx Data (No AI)", elem_classes="cyberpunk-button"
|
| 859 |
+
) # Renamed button
|
| 860 |
+
meldrx_pdf_status = gr.Textbox(
|
| 861 |
+
label="PDF Generation Status (No AI)"
|
| 862 |
+
) # Renamed status
|
| 863 |
+
meldrx_pdf_download = gr.File(
|
| 864 |
+
label="Download Generated PDF (No AI)"
|
| 865 |
+
) # Renamed download
|
| 866 |
|
| 867 |
def process_redirected_url(redirected_url):
|
| 868 |
"""Processes the redirected URL to extract and display the authorization code."""
|
| 869 |
auth_code, error_message = extract_auth_code_from_url(redirected_url)
|
| 870 |
if auth_code:
|
| 871 |
+
return auth_code, "<span style='color:#00FF7F;'>Authorization code extracted!</span>" # Neon Green Success
|
| 872 |
else:
|
| 873 |
+
return "", f"<span style='color:#FF4500;'>Could not extract authorization code.</span> {error_message or ''}" # Neon Orange Error
|
|
|
|
| 874 |
|
| 875 |
extract_code_button.click(
|
| 876 |
fn=process_redirected_url,
|
| 877 |
inputs=redirected_url_input,
|
| 878 |
+
outputs=[
|
| 879 |
+
extracted_code_output,
|
| 880 |
+
auth_result,
|
| 881 |
+
], # Reusing auth_result for extraction status
|
| 882 |
)
|
| 883 |
|
| 884 |
auth_submit.click(
|
|
|
|
| 887 |
outputs=auth_result,
|
| 888 |
)
|
| 889 |
|
| 890 |
+
with gr.Tab(
|
| 891 |
+
"Patient Dashboard", elem_classes="cyberpunk-tab"
|
| 892 |
+
): # Optional: Class for tab styling
|
| 893 |
+
gr.Markdown(
|
| 894 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Patient Data</h2>"
|
| 895 |
+
) # Neon Tab Header
|
| 896 |
+
dashboard_output = gr.HTML(
|
| 897 |
+
"<p style='color:#A9A9A9;'>Fetch patient data from the Authentication tab first.</p>"
|
| 898 |
+
) # Subdued placeholder text
|
| 899 |
+
|
| 900 |
+
refresh_btn = gr.Button(
|
| 901 |
+
"Refresh Data", elem_classes="cyberpunk-button"
|
| 902 |
+
) # Cyberpunk button style
|
| 903 |
|
| 904 |
# Simple function to update dashboard based on fetched data
|
| 905 |
def update_dashboard():
|
|
|
|
| 910 |
or data.startswith("<span style='color:#DC143C;'>Failed")
|
| 911 |
or data.startswith("<span style='color:#FF6347;'>Error")
|
| 912 |
):
|
| 913 |
+
return f"<p style='color:#FF8C00;'>{data}</p>" # Show auth errors in orange
|
| 914 |
|
| 915 |
try:
|
| 916 |
# Parse the data
|
|
|
|
| 924 |
patients.append(resource)
|
| 925 |
|
| 926 |
# Generate HTML card
|
| 927 |
+
html = "<h3 style='color:#00FFFF; text-shadow: 0 0 2px #00FFFF;'>Patients</h3>" # Neon Sub-header
|
| 928 |
for patient in patients:
|
| 929 |
# Extract name
|
| 930 |
name = patient.get("name", [{}])[0]
|
|
|
|
| 947 |
|
| 948 |
return html
|
| 949 |
except Exception as e:
|
| 950 |
+
return f"<p style='color:#FF6347;'>Error parsing patient data: {str(e)}</p>" # Tomato Error
|
| 951 |
except Exception as e:
|
| 952 |
+
return f"<p style='color:#FF6347;'>Error fetching patient data: {str(e)}</p>" # Tomato Error
|
| 953 |
|
| 954 |
+
refresh_btn.click(fn=update_dashboard, inputs=None, outputs=dashboard_output)
|
| 955 |
|
| 956 |
+
with gr.Tab("Discharge Form", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
|
| 957 |
+
gr.Markdown(
|
| 958 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Patient Details</h2>"
|
| 959 |
+
) # Neon Tab Header
|
| 960 |
with gr.Row():
|
| 961 |
first_name = gr.Textbox(label="First Name")
|
| 962 |
last_name = gr.Textbox(label="Last Name")
|
|
|
|
| 970 |
city = gr.Textbox(label="City")
|
| 971 |
state = gr.Textbox(label="State")
|
| 972 |
zip_code = gr.Textbox(label="Zip Code")
|
| 973 |
+
gr.Markdown(
|
| 974 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Primary Healthcare Professional Details</h2>"
|
| 975 |
+
) # Neon Sub-header
|
| 976 |
with gr.Row():
|
| 977 |
doctor_first_name = gr.Textbox(label="Doctor's First Name")
|
| 978 |
doctor_last_name = gr.Textbox(label="Doctor's Last Name")
|
|
|
|
| 983 |
doctor_city = gr.Textbox(label="City")
|
| 984 |
doctor_state = gr.Textbox(label="State")
|
| 985 |
doctor_zip = gr.Textbox(label="Zip Code")
|
| 986 |
+
gr.Markdown(
|
| 987 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Admission and Discharge Details</h2>"
|
| 988 |
+
) # Neon Sub-header
|
| 989 |
with gr.Row():
|
| 990 |
admission_date = gr.Textbox(label="Date of Admission")
|
| 991 |
referral_source = gr.Textbox(label="Source of Referral")
|
|
|
|
| 997 |
label="Discharge Reason",
|
| 998 |
)
|
| 999 |
date_of_death = gr.Textbox(label="Date of Death (if applicable)")
|
| 1000 |
+
gr.Markdown(
|
| 1001 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Diagnosis & Procedures</h2>"
|
| 1002 |
+
) # Neon Sub-header
|
| 1003 |
diagnosis = gr.Textbox(label="Diagnosis")
|
| 1004 |
procedures = gr.Textbox(label="Operation & Procedures")
|
| 1005 |
+
gr.Markdown(
|
| 1006 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Medication Details</h2>"
|
| 1007 |
+
) # Neon Sub-header
|
| 1008 |
medications = gr.Textbox(label="Medication on Discharge")
|
| 1009 |
+
gr.Markdown(
|
| 1010 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Prepared By</h2>"
|
| 1011 |
+
) # Neon Sub-header
|
| 1012 |
with gr.Row():
|
| 1013 |
preparer_name = gr.Textbox(label="Name")
|
| 1014 |
preparer_job_title = gr.Textbox(label="Job Title")
|
| 1015 |
|
| 1016 |
# Add buttons for both display form and generate PDF
|
| 1017 |
with gr.Row():
|
| 1018 |
+
submit_display = gr.Button(
|
| 1019 |
+
"Display Form", elem_classes="cyberpunk-button"
|
| 1020 |
+
) # Cyberpunk button style
|
| 1021 |
+
submit_pdf = gr.Button(
|
| 1022 |
+
"Generate PDF (No AI)", elem_classes="cyberpunk-button"
|
| 1023 |
+
) # Renamed button to clarify no AI and styled
|
| 1024 |
|
| 1025 |
# Output areas
|
| 1026 |
+
form_output = gr.HTML() # Use HTML to render styled form
|
| 1027 |
+
pdf_output = gr.File(
|
| 1028 |
+
label="Download PDF (No AI)"
|
| 1029 |
+
) # Renamed output to clarify no AI
|
| 1030 |
|
| 1031 |
# Connect the display form button
|
| 1032 |
submit_display.click(
|
|
|
|
| 1062 |
preparer_name,
|
| 1063 |
preparer_job_title,
|
| 1064 |
],
|
| 1065 |
+
outputs=form_output,
|
| 1066 |
)
|
| 1067 |
|
| 1068 |
# Connect the generate PDF button (No AI version)
|
|
|
|
| 1099 |
preparer_name,
|
| 1100 |
preparer_job_title,
|
| 1101 |
],
|
| 1102 |
+
outputs=pdf_output,
|
| 1103 |
)
|
| 1104 |
|
| 1105 |
+
with gr.Tab(
|
| 1106 |
+
"Medical File Analysis", elem_classes="cyberpunk-tab"
|
| 1107 |
+
): # Optional: Class for tab styling
|
| 1108 |
+
gr.Markdown(
|
| 1109 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Analyze Medical Files with Discharge Guard AI</h2>"
|
| 1110 |
+
) # Neon Tab Header
|
| 1111 |
with gr.Column():
|
| 1112 |
dicom_file = gr.File(
|
| 1113 |
file_types=[".dcm"], label="Upload DICOM File (.dcm)"
|
| 1114 |
)
|
| 1115 |
dicom_ai_output = gr.Textbox(label="DICOM Analysis Report", lines=5)
|
| 1116 |
+
analyze_dicom_button = gr.Button(
|
| 1117 |
+
"Analyze DICOM with AI", elem_classes="cyberpunk-button"
|
| 1118 |
+
) # Cyberpunk button style
|
| 1119 |
|
| 1120 |
+
hl7_file = gr.File(file_types=[".hl7"], label="Upload HL7 File (.hl7)")
|
|
|
|
|
|
|
| 1121 |
hl7_ai_output = gr.Textbox(label="HL7 Analysis Report", lines=5)
|
| 1122 |
+
analyze_hl7_button = gr.Button(
|
| 1123 |
+
"Analyze HL7 with AI", elem_classes="cyberpunk-button"
|
| 1124 |
+
) # Cyberpunk button style
|
| 1125 |
|
| 1126 |
+
xml_file = gr.File(file_types=[".xml"], label="Upload XML File (.xml)")
|
|
|
|
|
|
|
| 1127 |
xml_ai_output = gr.Textbox(label="XML Analysis Report", lines=5)
|
| 1128 |
+
analyze_xml_button = gr.Button(
|
| 1129 |
+
"Analyze XML with AI", elem_classes="cyberpunk-button"
|
| 1130 |
+
) # Cyberpunk button style
|
| 1131 |
|
| 1132 |
ccda_file = gr.File(
|
| 1133 |
+
file_types=[".xml", ".cda", ".ccd"],
|
| 1134 |
+
label="Upload CCDA File (.xml, .cda, .ccd)",
|
| 1135 |
)
|
| 1136 |
ccda_ai_output = gr.Textbox(label="CCDA Analysis Report", lines=5)
|
| 1137 |
+
analyze_ccda_button = gr.Button(
|
| 1138 |
+
"Analyze CCDA with AI", elem_classes="cyberpunk-button"
|
| 1139 |
+
) # Cyberpunk button style
|
| 1140 |
|
| 1141 |
ccd_file = gr.File(
|
| 1142 |
file_types=[".ccd"],
|
|
|
|
| 1145 |
ccd_ai_output = gr.Textbox(
|
| 1146 |
label="CCD Analysis Report", lines=5
|
| 1147 |
) # Redundant
|
| 1148 |
+
analyze_ccd_button = gr.Button(
|
| 1149 |
+
"Analyze CCD with AI", elem_classes="cyberpunk-button"
|
| 1150 |
+
) # Cyberpunk button style # Redundant
|
| 1151 |
+
pdf_file = gr.File(file_types=[".pdf"], label="Upload PDF File (.pdf)")
|
| 1152 |
pdf_ai_output = gr.Textbox(label="PDF Analysis Report", lines=5)
|
| 1153 |
+
analyze_pdf_button = gr.Button(
|
| 1154 |
+
"Analyze PDF with AI", elem_classes="cyberpunk-button"
|
| 1155 |
+
) # Cyberpunk button style
|
| 1156 |
|
| 1157 |
+
csv_file = gr.File(file_types=[".csv"], label="Upload CSV File (.csv)")
|
|
|
|
|
|
|
| 1158 |
csv_ai_output = gr.Textbox(label="CSV Analysis Report", lines=5)
|
| 1159 |
+
analyze_csv_button = gr.Button(
|
| 1160 |
+
"Analyze CSV with AI", elem_classes="cyberpunk-button"
|
| 1161 |
+
) # Cyberpunk button style
|
| 1162 |
|
| 1163 |
# Connect AI Analysis Buttons - using REAL AI functions now
|
| 1164 |
analyze_dicom_button.click(
|
| 1165 |
analyze_dicom_file_with_ai, # Call REAL AI function
|
| 1166 |
inputs=dicom_file,
|
| 1167 |
+
outputs=dicom_ai_output,
|
| 1168 |
)
|
| 1169 |
analyze_hl7_button.click(
|
| 1170 |
analyze_hl7_file_with_ai, # Call REAL AI function
|
| 1171 |
inputs=hl7_file,
|
| 1172 |
+
outputs=hl7_ai_output,
|
| 1173 |
)
|
| 1174 |
analyze_xml_button.click(
|
| 1175 |
analyze_cda_xml_file_with_ai, # Call REAL AI function
|
| 1176 |
inputs=xml_file,
|
| 1177 |
+
outputs=xml_ai_output,
|
| 1178 |
)
|
| 1179 |
analyze_ccda_button.click(
|
| 1180 |
analyze_cda_xml_file_with_ai, # Call REAL AI function
|
| 1181 |
inputs=ccda_file,
|
| 1182 |
+
outputs=ccda_ai_output,
|
| 1183 |
)
|
| 1184 |
analyze_ccd_button.click( # Redundant button, but kept for UI if needed
|
| 1185 |
analyze_cda_xml_file_with_ai, # Call REAL AI function
|
| 1186 |
inputs=ccd_file,
|
| 1187 |
+
outputs=ccd_ai_output,
|
| 1188 |
)
|
| 1189 |
analyze_pdf_button.click(
|
| 1190 |
analyze_pdf_file_with_ai, inputs=pdf_file, outputs=pdf_ai_output
|
|
|
|
| 1196 |
with gr.Tab(
|
| 1197 |
"One-Click Discharge Paper (AI)", elem_classes="cyberpunk-tab"
|
| 1198 |
): # New Tab for One-Click Discharge Paper with AI, styled
|
| 1199 |
+
gr.Markdown(
|
| 1200 |
+
"<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>One-Click Medical Discharge Paper Generation with AI Content</h2>"
|
| 1201 |
+
) # Neon Tab Header
|
| 1202 |
one_click_ai_pdf_button = gr.Button(
|
| 1203 |
+
"Generate Discharge Paper with AI (One-Click)",
|
| 1204 |
+
elem_classes="cyberpunk-button",
|
| 1205 |
) # Updated button label and styled
|
| 1206 |
one_click_ai_pdf_status = gr.Textbox(
|
| 1207 |
label="Discharge Paper Generation Status (AI)"
|
|
|
|
| 1218 |
|
| 1219 |
# Connect the patient data buttons
|
| 1220 |
patient_data_button.click(
|
| 1221 |
+
fn=CALLBACK_MANAGER.get_patient_data, inputs=None, outputs=patient_data_output
|
|
|
|
|
|
|
| 1222 |
)
|
| 1223 |
|
| 1224 |
# Connect refresh button to update dashboard
|
| 1225 |
+
refresh_btn.click(fn=update_dashboard, inputs=None, outputs=dashboard_output)
|
|
|
|
|
|
|
| 1226 |
|
| 1227 |
# Corrected the button click function name here to `generate_pdf_from_meldrx` (No AI PDF)
|
| 1228 |
meldrx_pdf_button.click(
|
| 1229 |
fn=generate_pdf_from_meldrx,
|
| 1230 |
inputs=patient_data_output,
|
| 1231 |
+
outputs=[meldrx_pdf_download, meldrx_pdf_status],
|
| 1232 |
)
|
| 1233 |
|
| 1234 |
# Connect patient data updates to dashboard
|
|
|
|
| 1236 |
fn=update_dashboard, inputs=None, outputs=dashboard_output
|
| 1237 |
)
|
| 1238 |
|
| 1239 |
+
|
| 1240 |
# Launch with sharing enabled for public access
|
| 1241 |
demo.launch(ssr_mode=False)
|