varshakolanu commited on
Commit
25255a7
·
verified ·
1 Parent(s): 8ac3371

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +223 -202
app.py CHANGED
@@ -7,6 +7,7 @@ import logging
7
  import pandas as pd
8
  import os
9
  from urllib.parse import quote, unquote
 
10
 
11
  # Configure logging for audit purposes (FR4, NFR: Security)
12
  logging.basicConfig(level=logging.INFO, filename='app_log.txt',
@@ -66,7 +67,7 @@ SALESFORCE_HEADERS = {
66
  "Content-Type": "application/json"
67
  }
68
 
69
- # Hugging Face Client (No token needed in Hugging Face Spaces)
70
  class HuggingFaceClient:
71
  def __init__(self):
72
  self.api_url = "https://api-inference.huggingface.co/models/"
@@ -79,26 +80,20 @@ class HuggingFaceClient:
79
  try:
80
  response = requests.post(f"{self.api_url}{model}", headers=self.headers, json=payload, timeout=10)
81
  response.raise_for_status()
82
- logging.info(f"API request successful for model {model}")
83
  return response.json()
84
  except requests.exceptions.RequestException as e:
85
  logging.error(f"API request failed for model {model}: {str(e)}")
86
  return None
87
 
88
- def analyze_response(self, response_text, language_code="en"):
89
- try:
90
- sentiment_result = self.query(response_text, self.sentiment_model)
91
- severity_result = self.query(response_text, self.severity_model)
92
- if not sentiment_result or not severity_result:
93
- return None
94
- sentiment_score = self.process_sentiment(sentiment_result)
95
- severity_score = self.process_severity(severity_result)
96
- risk_level = self.determine_risk_level(sentiment_score, severity_score)
97
- logging.info(f"Analysis: Sentiment={sentiment_score}, Severity={severity_score}, Risk={risk_level}")
98
- return {"sentiment_score": sentiment_score, "severity_score": severity_score, "risk_level": risk_level}
99
- except Exception as e:
100
- logging.error(f"Error analyzing response: {str(e)}")
101
  return None
 
 
 
 
102
 
103
  def process_sentiment(self, result):
104
  if result and isinstance(result, list) and len(result) > 0:
@@ -144,6 +139,19 @@ class SalesforceClient:
144
  logging.error(error_message)
145
  return error_message
146
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  def query_records(self, query):
148
  try:
149
  encoded_query = quote(query)
@@ -157,7 +165,7 @@ class SalesforceClient:
157
  logging.error(f"Failed to query records: {str(e)}")
158
  return []
159
 
160
- # Twilio Client with WhatsApp Support
161
  class TwilioClient:
162
  def __init__(self, account_sid, auth_token, from_number):
163
  self.api_url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json"
@@ -198,133 +206,114 @@ def get_patient_info():
198
  return None, None
199
 
200
  patient_id, patient_name = get_patient_info()
201
- if not patient_id or not patient_name:
202
- patient_id = gr.State(value=None)
203
- patient_name = gr.State(value=None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
  # Automated Follow-up Function
206
- def send_automated_followup(patient_id, action_type, patient_phone=None):
207
- try:
208
- if not patient_id:
209
- return "Patient ID not found. Please log in via Salesforce."
210
- patients = sf_client.query_records(f"SELECT Phone__c, Name__c FROM Patient__c WHERE Id = '{patient_id}'")
211
- if not patients:
212
- return "Patient not found."
213
- patient_phone = patient_phone or patients[0]["Phone__c"]
214
- consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patient_id}' ORDER BY CreatedDate DESC LIMIT 1")
215
- consent_method = consent[0]["Method__c"] if consent else "SMS"
216
- message_templates = {
217
- "consent": f"Hello {patients[0]['Name__c']}, your consent has been recorded. Welcome to our clinic!",
218
- "survey": f"Hi {patients[0]['Name__c']}, thank you for your feedback. We’ll follow up soon.",
219
- "appointment": f"Dear {patients[0]['Name__c']}, your appointment is confirmed. A reminder will be sent closer to the date."
220
- }
221
- message = message_templates.get(action_type, f"Hi {patients[0]['Name__c']}, an action ({action_type}) was performed. Contact us for details.")
222
- send_result = twilio_client.send_message(patient_phone, message, method=consent_method.lower())
223
- if send_result is True:
224
- sf_client.create_record("MessageLog__c", {
225
- "Patient__c": patient_id, "MessageText__c": message,
226
- "Direction__c": "Outbound", "Timestamp__c": datetime.utcnow().isoformat()
227
- })
228
- logging.info(f"Automated follow-up sent for {action_type}: {patient_id}")
229
- return f"Follow-up message sent for {action_type}!"
230
- return f"Failed to send follow-up: {send_result}"
231
- except Exception as e:
232
- logging.error(f"Error in automated follow-up: {str(e)}")
233
- return f"Error sending follow-up: {str(e)}. Please try again."
234
 
235
- # Gradio Interface Functions
236
- def submit_consent(patient_id, consent_method):
237
- if not patient_id or not consent_method:
238
- return "Please provide a consent method."
239
- try:
240
- consent_data = {"Patient__c": patient_id, "Method__c": consent_method, "GivenOn__c": date.today().isoformat()}
241
- result = sf_client.create_record("Consent__c", consent_data)
242
- if isinstance(result, str):
243
- return result
 
 
 
244
  if result:
245
- followup_msg = send_automated_followup(patient_id, "consent")
246
- return f"Consent recorded successfully! {followup_msg}"
247
- return "Failed to record consent. Please try again."
248
- except Exception as e:
249
- logging.error(f"Error submitting consent: {str(e)}")
250
- return f"Error: {str(e)}. Please try again."
251
 
252
- def schedule_appointment(patient_id, doctor_name, appointment_date, time_slot):
253
- if not patient_id or not doctor_name or not appointment_date or not time_slot:
254
- return "All fields are required for appointment scheduling."
255
- try:
256
- appointment_data = {
257
- "Patient__c": patient_id,
258
- "DoctorName__c": doctor_name,
259
- "DateTime__c": f"{appointment_date} {time_slot}:00",
260
- "Status__c": "Scheduled"
261
- }
262
- result = sf_client.create_record("Appointment__c", appointment_data)
263
- if isinstance(result, str):
264
- return result
265
- if result:
266
- followup_msg = send_automated_followup(patient_id, "appointment")
267
- return f"Appointment scheduled with Dr. {doctor_name} on {appointment_date} at {time_slot}:00! {followup_msg}"
268
- return "Failed to schedule appointment. Please try again."
269
- except Exception as e:
270
- logging.error(f"Error scheduling appointment: {str(e)}")
271
- return f"Error: {str(e)}. Please try again."
272
-
273
- def submit_survey(patient_id, question, answer):
274
- if not patient_id or not answer:
275
- return "Please provide an answer to the selected question."
276
- try:
277
- survey_data = {"Patient__c": patient_id, "Question__c": question, "Answer__c": answer}
278
- result = sf_client.create_record("Survey__c", survey_data)
279
- if isinstance(result, str):
280
- return result
281
- if result:
282
- analysis = hf_client.analyze_response(answer)
283
- if analysis:
284
- symptom_data = {
285
- "Patient__c": patient_id, "ResponseText__c": answer,
286
- "RiskScore__c": analysis["risk_level"], "Severity__c": analysis["severity_score"],
287
- "Sentiment__c": analysis["sentiment_score"]
288
- }
289
- symptom_result = sf_client.create_record("SymptomLog__c", symptom_data)
290
- if isinstance(symptom_result, str):
291
- return symptom_result
292
- if symptom_result and analysis["risk_level"] == "High":
293
- case_data = {
294
- "RelatedPatient__c": patient_id, "Priority__c": "High",
295
- "Description__c": f"High-risk survey response: {answer}"
296
- }
297
- sf_client.create_record("Case__c", case_data)
298
- followup_msg = send_automated_followup(patient_id, "survey")
299
- return f"Survey submitted! Risk Level: {analysis['risk_level']}. {followup_msg}"
300
- return "Survey submitted, but analysis failed. Please contact support."
301
- return "Failed to submit survey. Please try again."
302
- except Exception as e:
303
- logging.error(f"Error submitting survey: {str(e)}")
304
- return f"Error: {str(e)}. Please try again."
305
 
 
306
  def view_risk_dashboard(patient_id):
307
- try:
308
- query = f"SELECT Patient__c, ResponseText__c, RiskScore__c, Severity__c, Sentiment__c FROM SymptomLog__c WHERE Patient__c = '{patient_id}'"
309
- symptom_logs = sf_client.query_records(query)
310
- if symptom_logs:
311
- df = pd.DataFrame([
312
- {"Patient": log["Patient__c"], "Response": log["ResponseText__c"],
313
- "Risk Score": log["RiskScore__c"], "Severity": log["Severity__c"],
314
- "Sentiment": log["Sentiment__c"]} for log in symptom_logs
315
- ])
316
- high_risk = df[df["Severity"] == "High"]
317
- high_risk_info = high_risk.to_dict('records') if not high_risk.empty else "No high-risk cases for this patient."
318
- return df, high_risk_info
319
- return pd.DataFrame(), "No symptom logs available for this patient."
320
- except Exception as e:
321
- logging.error(f"Error loading risk dashboard: {str(e)}")
322
- return pd.DataFrame(), f"Error: {str(e)}. Please try again."
323
 
 
324
  def escalate_case(patient_id, response_text):
325
  if not patient_id or not response_text:
326
- return "Please provide a response text to escalate the case."
327
- try:
 
328
  case_data = {
329
  "RelatedPatient__c": patient_id, "Priority__c": "High",
330
  "Description__c": f"High-risk response: {response_text}"
@@ -332,89 +321,121 @@ def escalate_case(patient_id, response_text):
332
  result = sf_client.create_record("Case__c", case_data)
333
  if isinstance(result, str):
334
  return result
335
- if result:
336
- patients = sf_client.query_records(f"SELECT Phone__c FROM Patient__c WHERE Id = '{patient_id}'")
337
- if patients:
338
- consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patient_id}' ORDER BY CreatedDate DESC LIMIT 1")
339
- consent_method = consent[0]["Method__c"] if consent else "SMS"
340
- message = "Your case has been escalated due to a high-risk response. A doctor will contact you soon."
341
- twilio_client.send_message(patients[0]["Phone__c"], message, method=consent_method.lower())
342
- return "Case escalated successfully! The patient will be contacted soon."
343
- return "Failed to escalate case. Please try again."
344
- except Exception as e:
345
- logging.error(f"Error escalating case: {str(e)}")
346
- return f"Error: {str(e)}. Please try again."
347
 
348
  # Gradio Interface
349
- with gr.Blocks(theme=gr.themes.Soft(), title="Clinic Patient Portal") as demo:
350
- gr.Markdown(f"# Clinic Patient Portal - Welcome, {patient_name.value if patient_name.value else 'Guest'}!")
351
-
352
  with gr.Tabs():
353
- with gr.Tab("Consent Management"):
354
- gr.Markdown("**Update Your Consent Method**")
355
- consent_method_input = gr.Dropdown(["SMS", "WhatsApp"], label="Consent Method", value="SMS")
356
- consent_button = gr.Button("Submit Consent")
357
- consent_output = gr.Textbox(label="Result")
358
- consent_button.click(
359
- fn=submit_consent,
360
- inputs=[patient_id, consent_method_input],
361
- outputs=consent_output
362
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
 
364
- with gr.Tab("Appointment Scheduling"):
365
- gr.Markdown("**Book an Appointment**")
366
- doctors = ["Dr. Smith", "Dr. Jones", "Dr. Patel"] # Mock doctor list
367
- doctor_input = gr.Dropdown(doctors, label="Select Doctor", value=doctors[0])
368
- date_input = gr.Textbox(label="Select Date (YYYY-MM-DD)", value=date.today().isoformat(), placeholder="YYYY-MM-DD")
369
- time_slots = ["09:00", "10:00", "11:00", "14:00", "15:00", "16:00"] # Mock slots
370
- time_input = gr.Dropdown(time_slots, label="Select Time Slot", value=time_slots[0])
371
- appt_button = gr.Button("Schedule Appointment")
372
- appt_output = gr.Textbox(label="Result")
373
- appt_button.click(
374
- fn=schedule_appointment,
375
- inputs=[patient_id, doctor_input, date_input, time_input],
376
- outputs=appt_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  )
378
 
379
- with gr.Tab("Patient Survey"):
380
- gr.Markdown("**Submit Your Feedback**")
381
- survey_questions = [
382
- "How would you rate your visit today?",
383
- "Are you experiencing any discomfort?",
384
- "How satisfied are you with our staff?"
385
- ]
386
  survey_question_input = gr.Dropdown(survey_questions, label="Select Question", value=survey_questions[0])
387
  survey_answer_input = gr.Textbox(label="Your Answer", lines=4)
388
  survey_button = gr.Button("Submit Survey")
389
  survey_output = gr.Textbox(label="Result")
390
  survey_button.click(
391
- fn=submit_survey,
392
- inputs=[patient_id, survey_question_input, survey_answer_input],
393
  outputs=survey_output
394
  )
395
-
396
- with gr.Tab("Risk Dashboard"):
397
- gr.Markdown("**View Your Health Risk Insights**")
398
- dashboard_button = gr.Button("Load Dashboard")
399
- dashboard_table = gr.Dataframe(label="Symptom Logs")
400
- high_risk_output = gr.Textbox(label="High-Risk Cases")
401
- dashboard_button.click(
402
- fn=view_risk_dashboard,
403
- inputs=[patient_id],
404
- outputs=[dashboard_table, high_risk_output]
405
- )
406
-
407
- with gr.Tab("Case Escalation"):
408
- gr.Markdown("**Escalate a High-Risk Case**")
409
- escalate_response_input = gr.Textbox(label="Response Text", lines=4)
410
- escalate_button = gr.Button("Escalate Case")
411
- escalate_output = gr.Textbox(label="Result")
412
- escalate_button.click(
413
- fn=escalate_case,
414
- inputs=[patient_id, escalate_response_input],
415
- outputs=escalate_output
416
  )
417
 
418
- # Launch Gradio app
419
  if __name__ == "__main__":
 
 
 
420
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
7
  import pandas as pd
8
  import os
9
  from urllib.parse import quote, unquote
10
+ import time
11
 
12
  # Configure logging for audit purposes (FR4, NFR: Security)
13
  logging.basicConfig(level=logging.INFO, filename='app_log.txt',
 
67
  "Content-Type": "application/json"
68
  }
69
 
70
+ # Hugging Face Client for Risk Analysis
71
  class HuggingFaceClient:
72
  def __init__(self):
73
  self.api_url = "https://api-inference.huggingface.co/models/"
 
80
  try:
81
  response = requests.post(f"{self.api_url}{model}", headers=self.headers, json=payload, timeout=10)
82
  response.raise_for_status()
 
83
  return response.json()
84
  except requests.exceptions.RequestException as e:
85
  logging.error(f"API request failed for model {model}: {str(e)}")
86
  return None
87
 
88
+ def analyze_response(self, response_text):
89
+ sentiment_result = self.query(response_text, self.sentiment_model)
90
+ severity_result = self.query(response_text, self.severity_model)
91
+ if not sentiment_result or not severity_result:
 
 
 
 
 
 
 
 
 
92
  return None
93
+ sentiment_score = self.process_sentiment(sentiment_result)
94
+ severity_score = self.process_severity(severity_result)
95
+ risk_level = self.determine_risk_level(sentiment_score, severity_score)
96
+ return {"sentiment_score": sentiment_score, "severity_score": severity_score, "risk_level": risk_level}
97
 
98
  def process_sentiment(self, result):
99
  if result and isinstance(result, list) and len(result) > 0:
 
139
  logging.error(error_message)
140
  return error_message
141
 
142
+ def update_record(self, object_name, record_id, data):
143
+ try:
144
+ response = requests.patch(f"{self.base_url}sobjects/{object_name}/{record_id}", headers=self.headers, json=data)
145
+ response.raise_for_status()
146
+ logging.info(f"Updated {object_name} record: {record_id}")
147
+ return True
148
+ except requests.exceptions.HTTPError as e:
149
+ logging.error(f"HTTP error updating {object_name} record: {e.response.status_code} - {e.response.text}")
150
+ return False
151
+ except requests.exceptions.RequestException as e:
152
+ logging.error(f"Failed to update {object_name} record: {str(e)}")
153
+ return False
154
+
155
  def query_records(self, query):
156
  try:
157
  encoded_query = quote(query)
 
165
  logging.error(f"Failed to query records: {str(e)}")
166
  return []
167
 
168
+ # Twilio Client
169
  class TwilioClient:
170
  def __init__(self, account_sid, auth_token, from_number):
171
  self.api_url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json"
 
206
  return None, None
207
 
208
  patient_id, patient_name = get_patient_info()
209
+ patient_id_state = gr.State(value=patient_id)
210
+ patient_name_state = gr.State(value=patient_name)
211
+
212
+ # Follow-Up Scheduler (Simulated Cron Job)
213
+ def follow_up_scheduler():
214
+ while True:
215
+ current_date = date.today().isoformat()
216
+ plans = sf_client.query_records(f"SELECT Patient__c, MessageContent__c FROM FollowUpPlan__c WHERE ScheduledDate__c = {current_date} AND Status__c = 'Scheduled'")
217
+ for plan in plans:
218
+ patient = sf_client.query_records(f"SELECT Phone__c, ConsentGiven__c FROM Patient__c WHERE Id = '{plan['Patient__c']}'")[0]
219
+ if patient["ConsentGiven__c"]:
220
+ message = plan["MessageContent__c"] or f"Hi {patient_name_state.value}, this is your daily follow-up check-in."
221
+ twilio_client.send_message(patient["Phone__c"], message)
222
+ sf_client.create_record("MessageLog__c", {
223
+ "Patient__c": plan["Patient__c"], "MessageText__c": message,
224
+ "Direction__c": "Outbound", "Timestamp__c": datetime.utcnow().isoformat()
225
+ })
226
+ sf_client.update_record("FollowUpPlan__c", plan["Id"], {"Status__c": "Sent"})
227
+ time.sleep(86400) # Run daily
228
 
229
  # Automated Follow-up Function
230
+ def send_automated_followup(patient_id, action_type):
231
+ if not patient_id:
232
+ return "Patient ID not found. Please log in via Salesforce."
233
+ patients = sf_client.query_records(f"SELECT Phone__c, Name, ConsentGiven__c FROM Patient__c WHERE Id = '{patient_id}'")
234
+ if not patients:
235
+ return "Patient not found."
236
+ patient = patients[0]
237
+ if not patient["ConsentGiven__c"]:
238
+ return "Consent not given. Please update your consent."
239
+ consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patient_id}' ORDER BY CreatedDate DESC LIMIT 1")
240
+ consent_method = consent[0]["Method__c"] if consent else "SMS"
241
+ message_templates = {
242
+ "consent": f"Hello {patient['Name']}, thank you for registering with our clinic!",
243
+ "daily": f"Hi {patient['Name']}, this is your daily follow-up. How are you feeling today?",
244
+ "appointment": f"Dear {patient['Name']}, your appointment is confirmed. Details will follow."
245
+ }
246
+ message = message_templates.get(action_type, f"Hi {patient['Name']}, an action ({action_type}) was performed.")
247
+ send_result = twilio_client.send_message(patient["Phone__c"], message, method=consent_method.lower())
248
+ if send_result is True:
249
+ sf_client.create_record("MessageLog__c", {
250
+ "Patient__c": patient_id, "MessageText__c": message,
251
+ "Direction__c": "Outbound", "Timestamp__c": datetime.utcnow().isoformat()
252
+ })
253
+ sf_client.update_record("Patient__c", patient_id, {"FollowUpMessage__c": message})
254
+ logging.info(f"Automated follow-up sent for {action_type}: {patient_id}")
255
+ return f"Follow-up message sent for {action_type}!"
256
+ return f"Failed to send follow-up: {send_result}"
 
257
 
258
+ # Update Patient Profile
259
+ def update_patient_profile(patient_id, height, weight, emergency_name, emergency_number, emergency_relationship):
260
+ if not patient_id:
261
+ return "Patient ID not found."
262
+ update_data = {}
263
+ if height: update_data["Height__c"] = float(height)
264
+ if weight: update_data["Weight__c"] = float(weight)
265
+ if emergency_name: update_data["EmergencyContactName__c"] = emergency_name
266
+ if emergency_number: update_data["EmergencyContactNumber__c"] = emergency_number
267
+ if emergency_relationship: update_data["EmergencyContactRelationship__c"] = emergency_relationship
268
+ if update_data:
269
+ result = sf_client.update_record("Patient__c", patient_id, update_data)
270
  if result:
271
+ return "Profile updated successfully!"
272
+ return "Failed to update profile."
273
+ return "No changes made."
 
 
 
274
 
275
+ # Schedule Appointment
276
+ def schedule_appointment(patient_id, doctor_name, appointment_date, time_slot, reason, special_requests):
277
+ if not all([patient_id, doctor_name, appointment_date, time_slot]):
278
+ return "All fields are required."
279
+ doctors = sf_client.query_records("SELECT Name FROM Doctor__c")
280
+ if doctor_name not in [doc["Name"] for doc in doctors]:
281
+ return "Invalid doctor selected."
282
+ appointment_data = {
283
+ "Patient__c": patient_id,
284
+ "Name": f"Appointment with {doctor_name}",
285
+ "DateTime__c": f"{appointment_date} {time_slot}:00",
286
+ "Status__c": "Scheduled",
287
+ "Email__c": sf_client.query_records(f"SELECT Email__c FROM Patient__c WHERE Id = '{patient_id}'")[0]["Email__c"],
288
+ }
289
+ if reason: appointment_data["Reason__c"] = reason
290
+ if special_requests: appointment_data["SpecialRequests__c"] = special_requests
291
+ result = sf_client.create_record("Appointment__c", appointment_data)
292
+ if isinstance(result, str):
293
+ return result
294
+ send_automated_followup(patient_id, "appointment")
295
+ return f"Appointment scheduled with {doctor_name} on {appointment_date} at {time_slot}:00!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
 
297
+ # View Risk Dashboard
298
  def view_risk_dashboard(patient_id):
299
+ query = f"SELECT ResponseText__c, RiskScore__c, Severity__c, Sentiment__c FROM SymptomLog__c WHERE Patient__c = '{patient_id}'"
300
+ symptom_logs = sf_client.query_records(query)
301
+ if symptom_logs:
302
+ df = pd.DataFrame([
303
+ {"Response": log["ResponseText__c"], "Risk Score": log["RiskScore__c"],
304
+ "Severity": log["Severity__c"], "Sentiment": log["Sentiment__c"]}
305
+ for log in symptom_logs
306
+ ])
307
+ high_risk = df[df["Severity"] == "High"].to_dict('records')
308
+ return df, high_risk if high_risk else "No high-risk cases."
309
+ return pd.DataFrame(), "No symptom logs available."
 
 
 
 
 
310
 
311
+ # Escalate Case
312
  def escalate_case(patient_id, response_text):
313
  if not patient_id or not response_text:
314
+ return "Please provide a response text."
315
+ analysis = hf_client.analyze_response(response_text)
316
+ if analysis and analysis["risk_level"] == "High":
317
  case_data = {
318
  "RelatedPatient__c": patient_id, "Priority__c": "High",
319
  "Description__c": f"High-risk response: {response_text}"
 
321
  result = sf_client.create_record("Case__c", case_data)
322
  if isinstance(result, str):
323
  return result
324
+ patient = sf_client.query_records(f"SELECT Phone__c FROM Patient__c WHERE Id = '{patient_id}'")[0]
325
+ twilio_client.send_message(patient["Phone__c"], "Your case has been escalated. A doctor will contact you soon.")
326
+ return "Case escalated successfully!"
327
+ return "No high-risk case detected."
 
 
 
 
 
 
 
 
328
 
329
  # Gradio Interface
330
+ with gr.Blocks(theme=gr.themes.Soft(), title="HealthPortal - Patient Care System") as demo:
331
+ gr.Markdown(f"# HealthPortal - Patient Care System")
 
332
  with gr.Tabs():
333
+ with gr.Tab("Dashboard"):
334
+ gr.Markdown(f"## Welcome back, {patient_name_state.value or 'Guest'}!")
335
+ gr.Markdown("Here's an overview of your healthcare journey")
336
+ with gr.Row():
337
+ with gr.Column():
338
+ next_appt = sf_client.query_records(f"SELECT DateTime__c, Name FROM Appointment__c WHERE Patient__c = '{patient_id_state.value}' AND Status__c = 'Scheduled' ORDER BY DateTime__c ASC LIMIT 1")
339
+ gr.Markdown(f"**Next Appointment**\n{next_appt[0]['DateTime__c'].split(' ')[0]} - {next_appt[0]['Name'].split('with ')[1]}") if next_appt else gr.Markdown("**Next Appointment**\nNo upcoming appointments")
340
+ with gr.Column():
341
+ pending_fields = [f for f in ["Height__c", "Weight__c", "EmergencyContactName__c"] if not sf_client.query_records(f"SELECT {f} FROM Patient__c WHERE Id = '{patient_id_state.value}'")[0].get(f)]
342
+ gr.Markdown(f"**Pending Forms**\n{len(pending_fields)} - {', '.join(pending_fields)}") if pending_fields else gr.Markdown("**Pending Forms**\nNone")
343
+ with gr.Column():
344
+ health_score = sf_client.query_records(f"SELECT AVG(RiskScore__c) FROM SymptomLog__c WHERE Patient__c = '{patient_id_state.value}'")[0]["expr0"] or 0
345
+ gr.Markdown(f"**Health Score**\n{int(health_score)}/100\n{'Excellent progress' if health_score >= 80 else 'Monitor progress'}")
346
+ with gr.Column():
347
+ notifications = sf_client.query_records(f"SELECT MessageText__c FROM MessageLog__c WHERE Patient__c = '{patient_id_state.value}' ORDER BY Timestamp__c DESC LIMIT 3")
348
+ gr.Markdown(f"**Notifications**\n{len(notifications)} - {'Lab results available' if notifications else 'No new notifications'}")
349
+ with gr.Row():
350
+ with gr.Column():
351
+ gr.Markdown("**Tasks & Reminders**")
352
+ tasks = [
353
+ gr.Markdown("Complete Pre-visit Form\nFill out questionnaire before your appointment\nDue: Dec 14") if pending_fields else gr.Markdown("Complete Pre-visit Form\nAll fields completed"),
354
+ gr.Markdown("Review Lab Results\nCheck your latest blood work results\nAvailable now"),
355
+ gr.Markdown("Schedule Follow-up\nRecommended: Jan 2025")
356
+ ]
357
+ with gr.Column():
358
+ gr.Markdown("**Recent Activity**")
359
+ activities = [
360
+ gr.Markdown("Lab Results Available\nBlood work results from Dec 10 are now available"),
361
+ gr.Markdown("Appointment Confirmed\nYour Dec 15 appointment with Dr. Smith is confirmed\n1 day ago"),
362
+ gr.Markdown("Follow-up Reminder\nTime for your weekly blood pressure check\n5 days ago")
363
+ ]
364
 
365
+ with gr.Tab("Appointments"):
366
+ gr.Markdown("## Appointments\nManage your healthcare appointments and schedule new visits")
367
+ with gr.Row():
368
+ with gr.Column():
369
+ upcoming_appts = sf_client.query_records(f"SELECT DateTime__c, Name FROM Appointment__c WHERE Patient__c = '{patient_id_state.value}' AND Status__c = 'Scheduled'")
370
+ for appt in upcoming_appts[:2]:
371
+ gr.Markdown(f"{appt['Name']}\n{appt['DateTime__c']}\nReschedule | Cancel | Call Office")
372
+ with gr.Column():
373
+ gr.Markdown("**Quick Actions**")
374
+ gr.Button("View Available Times")
375
+ gr.Button("Call to Schedule")
376
+ gr.Button("Request Urgent Care")
377
+ with gr.Row():
378
+ with gr.Column():
379
+ gr.Markdown("**Schedule New Appointment**")
380
+ doctors = [doc["Name"] for doc in sf_client.query_records("SELECT Name FROM Doctor__c")]
381
+ doctor_input = gr.Dropdown(doctors or ["None"], label="Select Provider", value=doctors[0] if doctors else "None")
382
+ date_input = gr.Textbox(label="Preferred Date", value=date.today().isoformat(), interactive=True)
383
+ time_slots = ["09:00", "10:00", "11:00", "14:00", "15:00", "16:00"] # Mock, to be dynamic
384
+ time_input = gr.Dropdown(time_slots, label="Available Time Slots", value=time_slots[0])
385
+ reason_input = gr.Textbox(label="Reason for Visit", value="Please describe the reason for your appointment...")
386
+ special_input = gr.Textbox(label="Special Instructions or Requests", value="Any special accommodations or preferences...")
387
+ appt_button = gr.Button("Schedule Appointment")
388
+ appt_output = gr.Textbox(label="Result")
389
+ appt_button.click(
390
+ fn=schedule_appointment,
391
+ inputs=[patient_id_state, doctor_input, date_input, time_input, reason_input, special_input],
392
+ outputs=appt_output
393
+ )
394
+
395
+ with gr.Tab("Health Records"):
396
+ gr.Markdown("## Health Records")
397
+ patient_data = sf_client.query_records(f"SELECT Name, Height__c, Weight__c, EmergencyContactName__c, EmergencyContactNumber__c, EmergencyContactRelationship__c FROM Patient__c WHERE Id = '{patient_id_state.value}'")[0]
398
+ with gr.Row():
399
+ gr.Markdown(f"**Patient Info: {patient_data['Name']}**")
400
+ height_input = gr.Number(label="Height", value=patient_data["Height__c"] or 0, interactive=True)
401
+ weight_input = gr.Number(label="Weight", value=patient_data["Weight__c"] or 0, interactive=True)
402
+ emergency_name_input = gr.Textbox(label="Emergency Contact Name", value=patient_data["EmergencyContactName__c"] or "")
403
+ emergency_number_input = gr.Textbox(label="Emergency Contact Number", value=patient_data["EmergencyContactNumber__c"] or "")
404
+ emergency_rel_input = gr.Textbox(label="Emergency Contact Relationship", value=patient_data["EmergencyContactRelationship__c"] or "")
405
+ update_button = gr.Button("Update Profile")
406
+ update_output = gr.Textbox(label="Result")
407
+ update_button.click(
408
+ fn=update_patient_profile,
409
+ inputs=[patient_id_state, height_input, weight_input, emergency_name_input, emergency_number_input, emergency_rel_input],
410
+ outputs=update_output
411
  )
412
 
413
+ with gr.Tab("Follow-up"):
414
+ gr.Markdown("## Follow-up")
415
+ gr.Markdown("Submit your feedback or escalate a case")
416
+ survey_questions = ["How would you rate your visit?", "Are you experiencing discomfort?", "How satisfied are you with staff?"]
 
 
 
417
  survey_question_input = gr.Dropdown(survey_questions, label="Select Question", value=survey_questions[0])
418
  survey_answer_input = gr.Textbox(label="Your Answer", lines=4)
419
  survey_button = gr.Button("Submit Survey")
420
  survey_output = gr.Textbox(label="Result")
421
  survey_button.click(
422
+ fn=lambda pid, q, a: submit_survey(pid, q, a) or send_automated_followup(pid, "daily"),
423
+ inputs=[patient_id_state, survey_question_input, survey_answer_input],
424
  outputs=survey_output
425
  )
426
+ with gr.Row():
427
+ escalate_response_input = gr.Textbox(label="Response for Escalation", lines=4)
428
+ escalate_button = gr.Button("Escalate Case")
429
+ escalate_output = gr.Textbox(label="Result")
430
+ escalate_button.click(
431
+ fn=escalate_case,
432
+ inputs=[patient_id_state, escalate_response_input],
433
+ outputs=escalate_output
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  )
435
 
436
+ # Launch Gradio app with scheduler
437
  if __name__ == "__main__":
438
+ import threading
439
+ scheduler_thread = threading.Thread(target=follow_up_scheduler, daemon=True)
440
+ scheduler_thread.start()
441
  demo.launch(server_name="0.0.0.0", server_port=7860)