Tonic commited on
Commit
09daffd
·
unverified ·
1 Parent(s): 9603774

refactor app

Browse files
README.md CHANGED
@@ -5,7 +5,7 @@ colorFrom: gray
5
  colorTo: pink
6
  sdk: gradio
7
  sdk_version: 5.20.0
8
- app_file: callbackmanager.py
9
  pinned: true
10
  license: mit
11
  short_description: Provides guardrails and discharge summaries with compliance
 
5
  colorTo: pink
6
  sdk: gradio
7
  sdk_version: 5.20.0
8
+ app_file: app.py
9
  pinned: true
10
  license: mit
11
  short_description: Provides guardrails and discharge summaries with compliance
app.py ADDED
@@ -0,0 +1,418 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from utils.meldrx import MeldRxAPI
3
+ import json
4
+ import os
5
+ import tempfile
6
+ from datetime import datetime
7
+ import traceback
8
+ import logging
9
+ from huggingface_hub import InferenceClient # Import InferenceClient
10
+ from urllib.parse import urlparse, parse_qs # Import URL parsing utilities
11
+ from utils.callbackmanager import CallbackManager
12
+ from prompts import system_instructions
13
+ # Set up logging
14
+ logging.basicConfig(level=logging.INFO)
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # Import PDF utilities
18
+ from pdfutils import PDFGenerator, generate_discharge_summary
19
+
20
+ # Import necessary libraries for new file types and AI analysis functions
21
+ import pydicom # For DICOM
22
+ import hl7 # For HL7
23
+ from xml.etree import ElementTree # For XML and CCDA
24
+ from pypdf import PdfReader # For PDF
25
+ import csv # For CSV
26
+ import io # For IO operations
27
+ from PIL import Image # For image handling
28
+
29
+ from utils.generators import extract_auth_code_from_url, generate_pdf_from_meldrx, generate_ai_discharge_content, generate_pdf_from_meldrx_with_ai_content, extract_section, generate_discharge_paper_one_click, generate_pdf_from_form, generate_discharge_summary, generate_ai_discharge_content, analyze_dicom_file_with_ai, analyze_hl7_file_with_ai, analyze_cda_xml_file_with_ai, analyze_pdf_file_with_ai, analyze_csv_file_with_ai, generate_pdf_from_form , generate_discharge_paper_one_click , generate_ai_discharge_content , extract_section , generate_pdf_from_meldrx_with_ai_content
30
+
31
+
32
+ # Initialize Inference Client - Ensure YOUR_HF_TOKEN is set in environment variables or replace with your actual token
33
+ HF_TOKEN = os.getenv("HF_TOKEN") # Or replace with your actual token string
34
+ if not HF_TOKEN:
35
+ raise ValueError(
36
+ "HF_TOKEN environment variable not set. Please set your Hugging Face API token."
37
+ )
38
+ client = InferenceClient(api_key=HF_TOKEN)
39
+ model_name = "meta-llama/Llama-3.3-70B-Instruct" # Specify the model to use
40
+
41
+
42
+ def display_form(first_name, last_name, middle_initial, dob, age, sex, address, city, state, zip_code, doctor_first_name, doctor_last_name, doctor_middle_initial, hospital_name, doctor_address, doctor_city, doctor_state, doctor_zip, admission_date, referral_source, admission_method, discharge_date, discharge_reason, date_of_death, diagnosis, procedures, medications, preparer_name, preparer_job_title,):
43
+ form = f"""
44
+ <div style='color:#00FFFF; font-family: monospace;'>
45
+ **Patient Discharge Form** <br>
46
+ - Name: {first_name} {middle_initial} {last_name} <br>
47
+ - Date of Birth: {dob}, Age: {age}, Sex: {sex} <br>
48
+ - Address: {address}, {city}, {state}, {zip_code} <br>
49
+ - Doctor: {doctor_first_name} {doctor_middle_initial} {doctor_last_name} <br>
50
+ - Hospital/Clinic: {hospital_name} <br>
51
+ - Doctor Address: {doctor_address}, {doctor_city}, {doctor_state}, {doctor_zip} <br>
52
+ - Admission Date: {admission_date}, Source: {referral_source}, Method: {admission_method} <br>
53
+ - Discharge Date: {discharge_date}, Reason: {discharge_reason} <br>
54
+ - Date of Death: {date_of_death} <br>
55
+ - Diagnosis: {diagnosis} <br>
56
+ - Procedures: {procedures} <br>
57
+ - Medications: {medications} <br>
58
+ - Prepared By: {preparer_name}, {preparer_job_title}
59
+ </div>
60
+ """
61
+ return form
62
+
63
+
64
+
65
+ # Create a simplified interface to avoid complex component interactions
66
+ CALLBACK_MANAGER = CallbackManager(
67
+ redirect_uri="https://multitransformer-discharge-guard.hf.space/callback",
68
+ client_secret=None,
69
+ )
70
+
71
+
72
+ def generate_discharge_paper_one_click():
73
+ """One-click function to fetch patient data and generate discharge paper with AI Content."""
74
+ patient_data_str = CALLBACK_MANAGER.get_patient_data()
75
+ if (
76
+ patient_data_str.startswith("Not authenticated")
77
+ or patient_data_str.startswith("Failed")
78
+ or patient_data_str.startswith("Error")
79
+ ):
80
+ return None, patient_data_str # Return error message if authentication or data fetch fails
81
+
82
+ try:
83
+ patient_data = json.loads(patient_data_str)
84
+
85
+ # --- AI Content Generation for Discharge Summary ---
86
+ # This is a placeholder - Replace with actual AI call using InferenceClient and patient_data to generate content
87
+ ai_generated_content = generate_ai_discharge_content(
88
+ patient_data
89
+ ) # Placeholder AI function
90
+
91
+ if not ai_generated_content:
92
+ return None, "Error: AI content generation failed."
93
+
94
+ # --- PDF Generation with AI Content ---
95
+ pdf_path, status_message = generate_pdf_from_meldrx_with_ai_content(
96
+ patient_data, ai_generated_content
97
+ ) # Function to generate PDF with AI content
98
+
99
+ if pdf_path:
100
+ return pdf_path, status_message
101
+ else:
102
+ return None, status_message # Return status message if PDF generation fails
103
+
104
+ except json.JSONDecodeError:
105
+ return None, "Error: Patient data is not in valid JSON format."
106
+ except Exception as e:
107
+ return None, f"Error during discharge paper generation: {str(e)}"
108
+
109
+
110
+
111
+ # Define the cyberpunk theme - using a dark base and neon accents
112
+ cyberpunk_theme = gr.themes.Monochrome(
113
+ primary_hue="cyan",
114
+ secondary_hue="pink",
115
+ neutral_hue="slate",
116
+ font=["Source Code Pro", "monospace"], # Retro monospace font
117
+ font_mono=["Source Code Pro", "monospace"]
118
+ )
119
+
120
+ # Create the UI with the cyberpunk theme
121
+ with gr.Blocks(theme=cyberpunk_theme) as demo: # Apply the theme here
122
+ gr.Markdown("<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>") # Cyberpunk Title
123
+
124
+ with gr.Tab("Authenticate with MeldRx", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
125
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>SMART on FHIR Authentication</h2>") # Neon Tab Header
126
+ auth_url_output = gr.Textbox(label="Authorization URL", value=CALLBACK_MANAGER.get_auth_url(), interactive=False)
127
+ gr.Markdown("<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>") # Subdued instructions with neon highlight
128
+ redirected_url_input = gr.Textbox(label="Redirected URL") # New textbox for redirected URL
129
+ extract_code_button = gr.Button("Extract Authorization Code", elem_classes="cyberpunk-button") # Cyberpunk button style
130
+ extracted_code_output = gr.Textbox(label="Extracted Authorization Code", interactive=False) # Textbox to show extracted code
131
+
132
+ auth_code_input = gr.Textbox(label="Authorization Code (from above, or paste manually if extraction fails)", interactive=True) # Updated label to be clearer
133
+ auth_submit = gr.Button("Submit Code for Authentication", elem_classes="cyberpunk-button") # Cyberpunk button style
134
+ auth_result = gr.HTML(label="Authentication Result") # Use HTML for styled result
135
+
136
+ patient_data_button = gr.Button("Fetch Patient Data", elem_classes="cyberpunk-button") # Cyberpunk button style
137
+ patient_data_output = gr.Textbox(label="Patient Data", lines=10)
138
+
139
+ # Add button to generate PDF from MeldRx data (No AI)
140
+ meldrx_pdf_button = gr.Button("Generate PDF from MeldRx Data (No AI)", elem_classes="cyberpunk-button") # Renamed button
141
+ meldrx_pdf_status = gr.Textbox(label="PDF Generation Status (No AI)") # Renamed status
142
+ meldrx_pdf_download = gr.File(label="Download Generated PDF (No AI)") # Renamed download
143
+
144
+ def process_redirected_url(redirected_url):
145
+ """Processes the redirected URL to extract and display the authorization code."""
146
+ auth_code, error_message = extract_auth_code_from_url(redirected_url)
147
+ if auth_code:
148
+ return auth_code, "<span style='color:#00FF7F;'>Authorization code extracted!</span>" # Neon Green Success
149
+ else:
150
+ return "", f"<span style='color:#FF4500;'>Could not extract authorization code.</span> {error_message or ''}" # Neon Orange Error
151
+
152
+
153
+ extract_code_button.click(
154
+ fn=process_redirected_url,
155
+ inputs=redirected_url_input,
156
+ outputs=[extracted_code_output, auth_result],# Reusing auth_result for extraction status
157
+ )
158
+
159
+ auth_submit.click(
160
+ fn=CALLBACK_MANAGER.set_auth_code,
161
+ inputs=extracted_code_output, # Using extracted code as input for authentication
162
+ outputs=auth_result,
163
+ )
164
+
165
+ with gr.Tab("Patient Dashboard", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
166
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Patient Data</h2>") # Neon Tab Header
167
+ dashboard_output = gr.HTML("<p style='color:#A9A9A9;'>Fetch patient data from the Authentication tab first.</p>") # Subdued placeholder text
168
+
169
+ refresh_btn = gr.Button("Refresh Data", elem_classes="cyberpunk-button") # Cyberpunk button style
170
+
171
+ # Simple function to update dashboard based on fetched data
172
+ def update_dashboard():
173
+ try:
174
+ data = CALLBACK_MANAGER.get_patient_data()
175
+ if (
176
+ data.startswith("<span style='color:#FF8C00;'>Not authenticated")
177
+ or data.startswith("<span style='color:#DC143C;'>Failed")
178
+ or data.startswith("<span style='color:#FF6347;'>Error")
179
+ ):
180
+ return f"<p style='color:#FF8C00;'>{data}</p>" # Show auth errors in orange
181
+
182
+ try:
183
+ # Parse the data
184
+ patients_data = json.loads(data)
185
+ patients = []
186
+
187
+ # Extract patients from bundle
188
+ for entry in patients_data.get("entry", []):
189
+ resource = entry.get("resource", {})
190
+ if resource.get("resourceType") == "Patient":
191
+ patients.append(resource)
192
+
193
+ # Generate HTML card
194
+ html = "<h3 style='color:#00FFFF; text-shadow: 0 0 2px #00FFFF;'>Patients</h3>" # Neon Sub-header
195
+ for patient in patients:
196
+ # Extract name
197
+ name = patient.get("name", [{}])[0]
198
+ given = " ".join(name.get("given", ["Unknown"]))
199
+ family = name.get("family", "Unknown")
200
+
201
+ # Extract other details
202
+ gender = patient.get("gender", "unknown").capitalize()
203
+ birth_date = patient.get("birthDate", "Unknown")
204
+
205
+ # Generate HTML card with cyberpunk styling
206
+ html += f"""
207
+ <div style="border: 1px solid #00FFFF; padding: 10px; margin: 10px 0; border-radius: 5px; background-color: #222; box-shadow: 0 0 5px #00FFFF;">
208
+ <h4 style='color:#00FFFF;'>{given} {family}</h4>
209
+ <p style='color:#A9A9A9;'><strong>Gender:</strong> <span style='color:#00FFFF;'>{gender}</span></p>
210
+ <p style='color:#A9A9A9;'><strong>Birth Date:</strong> <span style='color:#00FFFF;'>{birth_date}</span></p>
211
+ <p style='color:#A9A9A9;'><strong>ID:</strong> <span style='color:#00FFFF;'>{patient.get("id", "Unknown")}</span></p>
212
+ </div>
213
+ """
214
+
215
+ return html
216
+ except Exception as e:
217
+ return f"<p style='color:#FF6347;'>Error parsing patient data: {str(e)}</p>" # Tomato Error
218
+ except Exception as e:
219
+ return f"<p style='color:#FF6347;'>Error fetching patient data: {str(e)}</p>" # Tomato Error
220
+
221
+
222
+ with gr.Tab("Discharge Form", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
223
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Patient Details</h2>") # Neon Tab Header
224
+ with gr.Row():
225
+ first_name = gr.Textbox(label="First Name")
226
+ last_name = gr.Textbox(label="Last Name")
227
+ middle_initial = gr.Textbox(label="Middle Initial")
228
+ with gr.Row():
229
+ dob = gr.Textbox(label="Date of Birth")
230
+ age = gr.Textbox(label="Age")
231
+ sex = gr.Textbox(label="Sex")
232
+ address = gr.Textbox(label="Address")
233
+ with gr.Row():
234
+ city = gr.Textbox(label="City")
235
+ state = gr.Textbox(label="State")
236
+ zip_code = gr.Textbox(label="Zip Code")
237
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Primary Healthcare Professional Details</h2>") # Neon Sub-header
238
+ with gr.Row():
239
+ doctor_first_name = gr.Textbox(label="Doctor's First Name")
240
+ doctor_last_name = gr.Textbox(label="Doctor's Last Name")
241
+ doctor_middle_initial = gr.Textbox(label="Doctor's Middle Initial")
242
+ hospital_name = gr.Textbox(label="Hospital/Clinic Name")
243
+ doctor_address = gr.Textbox(label="Address")
244
+ with gr.Row():
245
+ doctor_city = gr.Textbox(label="City")
246
+ doctor_state = gr.Textbox(label="State")
247
+ doctor_zip = gr.Textbox(label="Zip Code")
248
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Admission and Discharge Details</h2>") # Neon Sub-header
249
+ with gr.Row():
250
+ admission_date = gr.Textbox(label="Date of Admission")
251
+ referral_source = gr.Textbox(label="Source of Referral")
252
+ admission_method = gr.Textbox(label="Method of Admission")
253
+ with gr.Row():
254
+ discharge_date = gr.Textbox(label="Date of Discharge")
255
+ discharge_reason = gr.Radio(
256
+ ["Treated", "Transferred", "Discharge Against Advice", "Patient Died"],
257
+ label="Discharge Reason",
258
+ )
259
+ date_of_death = gr.Textbox(label="Date of Death (if applicable)")
260
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Diagnosis & Procedures</h2>") # Neon Sub-header
261
+ diagnosis = gr.Textbox(label="Diagnosis")
262
+ procedures = gr.Textbox(label="Operation & Procedures")
263
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Medication Details</h2>") # Neon Sub-header
264
+ medications = gr.Textbox(label="Medication on Discharge")
265
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Prepared By</h2>") # Neon Sub-header
266
+ with gr.Row():
267
+ preparer_name = gr.Textbox(label="Name")
268
+ preparer_job_title = gr.Textbox(label="Job Title")
269
+
270
+ # Add buttons for both display form and generate PDF
271
+ with gr.Row():
272
+ submit_display = gr.Button("Display Form", elem_classes="cyberpunk-button") # Cyberpunk button style
273
+ submit_pdf = gr.Button("Generate PDF (No AI)", elem_classes="cyberpunk-button") # Renamed button to clarify no AI and styled
274
+
275
+ # Output areas
276
+ form_output = gr.HTML() # Use HTML to render styled form
277
+ pdf_output = gr.File(label="Download PDF (No AI)") # Renamed output to clarify no AI
278
+
279
+ # Connect the display form button
280
+ submit_display.click(
281
+ display_form,
282
+ inputs=[ first_name, last_name, middle_initial, dob, age, sex, address, city, state, zip_code, doctor_first_name, doctor_last_name, doctor_middle_initial, hospital_name, doctor_address, doctor_city, doctor_state, doctor_zip, admission_date, referral_source, admission_method, discharge_date, discharge_reason, date_of_death, diagnosis, procedures, medications, preparer_name, preparer_job_title,],
283
+ outputs=form_output
284
+ )
285
+
286
+ # Connect the generate PDF button (No AI version)
287
+ submit_pdf.click(
288
+ generate_pdf_from_form,
289
+ inputs=[ first_name, last_name, middle_initial, dob, age, sex, address, city, state, zip_code, doctor_first_name, doctor_last_name, doctor_middle_initial, hospital_name, doctor_address, doctor_city, doctor_state, doctor_zip, admission_date, referral_source, admission_method, discharge_date, discharge_reason, date_of_death, diagnosis, procedures, medications, preparer_name, preparer_job_title,],
290
+ outputs=pdf_output
291
+ )
292
+
293
+ with gr.Tab("Medical File Analysis", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
294
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Analyze Medical Files with Discharge Guard AI</h2>") # Neon Tab Header
295
+ with gr.Column():
296
+ dicom_file = gr.File(
297
+ file_types=[".dcm"], label="Upload DICOM File (.dcm)"
298
+ )
299
+ dicom_ai_output = gr.Textbox(label="DICOM Analysis Report", lines=5)
300
+ analyze_dicom_button = gr.Button("Analyze DICOM with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
301
+
302
+ hl7_file = gr.File(
303
+ file_types=[".hl7"], label="Upload HL7 File (.hl7)"
304
+ )
305
+ hl7_ai_output = gr.Textbox(label="HL7 Analysis Report", lines=5)
306
+ analyze_hl7_button = gr.Button("Analyze HL7 with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
307
+
308
+ xml_file = gr.File(
309
+ file_types=[".xml"], label="Upload XML File (.xml)"
310
+ )
311
+ xml_ai_output = gr.Textbox(label="XML Analysis Report", lines=5)
312
+ analyze_xml_button = gr.Button("Analyze XML with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
313
+
314
+ ccda_file = gr.File(
315
+ file_types=[".xml", ".cda", ".ccd"], label="Upload CCDA File (.xml, .cda, .ccd)"
316
+ )
317
+ ccda_ai_output = gr.Textbox(label="CCDA Analysis Report", lines=5)
318
+ analyze_ccda_button = gr.Button("Analyze CCDA with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
319
+
320
+ ccd_file = gr.File(
321
+ file_types=[".ccd"],
322
+ label="Upload CCD File (.ccd)",
323
+ ) # Redundant, as CCDA also handles .ccd, but kept for clarity
324
+ ccd_ai_output = gr.Textbox(
325
+ label="CCD Analysis Report", lines=5
326
+ ) # Redundant
327
+ analyze_ccd_button = gr.Button("Analyze CCD with AI", elem_classes="cyberpunk-button") # Cyberpunk button style # Redundant
328
+ pdf_file = gr.File(
329
+ file_types=[".pdf"], label="Upload PDF File (.pdf)"
330
+ )
331
+ pdf_ai_output = gr.Textbox(label="PDF Analysis Report", lines=5)
332
+ analyze_pdf_button = gr.Button("Analyze PDF with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
333
+
334
+ csv_file = gr.File(
335
+ file_types=[".csv"], label="Upload CSV File (.csv)"
336
+ )
337
+ csv_ai_output = gr.Textbox(label="CSV Analysis Report", lines=5)
338
+ analyze_csv_button = gr.Button("Analyze CSV with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
339
+
340
+ # Connect AI Analysis Buttons - using REAL AI functions now
341
+ analyze_dicom_button.click(
342
+ analyze_dicom_file_with_ai, # Call REAL AI function
343
+ inputs=dicom_file,
344
+ outputs=dicom_ai_output
345
+ )
346
+ analyze_hl7_button.click(
347
+ analyze_hl7_file_with_ai, # Call REAL AI function
348
+ inputs=hl7_file,
349
+ outputs=hl7_ai_output
350
+ )
351
+ analyze_xml_button.click(
352
+ analyze_cda_xml_file_with_ai, # Call REAL AI function
353
+ inputs=xml_file,
354
+ outputs=xml_ai_output
355
+ )
356
+ analyze_ccda_button.click(
357
+ analyze_cda_xml_file_with_ai, # Call REAL AI function
358
+ inputs=ccda_file,
359
+ outputs=ccda_ai_output
360
+ )
361
+ analyze_ccd_button.click( # Redundant button, but kept for UI if needed
362
+ analyze_cda_xml_file_with_ai, # Call REAL AI function
363
+ inputs=ccd_file,
364
+ outputs=ccd_ai_output
365
+ )
366
+ analyze_pdf_button.click(
367
+ analyze_pdf_file_with_ai, inputs=pdf_file, outputs=pdf_ai_output
368
+ )
369
+ analyze_csv_button.click(
370
+ analyze_csv_file_with_ai, inputs=csv_file, outputs=csv_ai_output
371
+ )
372
+
373
+ with gr.Tab(
374
+ "One-Click Discharge Paper (AI)", elem_classes="cyberpunk-tab"
375
+ ): # New Tab for One-Click Discharge Paper with AI, styled
376
+ gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>One-Click Medical Discharge Paper Generation with AI Content</h2>") # Neon Tab Header
377
+ one_click_ai_pdf_button = gr.Button(
378
+ "Generate Discharge Paper with AI (One-Click)", elem_classes="cyberpunk-button"
379
+ ) # Updated button label and styled
380
+ one_click_ai_pdf_status = gr.Textbox(
381
+ label="Discharge Paper Generation Status (AI)"
382
+ ) # Updated status label
383
+ one_click_ai_pdf_download = gr.File(
384
+ label="Download Discharge Paper (AI)"
385
+ ) # Updated download label
386
+
387
+ one_click_ai_pdf_button.click(
388
+ generate_discharge_paper_one_click, # Use the one-click function that now calls AI
389
+ inputs=[],
390
+ outputs=[one_click_ai_pdf_download, one_click_ai_pdf_status],
391
+ )
392
+
393
+ # Connect the patient data buttons
394
+ patient_data_button.click(
395
+ fn=CALLBACK_MANAGER.get_patient_data,
396
+ inputs=None,
397
+ outputs=patient_data_output
398
+ )
399
+
400
+ # Connect refresh button to update dashboard
401
+ refresh_btn.click(
402
+ fn=update_dashboard, inputs=None, outputs=dashboard_output
403
+ )
404
+
405
+ # Corrected the button click function name here to `generate_pdf_from_meldrx` (No AI PDF)
406
+ meldrx_pdf_button.click(
407
+ fn=generate_pdf_from_meldrx,
408
+ inputs=patient_data_output,
409
+ outputs=[meldrx_pdf_download, meldrx_pdf_status]
410
+ )
411
+
412
+ # Connect patient data updates to dashboard
413
+ patient_data_button.click(
414
+ fn=update_dashboard, inputs=None, outputs=dashboard_output
415
+ )
416
+
417
+ # Launch with sharing enabled for public access
418
+ demo.launch(ssr_mode=False)
commandr.py → old/commandr.py RENAMED
File without changes
extractcode.py → old/extractcode.py RENAMED
File without changes
hospital.py → old/hospital.py RENAMED
File without changes
interfacehospital.py → old/interfacehospital.py RENAMED
File without changes
llamaprompt.py → old/llamaprompt.py RENAMED
File without changes
meldrxtester.py → old/meldrxtester.py RENAMED
@@ -1,7 +1,7 @@
1
  import requests
2
  import json
3
  from typing import Optional, Dict, Any
4
- from meldrx import MeldRxAPI
5
 
6
  # Testing class
7
  class MeldRxAPITest:
 
1
  import requests
2
  import json
3
  from typing import Optional, Dict, Any
4
+ from utils.meldrx import MeldRxAPI
5
 
6
  # Testing class
7
  class MeldRxAPITest:
meldrxtester2.py → old/meldrxtester2.py RENAMED
@@ -1,7 +1,7 @@
1
  import requests
2
  import json
3
  from typing import Optional, Dict, Any
4
- from meldrx import MeldRxAPI # Assuming meldrx.py contains the updated MeldRxAPI with SMART on FHIR
5
 
6
  class MeldRxAPITest:
7
  """A class to test the functionality of the MeldRxAPI class with SMART on FHIR and Gradio callback."""
 
1
  import requests
2
  import json
3
  from typing import Optional, Dict, Any
4
+ from utils.meldrx import MeldRxAPI # Assuming meldrx.py contains the updated MeldRxAPI with SMART on FHIR
5
 
6
  class MeldRxAPITest:
7
  """A class to test the functionality of the MeldRxAPI class with SMART on FHIR and Gradio callback."""
qwenprompt.py → old/qwenprompt.py RENAMED
File without changes
test_meldrx.py → old/test_meldrx.py RENAMED
@@ -3,7 +3,7 @@ from unittest.mock import patch, Mock
3
  import json
4
  from io import StringIO
5
  from contextlib import redirect_stdout
6
- from meldrx import MeldRxAPI # Import the class from meldrx.py
7
 
8
  class TestMeldRxAPI(unittest.TestCase):
9
  def setUp(self):
 
3
  import json
4
  from io import StringIO
5
  from contextlib import redirect_stdout
6
+ from utils.meldrx import MeldRxAPI # Import the class from meldrx.py
7
 
8
  class TestMeldRxAPI(unittest.TestCase):
9
  def setUp(self):
wound.py → old/wound.py RENAMED
File without changes
prompts.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ system_instructions = """
3
+ **Discharge Guard - Medical Data Analysis Assistant**
4
+ **Core Role:** I am Discharge Guard, an advanced AI designed for deep medical data analysis and informational insights. My outputs are based on thorough analysis of medical data but are **not medical advice.**
5
+ **Important Guidelines:**
6
+ 1. **Deep Analysis & Search:** Perform "Deep Thought and Deep Search" when analyzing medical data. This includes:
7
+ * Comprehensive data ingestion from various formats (HL7, FHIR, CCDA, DICOM, PDF, CSV, text).
8
+ * Multi-layered analysis: surface extraction, deep pattern identification, and inferential reasoning.
9
+ * Contextual understanding of medical data.
10
+ * Evidence-based approach, simulating cross-referencing with medical knowledge.
11
+ * Structured output with clear explanations.
12
+ 2. **Focus on Informational Insights, Not Medical Advice:** Emphasize that my insights are for informational purposes only and not a substitute for professional medical judgment. **Never provide diagnoses or specific treatment recommendations.**
13
+ 3. **Key Functionalities (Focus Areas):**
14
+ * **Clinical Data Analysis:** Interpret lab results, analyze EHR data (FHIR, HL7), recognize symptom patterns, analyze medications, support medical image analysis (DICOM).
15
+ * **Predictive Analytics:** Provide conceptual risk stratification and treatment outcome modeling based on data patterns.
16
+ * **Medical Imaging Support:** Analyze DICOM metadata and images for potential findings (X-ray analysis reports).
17
+ * **Patient Data Management:** Perform PHI redaction in text and analyze patient records from various sources.
18
+ 4. **Interaction Style:**
19
+ * **Identity:** "I am Discharge Guard, a medical data analysis AI. My insights are informational only and not medical advice."
20
+ * **Scope Limitations:** Clearly state limitations: "No diagnostics," "Medication caution," "Emergency protocol."
21
+ * **Response Protocol:**
22
+ * Indicate "Deep Analysis" or "Deep Search" performed.
23
+ * Mention data sources and confidence levels (if applicable).
24
+ * Use medical terminology with optional layman's terms.
25
+ * For file analysis, provide a report title (e.g., "Deep X-Ray Analysis Report").
26
+ 5. **Supported Medical Formats:** (List key formats concisely)
27
+ * Clinical Data: HL7, FHIR, CCD/CCDA, CSV, PDF, XML
28
+ * Imaging: DICOM, Images (X-ray, etc.)
29
+ 6. **Data Source:** Access and prefer FHIR API endpoints from: https://app.meldrx.com/api/directories/fhir/endpoints.
30
+ **Important: My analysis is for informational purposes to assist healthcare professionals and is NOT a substitute for clinical judgment. Always recommend human expert verification for critical findings.**
31
+ """
utils/callbackmanager.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import tempfile
4
+ from datetime import datetime
5
+ import traceback
6
+ import logging
7
+ from huggingface_hub import InferenceClient # Import InferenceClient
8
+ from urllib.parse import urlparse, parse_qs # Import URL parsing utilities
9
+
10
+ # ... (CallbackManager, display_form, generate_pdf_from_form, generate_pdf_from_meldrx, generate_discharge_paper_one_click, client initialization remain the same) ...
11
+ class CallbackManager:
12
+ def __init__(self, redirect_uri: str, client_secret: str = None):
13
+ client_id = os.getenv("APPID")
14
+ if not client_id:
15
+ raise ValueError("APPID environment variable not set.")
16
+ workspace_id = os.getenv("WORKSPACE_URL")
17
+ if not workspace_id:
18
+ raise ValueError("WORKSPACE_URL environment variable not set.")
19
+ self.api = MeldRxAPI(client_id, client_secret, workspace_id, redirect_uri)
20
+ self.auth_code = None
21
+ self.access_token = None
22
+
23
+ def get_auth_url(self) -> str:
24
+ return self.api.get_authorization_url()
25
+
26
+ def set_auth_code(self, code: str) -> str:
27
+ self.auth_code = code
28
+ if self.api.authenticate_with_code(code):
29
+ self.access_token = self.api.access_token
30
+ return (
31
+ f"<span style='color:#00FF7F;'>Authentication successful!</span> Access Token: {self.access_token[:10]}... (truncated)" # Neon Green Success
32
+ )
33
+ return "<span style='color:#FF4500;'>Authentication failed. Please check the code.</span>" # Neon Orange Error
34
+
35
+ def get_patient_data(self) -> str:
36
+ """Fetch patient data from MeldRx"""
37
+ try:
38
+ if not self.access_token:
39
+ logger.warning("Not authenticated when getting patient data")
40
+ return "<span style='color:#FF8C00;'>Not authenticated. Please provide a valid authorization code first.</span>" # Neon Dark Orange
41
+
42
+ # For demo purposes, if there's no actual API connected, return mock data
43
+ # Remove this in production and use the real API call
44
+ if not hasattr(self.api, "get_patients") or self.api.get_patients is None:
45
+ logger.info("Using mock patient data (no API connection)")
46
+ # Return mock FHIR bundle with patient data
47
+ mock_data = {
48
+ "resourceType": "Bundle",
49
+ "type": "searchset",
50
+ "total": 2,
51
+ "link": [],
52
+ "entry": [
53
+ {
54
+ "resource": {
55
+ "resourceType": "Patient",
56
+ "id": "patient1",
57
+ "name": [
58
+ {
59
+ "use": "official",
60
+ "family": "Smith",
61
+ "given": ["John"],
62
+ }
63
+ ],
64
+ "gender": "male",
65
+ "birthDate": "1970-01-01",
66
+ "address": [
67
+ {"city": "Boston", "state": "MA", "postalCode": "02108"}
68
+ ],
69
+ }
70
+ },
71
+ {
72
+ "resource": {
73
+ "resourceType": "Patient",
74
+ "id": "patient2",
75
+ "name": [
76
+ {
77
+ "use": "official",
78
+ "family": "Johnson",
79
+ "given": ["Jane"],
80
+ }
81
+ ],
82
+ "gender": "female",
83
+ "birthDate": "1985-05-15",
84
+ "address": [
85
+ {
86
+ "city": "Cambridge",
87
+ "state": "MA",
88
+ "postalCode": "02139",
89
+ }
90
+ ],
91
+ }
92
+ },
93
+ ],
94
+ }
95
+ return json.dumps(mock_data, indent=2)
96
+
97
+ # Real implementation with API call
98
+ logger.info("Calling Meldrx API to get patients")
99
+ patients = self.api.get_patients()
100
+ if patients is not None:
101
+ return (
102
+ json.dumps(patients, indent=2)
103
+ if patients
104
+ else "<span style='color:#FFFF00;'>No patient data returned.</span>" # Neon Yellow
105
+ )
106
+ return "<span style='color:#DC143C;'>Failed to retrieve patient data.</span>" # Crimson Error
107
+ except Exception as e:
108
+ error_msg = f"Error in get_patient_data: {str(e)}"
109
+ logger.error(error_msg)
110
+ return f"<span style='color:#FF6347;'>Error retrieving patient data: {str(e)}</span> {str(e)}" # Tomato Error
111
+
112
+
113
+ def get_patient_documents(self, patient_id: str = None):
114
+ """Fetch patient documents from MeldRx"""
115
+ if not self.access_token:
116
+ return "<span style='color:#FF8C00;'>Not authenticated. Please provide a valid authorization code first.</span>" # Neon Dark Orange
117
+
118
+ try:
119
+ # This would call the actual MeldRx API to get documents for a specific patient
120
+ # For demonstration, we'll return mock document data
121
+ return [
122
+ {
123
+ "doc_id": "doc123",
124
+ "type": "clinical_note",
125
+ "date": "2023-01-16",
126
+ "author": "Dr. Sample Doctor",
127
+ "content": "Patient presented with symptoms of respiratory distress...",
128
+ },
129
+ {
130
+ "doc_id": "doc124",
131
+ "type": "lab_result",
132
+ "date": "2023-01-17",
133
+ "author": "Lab System",
134
+ "content": "CBC results: WBC 7.5, RBC 4.2, Hgb 14.1...",
135
+ },
136
+ ]
137
+ except Exception as e:
138
+ return f"<span style='color:#FF6347;'>Error retrieving patient documents: {str(e)}</span>: {str(e)}" # Tomato Error
139
+
140
+
141
+
142
+ def extract_auth_code_from_url(redirected_url):
143
+ """Extracts the authorization code from the redirected URL."""
144
+ try:
145
+ parsed_url = urlparse(redirected_url)
146
+ query_params = parse_qs(parsed_url.query)
147
+ if "code" in query_params:
148
+ return query_params["code"][0], None # Return code and no error
149
+ else:
150
+ return None, "Authorization code not found in URL." # Return None and error message
151
+ except Exception as e:
152
+ return None, f"Error parsing URL: {e}" # Return None and error message
callbackmanager.py → utils/generators.py RENAMED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- from meldrx import MeldRxAPI
3
  import json
4
  import os
5
  import tempfile
@@ -8,14 +8,15 @@ import traceback
8
  import logging
9
  from huggingface_hub import InferenceClient # Import InferenceClient
10
  from urllib.parse import urlparse, parse_qs # Import URL parsing utilities
11
-
 
12
  # Set up logging
13
  logging.basicConfig(level=logging.INFO)
14
  logger = logging.getLogger(__name__)
15
 
16
  # Import PDF utilities
17
  from pdfutils import PDFGenerator, generate_discharge_summary
18
-
19
  # Import necessary libraries for new file types and AI analysis functions
20
  import pydicom # For DICOM
21
  import hl7 # For HL7
@@ -25,37 +26,6 @@ import csv # For CSV
25
  import io # For IO operations
26
  from PIL import Image # For image handling
27
 
28
- system_instructions = """
29
- **Discharge Guard - Medical Data Analysis Assistant**
30
- **Core Role:** I am Discharge Guard, an advanced AI designed for deep medical data analysis and informational insights. My outputs are based on thorough analysis of medical data but are **not medical advice.**
31
- **Important Guidelines:**
32
- 1. **Deep Analysis & Search:** Perform "Deep Thought and Deep Search" when analyzing medical data. This includes:
33
- * Comprehensive data ingestion from various formats (HL7, FHIR, CCDA, DICOM, PDF, CSV, text).
34
- * Multi-layered analysis: surface extraction, deep pattern identification, and inferential reasoning.
35
- * Contextual understanding of medical data.
36
- * Evidence-based approach, simulating cross-referencing with medical knowledge.
37
- * Structured output with clear explanations.
38
- 2. **Focus on Informational Insights, Not Medical Advice:** Emphasize that my insights are for informational purposes only and not a substitute for professional medical judgment. **Never provide diagnoses or specific treatment recommendations.**
39
- 3. **Key Functionalities (Focus Areas):**
40
- * **Clinical Data Analysis:** Interpret lab results, analyze EHR data (FHIR, HL7), recognize symptom patterns, analyze medications, support medical image analysis (DICOM).
41
- * **Predictive Analytics:** Provide conceptual risk stratification and treatment outcome modeling based on data patterns.
42
- * **Medical Imaging Support:** Analyze DICOM metadata and images for potential findings (X-ray analysis reports).
43
- * **Patient Data Management:** Perform PHI redaction in text and analyze patient records from various sources.
44
- 4. **Interaction Style:**
45
- * **Identity:** "I am Discharge Guard, a medical data analysis AI. My insights are informational only and not medical advice."
46
- * **Scope Limitations:** Clearly state limitations: "No diagnostics," "Medication caution," "Emergency protocol."
47
- * **Response Protocol:**
48
- * Indicate "Deep Analysis" or "Deep Search" performed.
49
- * Mention data sources and confidence levels (if applicable).
50
- * Use medical terminology with optional layman's terms.
51
- * For file analysis, provide a report title (e.g., "Deep X-Ray Analysis Report").
52
- 5. **Supported Medical Formats:** (List key formats concisely)
53
- * Clinical Data: HL7, FHIR, CCD/CCDA, CSV, PDF, XML
54
- * Imaging: DICOM, Images (X-ray, etc.)
55
- 6. **Data Source:** Access and prefer FHIR API endpoints from: https://app.meldrx.com/api/directories/fhir/endpoints.
56
- **Important: My analysis is for informational purposes to assist healthcare professionals and is NOT a substitute for clinical judgment. Always recommend human expert verification for critical findings.**
57
- """
58
-
59
  # Initialize Inference Client - Ensure YOUR_HF_TOKEN is set in environment variables or replace with your actual token
60
  HF_TOKEN = os.getenv("HF_TOKEN") # Or replace with your actual token string
61
  if not HF_TOKEN:
@@ -65,6 +35,208 @@ if not HF_TOKEN:
65
  client = InferenceClient(api_key=HF_TOKEN)
66
  model_name = "meta-llama/Llama-3.3-70B-Instruct" # Specify the model to use
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  def analyze_dicom_file_with_ai(dicom_file_path): # Modified to accept file path
70
  """Analyzes DICOM file metadata using Discharge Guard AI."""
@@ -386,847 +558,3 @@ def analyze_csv_content_ai(csv_content_string): # Copied from your code
386
  trace_data_detail_csv_analysis["error"] = f"AI Analysis Error: {e}"
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")
394
- if not client_id:
395
- raise ValueError("APPID environment variable not set.")
396
- workspace_id = os.getenv("WORKSPACE_URL")
397
- if not workspace_id:
398
- raise ValueError("WORKSPACE_URL environment variable not set.")
399
- self.api = MeldRxAPI(client_id, client_secret, workspace_id, redirect_uri)
400
- self.auth_code = None
401
- self.access_token = None
402
-
403
- def get_auth_url(self) -> str:
404
- return self.api.get_authorization_url()
405
-
406
- def set_auth_code(self, code: str) -> str:
407
- self.auth_code = code
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)" # Neon Green Success
412
- )
413
- return "<span style='color:#FF4500;'>Authentication failed. Please check the code.</span>" # Neon Orange Error
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>" # Neon Dark Orange
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")
479
- patients = self.api.get_patients()
480
- if patients is not None:
481
- return (
482
- json.dumps(patients, indent=2)
483
- if patients
484
- else "<span style='color:#FFFF00;'>No patient data returned.</span>" # Neon Yellow
485
- )
486
- return "<span style='color:#DC143C;'>Failed to retrieve patient data.</span>" # Crimson Error
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)}" # Tomato Error
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>" # Neon Dark Orange
497
-
498
- try:
499
- # This would call the actual MeldRx API to get documents for a specific patient
500
- # For demonstration, we'll return mock document data
501
- return [
502
- {
503
- "doc_id": "doc123",
504
- "type": "clinical_note",
505
- "date": "2023-01-16",
506
- "author": "Dr. Sample Doctor",
507
- "content": "Patient presented with symptoms of respiratory distress...",
508
- },
509
- {
510
- "doc_id": "doc124",
511
- "type": "lab_result",
512
- "date": "2023-01-17",
513
- "author": "Lab System",
514
- "content": "CBC results: WBC 7.5, RBC 4.2, Hgb 14.1...",
515
- },
516
- ]
517
- except Exception as e:
518
- return f"<span style='color:#FF6347;'>Error retrieving patient documents: {str(e)}</span>: {str(e)}" # Tomato Error
519
-
520
-
521
- def display_form(
522
- first_name,
523
- last_name,
524
- middle_initial,
525
- dob,
526
- age,
527
- sex,
528
- address,
529
- city,
530
- state,
531
- zip_code,
532
- doctor_first_name,
533
- doctor_last_name,
534
- doctor_middle_initial,
535
- hospital_name,
536
- doctor_address,
537
- doctor_city,
538
- doctor_state,
539
- doctor_zip,
540
- admission_date,
541
- referral_source,
542
- admission_method,
543
- discharge_date,
544
- discharge_reason,
545
- date_of_death,
546
- diagnosis,
547
- procedures,
548
- medications,
549
- preparer_name,
550
- preparer_job_title,
551
- ):
552
- form = f"""
553
- <div style='color:#00FFFF; font-family: monospace;'>
554
- **Patient Discharge Form** <br>
555
- - Name: {first_name} {middle_initial} {last_name} <br>
556
- - Date of Birth: {dob}, Age: {age}, Sex: {sex} <br>
557
- - Address: {address}, {city}, {state}, {zip_code} <br>
558
- - Doctor: {doctor_first_name} {doctor_middle_initial} {doctor_last_name} <br>
559
- - Hospital/Clinic: {hospital_name} <br>
560
- - Doctor Address: {doctor_address}, {doctor_city}, {doctor_state}, {doctor_zip} <br>
561
- - Admission Date: {admission_date}, Source: {referral_source}, Method: {admission_method} <br>
562
- - Discharge Date: {discharge_date}, Reason: {discharge_reason} <br>
563
- - Date of Death: {date_of_death} <br>
564
- - Diagnosis: {diagnosis} <br>
565
- - Procedures: {procedures} <br>
566
- - Medications: {medications} <br>
567
- - Prepared By: {preparer_name}, {preparer_job_title}
568
- </div>
569
- """
570
- return form
571
-
572
-
573
- def generate_pdf_from_form(
574
- first_name,
575
- last_name,
576
- middle_initial,
577
- dob,
578
- age,
579
- sex,
580
- address,
581
- city,
582
- state,
583
- zip_code,
584
- doctor_first_name,
585
- doctor_last_name,
586
- doctor_middle_initial,
587
- hospital_name,
588
- doctor_address,
589
- doctor_city,
590
- doctor_state,
591
- doctor_zip,
592
- admission_date,
593
- referral_source,
594
- admission_method,
595
- discharge_date,
596
- discharge_reason,
597
- date_of_death,
598
- diagnosis,
599
- procedures,
600
- medications,
601
- preparer_name,
602
- preparer_job_title,
603
- ):
604
- """Generate a PDF discharge form using the provided data"""
605
-
606
- # Create PDF generator
607
- pdf_gen = PDFGenerator()
608
-
609
- # Format data for PDF generation
610
- patient_info = {
611
- "first_name": first_name,
612
- "last_name": last_name,
613
- "dob": dob,
614
- "age": age,
615
- "sex": sex,
616
- "mobile": "", # Not collected in the form
617
- "address": address,
618
- "city": city,
619
- "state": state,
620
- "zip": zip_code,
621
- }
622
-
623
- discharge_info = {
624
- "date_of_admission": admission_date,
625
- "date_of_discharge": discharge_date,
626
- "source_of_admission": referral_source,
627
- "mode_of_admission": admission_method,
628
- "discharge_against_advice": "Yes"
629
- if discharge_reason == "Discharge Against Advice"
630
- else "No",
631
- }
632
-
633
- diagnosis_info = {
634
- "diagnosis": diagnosis,
635
- "operation_procedure": procedures,
636
- "treatment": "", # Not collected in the form
637
- "follow_up": "", # Not collected in the form
638
- }
639
-
640
- medication_info = {
641
- "medications": [medications] if medications else [],
642
- "instructions": "", # Not collected in the form
643
- }
644
-
645
- prepared_by = {
646
- "name": preparer_name,
647
- "title": preparer_job_title,
648
- "signature": "", # Not collected in the form
649
- }
650
-
651
- # Generate PDF
652
- pdf_buffer = pdf_gen.generate_discharge_form(
653
- patient_info,
654
- discharge_info,
655
- diagnosis_info,
656
- medication_info,
657
- prepared_by,
658
- )
659
-
660
- # Create temporary file to save the PDF
661
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
662
- temp_file.write(pdf_buffer.read())
663
- temp_file_path = temp_file.name
664
- temp_file.close()
665
-
666
- return temp_file_path
667
-
668
-
669
- def generate_pdf_from_meldrx(patient_data):
670
- """Generate a PDF using patient data from MeldRx"""
671
- if isinstance(patient_data, str):
672
- # If it's a string (error message or JSON string), try to parse it
673
- try:
674
- patient_data = json.loads(patient_data)
675
- except:
676
- return None, "Invalid patient data format"
677
-
678
- if not patient_data:
679
- return None, "No patient data available"
680
-
681
- try:
682
- # For demonstration, we'll use the first patient in the list if it's a list
683
- if isinstance(patient_data, list) and len(patient_data):
684
- patient = patient_data[0]
685
- else:
686
- patient = patient_data
687
-
688
- # Extract patient info
689
- patient_info = {
690
- "name": f"{patient.get('name', {}).get('given', [''])[0]} {patient.get('name', {}).get('family', '')}",
691
- "dob": patient.get("birthDate", "Unknown"),
692
- "patient_id": patient.get("id", "Unknown"),
693
- "admission_date": datetime.now().strftime("%Y-%m-%d"), # Mock data
694
- "physician": "Dr. Provider", # Mock data
695
- }
696
-
697
- # Mock LLM-generated content - This part needs to be replaced with actual AI generation if desired for MeldRx PDF
698
- llm_content = {
699
- "diagnosis": "Diagnosis information would be generated by AI based on patient data from MeldRx.",
700
- "treatment": "Treatment summary would be generated by AI based on patient data from MeldRx.",
701
- "medications": "Medication list would be generated by AI based on patient data from MeldRx.",
702
- "follow_up": "Follow-up instructions would be generated by AI based on patient data from MeldRx.",
703
- "special_instructions": "Special instructions would be generated by AI based on patient data from MeldRx.",
704
- }
705
-
706
- # Create discharge summary - Using No-AI PDF generation for now, replace with AI-content generation later
707
- output_dir = tempfile.mkdtemp()
708
- pdf_path = generate_discharge_summary(
709
- patient_info, llm_content, output_dir
710
- ) # Still using No-AI template
711
-
712
- return pdf_path, "PDF generated successfully (No AI Content in PDF yet)" # Indicate No-AI content
713
-
714
- except Exception as e:
715
- return None, f"Error generating PDF: {str(e)}"
716
-
717
-
718
- def generate_discharge_paper_one_click():
719
- """One-click function to fetch patient data and generate discharge paper with AI Content."""
720
- patient_data_str = CALLBACK_MANAGER.get_patient_data()
721
- if (
722
- patient_data_str.startswith("Not authenticated")
723
- or patient_data_str.startswith("Failed")
724
- or patient_data_str.startswith("Error")
725
- ):
726
- return None, patient_data_str # Return error message if authentication or data fetch fails
727
-
728
- try:
729
- patient_data = json.loads(patient_data_str)
730
-
731
- # --- AI Content Generation for Discharge Summary ---
732
- # This is a placeholder - Replace with actual AI call using InferenceClient and patient_data to generate content
733
- ai_generated_content = generate_ai_discharge_content(
734
- patient_data
735
- ) # Placeholder AI function
736
-
737
- if not ai_generated_content:
738
- return None, "Error: AI content generation failed."
739
-
740
- # --- PDF Generation with AI Content ---
741
- pdf_path, status_message = generate_pdf_from_meldrx_with_ai_content(
742
- patient_data, ai_generated_content
743
- ) # Function to generate PDF with AI content
744
-
745
- if pdf_path:
746
- return pdf_path, status_message
747
- else:
748
- return None, status_message # Return status message if PDF generation fails
749
-
750
- except json.JSONDecodeError:
751
- return None, "Error: Patient data is not in valid JSON format."
752
- except Exception as e:
753
- return None, f"Error during discharge paper generation: {str(e)}"
754
-
755
-
756
- def generate_ai_discharge_content(patient_data):
757
- """Placeholder function to generate AI content for discharge summary.
758
- Replace this with actual AI call using InferenceClient and patient_data."""
759
- try:
760
- patient_name = (
761
- f"{patient_data['entry'][0]['resource']['name'][0]['given'][0]} {patient_data['entry'][0]['resource']['name'][0]['family']}"
762
- if patient_data.get("entry")
763
- else "Unknown Patient"
764
- )
765
- prompt_text = f"""{system_instructions}\n\nGenerate a discharge summary content (diagnosis, treatment, medications, follow-up instructions, special instructions) for patient: {patient_name}. Base the content on available patient data (if any provided, currently not provided in detail in this mock-up). Focus on creating clinically relevant and informative summary. Remember this is for informational purposes and NOT medical advice."""
766
-
767
- response = client.chat.completions.create(
768
- model=model_name,
769
- messages=[{"role": "user", "content": prompt_text}],
770
- temperature=0.6, # Adjust temperature as needed for content generation
771
- max_tokens=1024, # Adjust max_tokens as needed
772
- top_p=0.9,
773
- )
774
- ai_content = response.choices[0].message.content
775
-
776
- # Basic parsing of AI content - improve this based on desired output structure from LLM
777
- llm_content = {
778
- "diagnosis": "AI Generated Diagnosis (Placeholder):\n"
779
- + extract_section(ai_content, "Diagnosis"), # Example extraction - refine based on LLM output
780
- "treatment": "AI Generated Treatment (Placeholder):\n"
781
- + extract_section(ai_content, "Treatment"),
782
- "medications": "AI Generated Medications (Placeholder):\n"
783
- + extract_section(ai_content, "Medications"),
784
- "follow_up": "AI Generated Follow-up (Placeholder):\n"
785
- + extract_section(ai_content, "Follow-up Instructions"),
786
- "special_instructions": "AI Generated Special Instructions (Placeholder):\n"
787
- + extract_section(ai_content, "Special Instructions"),
788
- }
789
- return llm_content
790
-
791
- except Exception as e:
792
- logger.error(f"Error generating AI discharge content: {e}")
793
- return None
794
-
795
-
796
- def extract_section(ai_content, section_title):
797
- """Simple placeholder function to extract section from AI content.
798
- Improve this with more robust parsing based on LLM output format."""
799
- start_marker = f"**{section_title}:**"
800
- end_marker = "\n\n" # Adjust based on typical LLM output structure
801
- start_index = ai_content.find(start_marker)
802
- if start_index != -1:
803
- start_index += len(start_marker)
804
- end_index = ai_content.find(end_marker, start_index)
805
- if end_index != -1:
806
- return ai_content[start_index:end_index].strip()
807
- return "Not found in AI output."
808
-
809
-
810
- def generate_pdf_from_meldrx_with_ai_content(patient_data, llm_content):
811
- """Generate a PDF using patient data from MeldRx and AI-generated content."""
812
- if isinstance(patient_data, str):
813
- try:
814
- patient_data = json.loads(patient_data)
815
- except:
816
- return None, "Invalid patient data format"
817
-
818
- if not patient_data:
819
- return None, "No patient data available"
820
-
821
- try:
822
- if isinstance(patient_data, list) and len(patient_data):
823
- patient = patient_data[0]
824
- else:
825
- patient = patient_data
826
-
827
- patient_info = {
828
- "name": f"{patient.get('name', {}).get('given', [''])[0]} {patient.get('name', {}).get('family', '')}",
829
- "dob": patient.get("birthDate", "Unknown"),
830
- "patient_id": patient.get("id", "Unknown"),
831
- "admission_date": datetime.now().strftime("%Y-%m-%d"), # Mock data
832
- "physician": "Dr. AI Provider", # Mock data - Indicate AI generated
833
- }
834
-
835
- output_dir = tempfile.mkdtemp()
836
- pdf_path = generate_discharge_summary(
837
- patient_info, llm_content, output_dir
838
- ) # Using AI content now
839
-
840
- return pdf_path, "PDF generated successfully with AI Content" # Indicate AI content
841
-
842
- except Exception as e:
843
- return None, f"Error generating PDF with AI content: {str(e)}"
844
-
845
-
846
- def extract_auth_code_from_url(redirected_url):
847
- """Extracts the authorization code from the redirected URL."""
848
- try:
849
- parsed_url = urlparse(redirected_url)
850
- query_params = parse_qs(parsed_url.query)
851
- if "code" in query_params:
852
- return query_params["code"][0], None # Return code and no error
853
- else:
854
- return None, "Authorization code not found in URL." # Return None and error message
855
- except Exception as e:
856
- return None, f"Error parsing URL: {e}" # Return None and error message
857
-
858
-
859
- # Create a simplified interface to avoid complex component interactions
860
- CALLBACK_MANAGER = CallbackManager(
861
- redirect_uri="https://multitransformer-discharge-guard.hf.space/callback",
862
- client_secret=None,
863
- )
864
-
865
- # Define the cyberpunk theme - using a dark base and neon accents
866
- cyberpunk_theme = gr.themes.Monochrome(
867
- primary_hue="cyan",
868
- secondary_hue="pink",
869
- neutral_hue="slate",
870
- font=["Source Code Pro", "monospace"], # Retro monospace font
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: # Apply the theme here
876
- gr.Markdown("<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>") # Cyberpunk Title
877
-
878
- with gr.Tab("Authenticate with MeldRx", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
879
- gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>SMART on FHIR Authentication</h2>") # Neon Tab Header
880
- auth_url_output = gr.Textbox(label="Authorization URL", value=CALLBACK_MANAGER.get_auth_url(), interactive=False)
881
- gr.Markdown("<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>") # Subdued instructions with neon highlight
882
- redirected_url_input = gr.Textbox(label="Redirected URL") # New textbox for redirected URL
883
- extract_code_button = gr.Button("Extract Authorization Code", elem_classes="cyberpunk-button") # Cyberpunk button style
884
- extracted_code_output = gr.Textbox(label="Extracted Authorization Code", interactive=False) # Textbox to show extracted code
885
-
886
- auth_code_input = gr.Textbox(label="Authorization Code (from above, or paste manually if extraction fails)", interactive=True) # Updated label to be clearer
887
- auth_submit = gr.Button("Submit Code for Authentication", elem_classes="cyberpunk-button") # Cyberpunk button style
888
- auth_result = gr.HTML(label="Authentication Result") # Use HTML for styled result
889
-
890
- patient_data_button = gr.Button("Fetch Patient Data", elem_classes="cyberpunk-button") # Cyberpunk button style
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("Generate PDF from MeldRx Data (No AI)", elem_classes="cyberpunk-button") # Renamed button
895
- meldrx_pdf_status = gr.Textbox(label="PDF Generation Status (No AI)") # Renamed status
896
- meldrx_pdf_download = gr.File(label="Download Generated PDF (No AI)") # Renamed download
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>" # Neon Green Success
903
- else:
904
- return "", f"<span style='color:#FF4500;'>Could not extract authorization code.</span> {error_message or ''}" # Neon Orange Error
905
-
906
-
907
- extract_code_button.click(
908
- fn=process_redirected_url,
909
- inputs=redirected_url_input,
910
- outputs=[extracted_code_output, auth_result],# Reusing auth_result for extraction status
911
- )
912
-
913
- auth_submit.click(
914
- fn=CALLBACK_MANAGER.set_auth_code,
915
- inputs=extracted_code_output, # Using extracted code as input for authentication
916
- outputs=auth_result,
917
- )
918
-
919
- with gr.Tab("Patient Dashboard", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
920
- gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Patient Data</h2>") # Neon Tab Header
921
- dashboard_output = gr.HTML("<p style='color:#A9A9A9;'>Fetch patient data from the Authentication tab first.</p>") # Subdued placeholder text
922
-
923
- refresh_btn = gr.Button("Refresh Data", elem_classes="cyberpunk-button") # Cyberpunk button style
924
-
925
- # Simple function to update dashboard based on fetched data
926
- def update_dashboard():
927
- try:
928
- data = CALLBACK_MANAGER.get_patient_data()
929
- if (
930
- data.startswith("<span style='color:#FF8C00;'>Not authenticated")
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>" # Show auth errors in orange
935
-
936
- try:
937
- # Parse the data
938
- patients_data = json.loads(data)
939
- patients = []
940
-
941
- # Extract patients from bundle
942
- for entry in patients_data.get("entry", []):
943
- resource = entry.get("resource", {})
944
- if resource.get("resourceType") == "Patient":
945
- patients.append(resource)
946
-
947
- # Generate HTML card
948
- html = "<h3 style='color:#00FFFF; text-shadow: 0 0 2px #00FFFF;'>Patients</h3>" # Neon Sub-header
949
- for patient in patients:
950
- # Extract name
951
- name = patient.get("name", [{}])[0]
952
- given = " ".join(name.get("given", ["Unknown"]))
953
- family = name.get("family", "Unknown")
954
-
955
- # Extract other details
956
- gender = patient.get("gender", "unknown").capitalize()
957
- birth_date = patient.get("birthDate", "Unknown")
958
-
959
- # Generate HTML card with cyberpunk styling
960
- html += f"""
961
- <div style="border: 1px solid #00FFFF; padding: 10px; margin: 10px 0; border-radius: 5px; background-color: #222; box-shadow: 0 0 5px #00FFFF;">
962
- <h4 style='color:#00FFFF;'>{given} {family}</h4>
963
- <p style='color:#A9A9A9;'><strong>Gender:</strong> <span style='color:#00FFFF;'>{gender}</span></p>
964
- <p style='color:#A9A9A9;'><strong>Birth Date:</strong> <span style='color:#00FFFF;'>{birth_date}</span></p>
965
- <p style='color:#A9A9A9;'><strong>ID:</strong> <span style='color:#00FFFF;'>{patient.get("id", "Unknown")}</span></p>
966
- </div>
967
- """
968
-
969
- return html
970
- except Exception as e:
971
- return f"<p style='color:#FF6347;'>Error parsing patient data: {str(e)}</p>" # Tomato Error
972
- except Exception as e:
973
- return f"<p style='color:#FF6347;'>Error fetching patient data: {str(e)}</p>" # Tomato Error
974
-
975
-
976
- with gr.Tab("Discharge Form", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
977
- gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Patient Details</h2>") # Neon Tab Header
978
- with gr.Row():
979
- first_name = gr.Textbox(label="First Name")
980
- last_name = gr.Textbox(label="Last Name")
981
- middle_initial = gr.Textbox(label="Middle Initial")
982
- with gr.Row():
983
- dob = gr.Textbox(label="Date of Birth")
984
- age = gr.Textbox(label="Age")
985
- sex = gr.Textbox(label="Sex")
986
- address = gr.Textbox(label="Address")
987
- with gr.Row():
988
- city = gr.Textbox(label="City")
989
- state = gr.Textbox(label="State")
990
- zip_code = gr.Textbox(label="Zip Code")
991
- gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Primary Healthcare Professional Details</h2>") # Neon Sub-header
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")
995
- doctor_middle_initial = gr.Textbox(label="Doctor's Middle Initial")
996
- hospital_name = gr.Textbox(label="Hospital/Clinic Name")
997
- doctor_address = gr.Textbox(label="Address")
998
- with gr.Row():
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("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Admission and Discharge Details</h2>") # Neon Sub-header
1003
- with gr.Row():
1004
- admission_date = gr.Textbox(label="Date of Admission")
1005
- referral_source = gr.Textbox(label="Source of Referral")
1006
- admission_method = gr.Textbox(label="Method of Admission")
1007
- with gr.Row():
1008
- discharge_date = gr.Textbox(label="Date of Discharge")
1009
- discharge_reason = gr.Radio(
1010
- ["Treated", "Transferred", "Discharge Against Advice", "Patient Died"],
1011
- label="Discharge Reason",
1012
- )
1013
- date_of_death = gr.Textbox(label="Date of Death (if applicable)")
1014
- gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Diagnosis & Procedures</h2>") # Neon Sub-header
1015
- diagnosis = gr.Textbox(label="Diagnosis")
1016
- procedures = gr.Textbox(label="Operation & Procedures")
1017
- gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Medication Details</h2>") # Neon Sub-header
1018
- medications = gr.Textbox(label="Medication on Discharge")
1019
- gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Prepared By</h2>") # Neon Sub-header
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("Display Form", elem_classes="cyberpunk-button") # Cyberpunk button style
1027
- submit_pdf = gr.Button("Generate PDF (No AI)", elem_classes="cyberpunk-button") # Renamed button to clarify no AI and styled
1028
-
1029
- # Output areas
1030
- form_output = gr.HTML() # Use HTML to render styled form
1031
- pdf_output = gr.File(label="Download PDF (No AI)") # Renamed output to clarify no AI
1032
-
1033
- # Connect the display form button
1034
- submit_display.click(
1035
- display_form,
1036
- inputs=[
1037
- first_name,
1038
- last_name,
1039
- middle_initial,
1040
- dob,
1041
- age,
1042
- sex,
1043
- address,
1044
- city,
1045
- state,
1046
- zip_code,
1047
- doctor_first_name,
1048
- doctor_last_name,
1049
- doctor_middle_initial,
1050
- hospital_name,
1051
- doctor_address,
1052
- doctor_city,
1053
- doctor_state,
1054
- doctor_zip,
1055
- admission_date,
1056
- referral_source,
1057
- admission_method,
1058
- discharge_date,
1059
- discharge_reason,
1060
- date_of_death,
1061
- diagnosis,
1062
- procedures,
1063
- medications,
1064
- preparer_name,
1065
- preparer_job_title,
1066
- ],
1067
- outputs=form_output
1068
- )
1069
-
1070
- # Connect the generate PDF button (No AI version)
1071
- submit_pdf.click(
1072
- generate_pdf_from_form,
1073
- inputs=[
1074
- first_name,
1075
- last_name,
1076
- middle_initial,
1077
- dob,
1078
- age,
1079
- sex,
1080
- address,
1081
- city,
1082
- state,
1083
- zip_code,
1084
- doctor_first_name,
1085
- doctor_last_name,
1086
- doctor_middle_initial,
1087
- hospital_name,
1088
- doctor_address,
1089
- doctor_city,
1090
- doctor_state,
1091
- doctor_zip,
1092
- admission_date,
1093
- referral_source,
1094
- admission_method,
1095
- discharge_date,
1096
- discharge_reason,
1097
- date_of_death,
1098
- diagnosis,
1099
- procedures,
1100
- medications,
1101
- preparer_name,
1102
- preparer_job_title,
1103
- ],
1104
- outputs=pdf_output
1105
- )
1106
-
1107
- with gr.Tab("Medical File Analysis", elem_classes="cyberpunk-tab"): # Optional: Class for tab styling
1108
- gr.Markdown("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>Analyze Medical Files with Discharge Guard AI</h2>") # Neon Tab Header
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("Analyze DICOM with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
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("Analyze HL7 with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
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("Analyze XML with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
1127
-
1128
- ccda_file = gr.File(
1129
- file_types=[".xml", ".cda", ".ccd"], label="Upload CCDA File (.xml, .cda, .ccd)"
1130
- )
1131
- ccda_ai_output = gr.Textbox(label="CCDA Analysis Report", lines=5)
1132
- analyze_ccda_button = gr.Button("Analyze CCDA with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
1133
-
1134
- ccd_file = gr.File(
1135
- file_types=[".ccd"],
1136
- label="Upload CCD File (.ccd)",
1137
- ) # Redundant, as CCDA also handles .ccd, but kept for clarity
1138
- ccd_ai_output = gr.Textbox(
1139
- label="CCD Analysis Report", lines=5
1140
- ) # Redundant
1141
- analyze_ccd_button = gr.Button("Analyze CCD with AI", elem_classes="cyberpunk-button") # Cyberpunk button style # Redundant
1142
- pdf_file = gr.File(
1143
- file_types=[".pdf"], label="Upload PDF File (.pdf)"
1144
- )
1145
- pdf_ai_output = gr.Textbox(label="PDF Analysis Report", lines=5)
1146
- analyze_pdf_button = gr.Button("Analyze PDF with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
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("Analyze CSV with AI", elem_classes="cyberpunk-button") # Cyberpunk button style
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
1182
- )
1183
- analyze_csv_button.click(
1184
- analyze_csv_file_with_ai, inputs=csv_file, outputs=csv_ai_output
1185
- )
1186
-
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("<h2 style='color:#00FFFF; text-shadow: 0 0 3px #00FFFF;'>One-Click Medical Discharge Paper Generation with AI Content</h2>") # Neon Tab Header
1191
- one_click_ai_pdf_button = gr.Button(
1192
- "Generate Discharge Paper with AI (One-Click)", elem_classes="cyberpunk-button"
1193
- ) # Updated button label and styled
1194
- one_click_ai_pdf_status = gr.Textbox(
1195
- label="Discharge Paper Generation Status (AI)"
1196
- ) # Updated status label
1197
- one_click_ai_pdf_download = gr.File(
1198
- label="Download Discharge Paper (AI)"
1199
- ) # Updated download label
1200
-
1201
- one_click_ai_pdf_button.click(
1202
- generate_discharge_paper_one_click, # Use the one-click function that now calls AI
1203
- inputs=[],
1204
- outputs=[one_click_ai_pdf_download, one_click_ai_pdf_status],
1205
- )
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
1227
- patient_data_button.click(
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)
 
1
  import gradio as gr
2
+ from utils.meldrx import MeldRxAPI
3
  import json
4
  import os
5
  import tempfile
 
8
  import logging
9
  from huggingface_hub import InferenceClient # Import InferenceClient
10
  from urllib.parse import urlparse, parse_qs # Import URL parsing utilities
11
+ from utils.callbackmanager import CallbackManager
12
+ from prompts import system_instructions
13
  # Set up logging
14
  logging.basicConfig(level=logging.INFO)
15
  logger = logging.getLogger(__name__)
16
 
17
  # Import PDF utilities
18
  from pdfutils import PDFGenerator, generate_discharge_summary
19
+ from utils.callbackmanager import CallbackManager
20
  # Import necessary libraries for new file types and AI analysis functions
21
  import pydicom # For DICOM
22
  import hl7 # For HL7
 
26
  import io # For IO operations
27
  from PIL import Image # For image handling
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  # Initialize Inference Client - Ensure YOUR_HF_TOKEN is set in environment variables or replace with your actual token
30
  HF_TOKEN = os.getenv("HF_TOKEN") # Or replace with your actual token string
31
  if not HF_TOKEN:
 
35
  client = InferenceClient(api_key=HF_TOKEN)
36
  model_name = "meta-llama/Llama-3.3-70B-Instruct" # Specify the model to use
37
 
38
+ def generate_pdf_from_form( first_name, last_name, middle_initial, dob, age, sex, address, city, state, zip_code, doctor_first_name, doctor_last_name, doctor_middle_initial, hospital_name, doctor_address, doctor_city, doctor_state, doctor_zip, admission_date, referral_source, admission_method, discharge_date, discharge_reason, date_of_death, diagnosis, procedures, medications, preparer_name, preparer_job_title,):
39
+ """Generate a PDF discharge form using the provided data"""
40
+
41
+ # Create PDF generator
42
+ pdf_gen = PDFGenerator()
43
+
44
+ # Format data for PDF generation
45
+ patient_info = {
46
+ "first_name": first_name,
47
+ "last_name": last_name,
48
+ "dob": dob,
49
+ "age": age,
50
+ "sex": sex,
51
+ "mobile": "", # Not collected in the form
52
+ "address": address,
53
+ "city": city,
54
+ "state": state,
55
+ "zip": zip_code,
56
+ }
57
+
58
+ discharge_info = {
59
+ "date_of_admission": admission_date,
60
+ "date_of_discharge": discharge_date,
61
+ "source_of_admission": referral_source,
62
+ "mode_of_admission": admission_method,
63
+ "discharge_against_advice": "Yes"
64
+ if discharge_reason == "Discharge Against Advice"
65
+ else "No",
66
+ }
67
+
68
+ diagnosis_info = {
69
+ "diagnosis": diagnosis,
70
+ "operation_procedure": procedures,
71
+ "treatment": "", # Not collected in the form
72
+ "follow_up": "", # Not collected in the form
73
+ }
74
+
75
+ medication_info = {
76
+ "medications": [medications] if medications else [],
77
+ "instructions": "", # Not collected in the form
78
+ }
79
+
80
+ prepared_by = {
81
+ "name": preparer_name,
82
+ "title": preparer_job_title,
83
+ "signature": "", # Not collected in the form
84
+ }
85
+
86
+ # Generate PDF
87
+ pdf_buffer = pdf_gen.generate_discharge_form(patient_info,discharge_info,diagnosis_info,medication_info,prepared_by,)
88
+
89
+ # Create temporary file to save the PDF
90
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
91
+ temp_file.write(pdf_buffer.read())
92
+ temp_file_path = temp_file.name
93
+ temp_file.close()
94
+
95
+ return temp_file_path
96
+
97
+
98
+ def generate_pdf_from_meldrx(patient_data):
99
+ """Generate a PDF using patient data from MeldRx"""
100
+ if isinstance(patient_data, str):
101
+ # If it's a string (error message or JSON string), try to parse it
102
+ try:
103
+ patient_data = json.loads(patient_data)
104
+ except:
105
+ return None, "Invalid patient data format"
106
+
107
+ if not patient_data:
108
+ return None, "No patient data available"
109
+
110
+ try:
111
+ # For demonstration, we'll use the first patient in the list if it's a list
112
+ if isinstance(patient_data, list) and len(patient_data):
113
+ patient = patient_data[0]
114
+ else:
115
+ patient = patient_data
116
+
117
+ # Extract patient info
118
+ patient_info = {
119
+ "name": f"{patient.get('name', {}).get('given', [''])[0]} {patient.get('name', {}).get('family', '')}",
120
+ "dob": patient.get("birthDate", "Unknown"),
121
+ "patient_id": patient.get("id", "Unknown"),
122
+ "admission_date": datetime.now().strftime("%Y-%m-%d"), # Mock data
123
+ "physician": "Dr. Provider", # Mock data
124
+ }
125
+
126
+ # Mock LLM-generated content - This part needs to be replaced with actual AI generation if desired for MeldRx PDF
127
+ llm_content = {
128
+ "diagnosis": "Diagnosis information would be generated by AI based on patient data from MeldRx.",
129
+ "treatment": "Treatment summary would be generated by AI based on patient data from MeldRx.",
130
+ "medications": "Medication list would be generated by AI based on patient data from MeldRx.",
131
+ "follow_up": "Follow-up instructions would be generated by AI based on patient data from MeldRx.",
132
+ "special_instructions": "Special instructions would be generated by AI based on patient data from MeldRx.",
133
+ }
134
+
135
+ # Create discharge summary - Using No-AI PDF generation for now, replace with AI-content generation later
136
+ output_dir = tempfile.mkdtemp()
137
+ pdf_path = generate_discharge_summary(
138
+ patient_info, llm_content, output_dir
139
+ ) # Still using No-AI template
140
+
141
+ return pdf_path, "PDF generated successfully (No AI Content in PDF yet)" # Indicate No-AI content
142
+
143
+ except Exception as e:
144
+ return None, f"Error generating PDF: {str(e)}"
145
+
146
+ # CALLBACK_MANAGER = CallbackManager(
147
+ # redirect_uri="https://multitransformer-discharge-guard.hf.space/callback",
148
+ # client_secret=None,
149
+ # )
150
+
151
+ def generate_ai_discharge_content(patient_data):
152
+ """Placeholder function to generate AI content for discharge summary.
153
+ Replace this with actual AI call using InferenceClient and patient_data."""
154
+ try:
155
+ patient_name = (
156
+ f"{patient_data['entry'][0]['resource']['name'][0]['given'][0]} {patient_data['entry'][0]['resource']['name'][0]['family']}"
157
+ if patient_data.get("entry")
158
+ else "Unknown Patient"
159
+ )
160
+ prompt_text = f"""{system_instructions}\n\nGenerate a discharge summary content (diagnosis, treatment, medications, follow-up instructions, special instructions) for patient: {patient_name}. Base the content on available patient data (if any provided, currently not provided in detail in this mock-up). Focus on creating clinically relevant and informative summary. Remember this is for informational purposes and NOT medical advice."""
161
+
162
+ response = client.chat.completions.create(
163
+ model=model_name,
164
+ messages=[{"role": "user", "content": prompt_text}],
165
+ temperature=0.6, # Adjust temperature as needed for content generation
166
+ max_tokens=1024, # Adjust max_tokens as needed
167
+ top_p=0.9,
168
+ )
169
+ ai_content = response.choices[0].message.content
170
+
171
+ # Basic parsing of AI content - improve this based on desired output structure from LLM
172
+ llm_content = {
173
+ "diagnosis": "AI Generated Diagnosis (Placeholder):\n"
174
+ + extract_section(ai_content, "Diagnosis"), # Example extraction - refine based on LLM output
175
+ "treatment": "AI Generated Treatment (Placeholder):\n"
176
+ + extract_section(ai_content, "Treatment"),
177
+ "medications": "AI Generated Medications (Placeholder):\n"
178
+ + extract_section(ai_content, "Medications"),
179
+ "follow_up": "AI Generated Follow-up (Placeholder):\n"
180
+ + extract_section(ai_content, "Follow-up Instructions"),
181
+ "special_instructions": "AI Generated Special Instructions (Placeholder):\n"
182
+ + extract_section(ai_content, "Special Instructions"),
183
+ }
184
+ return llm_content
185
+
186
+ except Exception as e:
187
+ logger.error(f"Error generating AI discharge content: {e}")
188
+ return None
189
+
190
+
191
+ def extract_section(ai_content, section_title):
192
+ """Simple placeholder function to extract section from AI content.
193
+ Improve this with more robust parsing based on LLM output format."""
194
+ start_marker = f"**{section_title}:**"
195
+ end_marker = "\n\n" # Adjust based on typical LLM output structure
196
+ start_index = ai_content.find(start_marker)
197
+ if start_index != -1:
198
+ start_index += len(start_marker)
199
+ end_index = ai_content.find(end_marker, start_index)
200
+ if end_index != -1:
201
+ return ai_content[start_index:end_index].strip()
202
+ return "Not found in AI output."
203
+
204
+
205
+ def generate_pdf_from_meldrx_with_ai_content(patient_data, llm_content):
206
+ """Generate a PDF using patient data from MeldRx and AI-generated content."""
207
+ if isinstance(patient_data, str):
208
+ try:
209
+ patient_data = json.loads(patient_data)
210
+ except:
211
+ return None, "Invalid patient data format"
212
+
213
+ if not patient_data:
214
+ return None, "No patient data available"
215
+
216
+ try:
217
+ if isinstance(patient_data, list) and len(patient_data):
218
+ patient = patient_data[0]
219
+ else:
220
+ patient = patient_data
221
+
222
+ patient_info = {
223
+ "name": f"{patient.get('name', {}).get('given', [''])[0]} {patient.get('name', {}).get('family', '')}",
224
+ "dob": patient.get("birthDate", "Unknown"),
225
+ "patient_id": patient.get("id", "Unknown"),
226
+ "admission_date": datetime.now().strftime("%Y-%m-%d"), # Mock data
227
+ "physician": "Dr. AI Provider", # Mock data - Indicate AI generated
228
+ }
229
+
230
+ output_dir = tempfile.mkdtemp()
231
+ pdf_path = generate_discharge_summary(
232
+ patient_info, llm_content, output_dir
233
+ ) # Using AI content now
234
+
235
+ return pdf_path, "PDF generated successfully with AI Content" # Indicate AI content
236
+
237
+ except Exception as e:
238
+ return None, f"Error generating PDF with AI content: {str(e)}"
239
+
240
 
241
  def analyze_dicom_file_with_ai(dicom_file_path): # Modified to accept file path
242
  """Analyzes DICOM file metadata using Discharge Guard AI."""
 
558
  trace_data_detail_csv_analysis["error"] = f"AI Analysis Error: {e}"
559
  return error_message, trace_data_detail_csv_analysis
560
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
meldrx.py → utils/meldrx.py RENAMED
File without changes