Ali2206 commited on
Commit
3af8921
·
verified ·
1 Parent(s): ad7372c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -159
app.py CHANGED
@@ -57,6 +57,7 @@ def extract_medical_data(df: pd.DataFrame) -> Dict[str, Any]:
57
 
58
  for _, row in df.iterrows():
59
  record = {
 
60
  'form_name': row.get('Form Name', ''),
61
  'form_item': row.get('Form Item', ''),
62
  'response': row.get('Item Response', ''),
@@ -69,117 +70,134 @@ def extract_medical_data(df: pd.DataFrame) -> Dict[str, Any]:
69
  return medical_data
70
 
71
  def identify_red_flags(records: List[Dict[str, Any]]) -> Dict[str, Any]:
72
- """Identify potential red flags in medical records"""
73
  red_flags = {
74
  'symptoms': defaultdict(list),
75
  'medications': defaultdict(list),
76
  'diagnoses': defaultdict(list),
77
  'vitals': defaultdict(list),
78
- 'labs': defaultdict(list)
 
79
  }
80
 
81
- for record in records:
82
- form_name = record['form_name'].lower()
83
- item = record['form_item'].lower()
84
- response = record['response'].lower()
85
-
86
- # Symptom patterns
87
- if 'pain' in item or 'symptom' in form_name:
88
- if 'severe' in response or 'chronic' in response:
89
- red_flags['symptoms'][item].append(response)
90
-
91
- # Medication checks
92
- elif 'medication' in form_name or 'drug' in form_name:
93
- if 'interaction' in response or 'allergy' in response:
94
- red_flags['medications'][item].append(response)
95
-
96
- # Diagnosis inconsistencies
97
- elif 'diagnosis' in form_name:
98
- if 'rule out' in response or 'possible' in response:
99
- red_flags['diagnoses'][item].append(response)
100
-
101
- # Abnormal vitals
102
- elif 'vital' in form_name:
103
- try:
104
- value = float(re.search(r'\d+\.?\d*', response).group())
105
- if ('blood pressure' in item and value > 140) or \
106
- ('heart rate' in item and (value < 50 or value > 100)) or \
107
- ('temperature' in item and value > 38):
108
- red_flags['vitals'][item].append(response)
109
- except:
110
- pass
111
-
112
- # Abnormal labs
113
- elif 'lab' in form_name or 'test' in form_name:
114
- if 'abnormal' in response or 'high' in response or 'low' in response:
115
- red_flags['labs'][item].append(response)
 
116
 
117
  return red_flags
118
 
119
- def generate_analysis_prompt(booking: str, records: List[Dict[str, Any]], red_flags: Dict[str, Any]) -> str:
120
- """Generate structured prompt for analysis"""
121
- records_text = "\n".join(
122
- f"- {r['form_name']}: {r['form_item']} = {r['response']} ({r['date']} by {r['interviewer']})\n {r['description']}"
123
- for r in records
124
- )
 
 
 
 
 
125
 
126
- red_flags_text = "\n".join(
127
- f"### {category.capitalize()} Red Flags\n" + "\n".join(
128
- f"- {item}: {', '.join(responses)}"
129
- for item, responses in items.items()
130
- )
131
- for category, items in red_flags.items() if items
132
- )
 
 
 
 
 
 
 
133
 
134
  prompt = f"""
135
- **Patient Booking Number**: {booking}
136
 
137
  **Medical Records Summary**:
138
- {records_text}
139
 
140
- **Identified Red Flags**:
141
- {red_flags_text if red_flags_text else "No obvious red flags detected"}
142
 
143
- **Comprehensive Analysis Instructions**:
144
- 1. Review all medical data and red flags above
145
- 2. Identify any potential missed diagnoses based on symptoms, labs, and clinical findings
146
- 3. Check for medication conflicts or inappropriate prescriptions
147
- 4. Note any incomplete assessments or missing diagnostic workups
148
- 5. Flag any urgent follow-up needs or critical findings
149
- 6. Provide recommendations in clear, actionable terms
150
 
151
  **Required Output Format**:
152
- ### Missed Diagnoses
153
- - [List any conditions that may have been overlooked based on the data]
 
 
 
 
154
 
155
- ### Medication Issues
156
- - [List any medication conflicts, inappropriate prescriptions, or missing medications]
 
157
 
158
- ### Assessment Gaps
159
- - [List any incomplete assessments or missing diagnostic tests]
 
160
 
161
- ### Urgent Follow-up
162
- - [List any findings requiring immediate attention]
 
163
 
164
- ### Clinical Recommendations
165
- - [Provide specific recommendations for next steps]
 
166
  """
167
  return prompt
168
 
169
- def parse_excel_to_prompts(file_path: str) -> List[Tuple[str, str]]:
170
- """Parse Excel file into analysis prompts with red flag detection"""
171
  try:
172
  xl = pd.ExcelFile(file_path)
173
  df = xl.parse(xl.sheet_names[0], header=0).fillna("")
174
  medical_data = extract_medical_data(df)
175
-
176
- prompts = []
177
- for booking, records in medical_data.items():
178
- red_flags = identify_red_flags(records)
179
- prompt = generate_analysis_prompt(booking, records, red_flags)
180
- prompts.append((booking, prompt))
181
-
182
- return prompts
183
  except Exception as e:
184
  raise ValueError(f"Error parsing Excel file: {str(e)}")
185
 
@@ -204,14 +222,10 @@ def init_agent():
204
  agent.init_model()
205
  return agent
206
 
207
- def format_markdown(text: str) -> str:
208
- """Convert markdown text to HTML for better display"""
209
- return markdown.markdown(text, extensions=['fenced_code', 'tables'])
210
-
211
  def create_ui(agent):
212
  """Create Gradio UI interface"""
213
  with gr.Blocks(theme=gr.themes.Soft(), title="Clinical Oversight Assistant") as demo:
214
- gr.Markdown("# 🏥 Clinical Oversight Assistant (Missed Diagnosis Detection)")
215
 
216
  with gr.Tabs():
217
  with gr.TabItem("Analysis"):
@@ -231,12 +245,12 @@ def create_ui(agent):
231
  )
232
  with gr.Row():
233
  clear_btn = gr.Button("Clear", variant="secondary")
234
- send_btn = gr.Button("Analyze", variant="primary")
235
 
236
  # Right column - Outputs
237
  with gr.Column(scale=2):
238
  chatbot = gr.Chatbot(
239
- label="Analysis Results",
240
  height=600,
241
  bubble_full_width=False,
242
  show_copy_button=True,
@@ -253,94 +267,62 @@ def create_ui(agent):
253
 
254
  1. **Upload Excel File**: Select your patient records Excel file
255
  2. **Add Instructions** (Optional): Provide any specific analysis requests
256
- 3. **Click Analyze**: The system will process each patient record
257
- 4. **Review Results**: Analysis appears in the chat window
258
- 5. **Download Report**: Get a full text report of all findings
259
 
260
- ### Excel File Requirements
261
- Your Excel file must contain these columns:
262
- - Booking Number (patient identifier)
263
- - Form Name (type of medical form)
264
- - Form Item (specific field name)
265
- - Item Response (patient response or value)
266
- - Interview Date (date of recording)
267
- - Interviewer (who recorded the data)
268
- - Description (additional notes)
269
-
270
- ### Analysis Includes
271
- - **Missed diagnoses**: Potential conditions not identified
272
- - **Medication issues**: Conflicts, side effects, inappropriate prescriptions
273
- - **Assessment gaps**: Missing tests or incomplete evaluations
274
- - **Urgent follow-up**: Critical findings needing immediate attention
275
- - **Clinical recommendations**: Actionable next steps
276
  """)
277
 
278
- def format_message(role: str, content: str) -> Tuple[str, str]:
279
- """Format messages for the chatbot in (user, bot) format"""
280
- if role == "user":
281
- return (content, None)
282
- else:
283
- return (None, content)
284
-
285
  def analyze(message: str, chat_history: List[Tuple[str, str]], file) -> Tuple[List[Tuple[str, str]], str]:
286
- """Main analysis function"""
287
  if not file:
288
  raise gr.Error("Please upload an Excel file first")
289
 
290
  try:
291
- # Initialize chat history with user message
292
- new_history = chat_history + [format_message("user", message)]
293
- new_history.append(format_message("assistant", "⏳ Processing Excel data..."))
294
  yield new_history, None
295
 
296
- prompts = parse_excel_to_prompts(file.name)
297
- full_output = ""
298
 
299
- for idx, (booking, prompt) in enumerate(prompts, 1):
300
- chunk_output = ""
301
- try:
302
- for result in agent.run_gradio_chat(
303
- message=prompt,
304
- history=[],
305
- temperature=0.2,
306
- max_new_tokens=1024,
307
- max_token=4096,
308
- call_agent=False,
309
- conversation=[],
310
- ):
311
- if isinstance(result, list):
312
- for r in result:
313
- if hasattr(r, 'content') and r.content:
314
- cleaned = clean_response(r.content)
315
- chunk_output += cleaned + "\n"
316
- elif isinstance(result, str):
317
- cleaned = clean_response(result)
318
- chunk_output += cleaned + "\n"
319
-
320
- if chunk_output:
321
- output = f"## Patient Booking: {booking}\n{chunk_output.strip()}\n"
322
- new_history[-1] = format_message("assistant", output)
323
- yield new_history, None
324
-
325
- except Exception as e:
326
- error_msg = f"⚠️ Error processing booking {booking}: {str(e)}"
327
- new_history.append(format_message("assistant", error_msg))
328
- yield new_history, None
329
- continue
330
-
331
- if chunk_output:
332
- output = f"## Patient Booking: {booking}\n{chunk_output.strip()}\n"
333
- new_history.append(format_message("assistant", output))
334
- full_output += output + "\n"
335
  yield new_history, None
336
 
337
  # Save report
338
  file_hash_value = file_hash(file.name)
339
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
340
- report_path = os.path.join(report_dir, f"{file_hash_value}_{timestamp}_report.md")
341
 
342
  with open(report_path, "w", encoding="utf-8") as f:
343
- f.write("# Clinical Oversight Analysis Report\n\n")
344
  f.write(f"**Generated on**: {timestamp}\n\n")
345
  f.write(f"**Source file**: {file.name}\n\n")
346
  f.write(full_output)
@@ -348,7 +330,7 @@ def create_ui(agent):
348
  yield new_history, report_path if os.path.exists(report_path) else None
349
 
350
  except Exception as e:
351
- new_history.append(format_message("assistant", f"❌ Error: {str(e)}"))
352
  yield new_history, None
353
  raise gr.Error(f"Analysis failed: {str(e)}")
354
 
 
57
 
58
  for _, row in df.iterrows():
59
  record = {
60
+ 'booking': row.get('Booking Number', ''),
61
  'form_name': row.get('Form Name', ''),
62
  'form_item': row.get('Form Item', ''),
63
  'response': row.get('Item Response', ''),
 
70
  return medical_data
71
 
72
  def identify_red_flags(records: List[Dict[str, Any]]) -> Dict[str, Any]:
73
+ """Identify potential red flags across all medical records"""
74
  red_flags = {
75
  'symptoms': defaultdict(list),
76
  'medications': defaultdict(list),
77
  'diagnoses': defaultdict(list),
78
  'vitals': defaultdict(list),
79
+ 'labs': defaultdict(list),
80
+ 'patients': defaultdict(list)
81
  }
82
 
83
+ for booking, patient_records in records.items():
84
+ for record in patient_records:
85
+ form_name = record['form_name'].lower()
86
+ item = record['form_item'].lower()
87
+ response = record['response'].lower()
88
+
89
+ # Symptom patterns
90
+ if 'pain' in item or 'symptom' in form_name:
91
+ if 'severe' in response or 'chronic' in response:
92
+ red_flags['symptoms'][item].append((booking, response))
93
+
94
+ # Medication checks
95
+ elif 'medication' in form_name or 'drug' in form_name:
96
+ if 'interaction' in response or 'allergy' in response:
97
+ red_flags['medications'][item].append((booking, response))
98
+
99
+ # Diagnosis inconsistencies
100
+ elif 'diagnosis' in form_name:
101
+ if 'rule out' in response or 'possible' in response:
102
+ red_flags['diagnoses'][item].append((booking, response))
103
+
104
+ # Abnormal vitals
105
+ elif 'vital' in form_name:
106
+ try:
107
+ value = float(re.search(r'\d+\.?\d*', response).group())
108
+ if ('blood pressure' in item and value > 140) or \
109
+ ('heart rate' in item and (value < 50 or value > 100)) or \
110
+ ('temperature' in item and value > 38):
111
+ red_flags['vitals'][item].append((booking, response))
112
+ except:
113
+ pass
114
+
115
+ # Abnormal labs
116
+ elif 'lab' in form_name or 'test' in form_name:
117
+ if 'abnormal' in response or 'high' in response or 'low' in response:
118
+ red_flags['labs'][item].append((booking, response))
119
 
120
  return red_flags
121
 
122
+ def generate_combined_prompt(all_records: Dict[str, Any], red_flags: Dict[str, Any]]) -> str:
123
+ """Generate a single comprehensive prompt for all patient data"""
124
+ # Create summary of all records
125
+ records_summary = []
126
+ for booking, records in all_records.items():
127
+ records_summary.append(f"\n## Patient {booking}")
128
+ for r in records:
129
+ records_summary.append(
130
+ f"- {r['form_name']}: {r['form_item']} = {r['response']} "
131
+ f"({r['date']} by {r['interviewer']})\n {r['description']}"
132
+ )
133
 
134
+ # Format red flags with patient references
135
+ red_flags_text = []
136
+ for category, items in red_flags.items():
137
+ if items:
138
+ red_flags_text.append(f"\n### {category.capitalize()} Red Flags")
139
+ for item, entries in items.items():
140
+ patient_entries = defaultdict(list)
141
+ for booking, response in entries:
142
+ patient_entries[booking].append(response)
143
+
144
+ for booking, responses in patient_entries.items():
145
+ red_flags_text.append(
146
+ f"- {item} (Patient {booking}): {', '.join(responses)}"
147
+ )
148
 
149
  prompt = f"""
150
+ **COMPREHENSIVE PATIENT ANALYSIS**
151
 
152
  **Medical Records Summary**:
153
+ {"".join(records_summary)}
154
 
155
+ **Identified Red Flags Across All Patients**:
156
+ {"".join(red_flags_text) if red_flags_text else "No obvious red flags detected"}
157
 
158
+ **Analysis Instructions**:
159
+ 1. Review ALL patient data holistically
160
+ 2. Identify patterns that might indicate systemic issues
161
+ 3. Check for recurring medication problems across patients
162
+ 4. Note any common missed diagnoses
163
+ 5. Flag any urgent cases needing immediate attention
164
+ 6. Provide overall clinical recommendations
165
 
166
  **Required Output Format**:
167
+ ### Summary of Findings
168
+ [Overview of most significant findings across all patients]
169
+
170
+ ### Common Missed Diagnoses
171
+ - [Conditions frequently overlooked across multiple patients]
172
+ - [Specific patients affected: Booking numbers]
173
 
174
+ ### Recurring Medication Issues
175
+ - [Common drug interactions or inappropriate prescriptions]
176
+ - [Patients affected]
177
 
178
+ ### Systemic Assessment Gaps
179
+ - [Patterns of incomplete assessments across patients]
180
+ - [Recommended additional tests]
181
 
182
+ ### Critical Cases Needing Follow-up
183
+ - [Patients requiring urgent attention]
184
+ - [Specific reasons]
185
 
186
+ ### Overall Recommendations
187
+ - [General recommendations for clinical practice]
188
+ - [Specific actions for different patient groups]
189
  """
190
  return prompt
191
 
192
+ def parse_excel_to_combined_prompt(file_path: str) -> str:
193
+ """Parse Excel file into a single comprehensive analysis prompt"""
194
  try:
195
  xl = pd.ExcelFile(file_path)
196
  df = xl.parse(xl.sheet_names[0], header=0).fillna("")
197
  medical_data = extract_medical_data(df)
198
+ red_flags = identify_red_flags(medical_data)
199
+ prompt = generate_combined_prompt(medical_data, red_flags)
200
+ return prompt
 
 
 
 
 
201
  except Exception as e:
202
  raise ValueError(f"Error parsing Excel file: {str(e)}")
203
 
 
222
  agent.init_model()
223
  return agent
224
 
 
 
 
 
225
  def create_ui(agent):
226
  """Create Gradio UI interface"""
227
  with gr.Blocks(theme=gr.themes.Soft(), title="Clinical Oversight Assistant") as demo:
228
+ gr.Markdown("# 🏥 Comprehensive Clinical Analysis")
229
 
230
  with gr.Tabs():
231
  with gr.TabItem("Analysis"):
 
245
  )
246
  with gr.Row():
247
  clear_btn = gr.Button("Clear", variant="secondary")
248
+ send_btn = gr.Button("Analyze All Patients", variant="primary")
249
 
250
  # Right column - Outputs
251
  with gr.Column(scale=2):
252
  chatbot = gr.Chatbot(
253
+ label="Comprehensive Analysis Results",
254
  height=600,
255
  bubble_full_width=False,
256
  show_copy_button=True,
 
267
 
268
  1. **Upload Excel File**: Select your patient records Excel file
269
  2. **Add Instructions** (Optional): Provide any specific analysis requests
270
+ 3. **Click Analyze**: The system will process ALL patient records together
271
+ 4. **Review Results**: Comprehensive analysis appears in the chat window
272
+ 5. **Download Report**: Get a complete text report of all findings
273
 
274
+ ### Key Features
275
+ - **Holistic analysis** of all patient records
276
+ - **Pattern detection** across multiple patients
277
+ - **Systemic issues** identification
278
+ - **Prioritized recommendations** based on severity
 
 
 
 
 
 
 
 
 
 
 
279
  """)
280
 
 
 
 
 
 
 
 
281
  def analyze(message: str, chat_history: List[Tuple[str, str]], file) -> Tuple[List[Tuple[str, str]], str]:
282
+ """Main analysis function for all patients"""
283
  if not file:
284
  raise gr.Error("Please upload an Excel file first")
285
 
286
  try:
287
+ # Initialize chat history
288
+ new_history = chat_history + [(message, None)]
289
+ new_history.append((None, "⏳ Processing all patient data..."))
290
  yield new_history, None
291
 
292
+ # Generate combined prompt
293
+ prompt = parse_excel_to_combined_prompt(file.name)
294
 
295
+ # Run analysis
296
+ full_output = ""
297
+ for result in agent.run_gradio_chat(
298
+ message=prompt,
299
+ history=[],
300
+ temperature=0.2,
301
+ max_new_tokens=2048, # Increased for comprehensive analysis
302
+ max_token=4096,
303
+ call_agent=False,
304
+ conversation=[],
305
+ ):
306
+ if isinstance(result, list):
307
+ for r in result:
308
+ if hasattr(r, 'content') and r.content:
309
+ cleaned = clean_response(r.content)
310
+ full_output += cleaned + "\n"
311
+ elif isinstance(result, str):
312
+ cleaned = clean_response(result)
313
+ full_output += cleaned + "\n"
314
+
315
+ if full_output:
316
+ new_history[-1] = (None, full_output.strip())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  yield new_history, None
318
 
319
  # Save report
320
  file_hash_value = file_hash(file.name)
321
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
322
+ report_path = os.path.join(report_dir, f"comprehensive_{file_hash_value}_{timestamp}_report.md")
323
 
324
  with open(report_path, "w", encoding="utf-8") as f:
325
+ f.write("# Comprehensive Clinical Analysis Report\n\n")
326
  f.write(f"**Generated on**: {timestamp}\n\n")
327
  f.write(f"**Source file**: {file.name}\n\n")
328
  f.write(full_output)
 
330
  yield new_history, report_path if os.path.exists(report_path) else None
331
 
332
  except Exception as e:
333
+ new_history.append((None, f"❌ Error: {str(e)}"))
334
  yield new_history, None
335
  raise gr.Error(f"Analysis failed: {str(e)}")
336