varshakolanu commited on
Commit
cde7bd3
·
verified ·
1 Parent(s): 3d53add

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +192 -229
app.py CHANGED
@@ -6,7 +6,7 @@ from uuid import uuid4
6
  import logging
7
  import pandas as pd
8
  import os
9
- from urllib.parse import quote
10
 
11
  # Configure logging for audit purposes (FR4, NFR: Security)
12
  logging.basicConfig(level=logging.INFO, filename='app_log.txt',
@@ -67,13 +67,13 @@ SALESFORCE_HEADERS = {
67
  "Content-Type": "application/json"
68
  }
69
 
70
- # Hugging Face Client (No token needed in Hugging Face Spaces)
71
  class HuggingFaceClient:
72
  def __init__(self):
73
  self.api_url = "https://api-inference.huggingface.co/models/"
74
- self.headers = {} # Token is implicit in Hugging Face Spaces
75
  self.sentiment_model = "distilbert-base-uncased-finetuned-sst-2-english"
76
- self.severity_model = "bert-base-uncased" # Placeholder; use custom model
77
  self.translation_model = "facebook/m2m100_418M"
78
 
79
  def query(self, text, model):
@@ -231,121 +231,100 @@ class CalendlyClient:
231
  logging.error(f"Failed to schedule Calendly event: {str(e)}")
232
  return None
233
 
234
- # Gradio Interface Functions
235
  hf_client = HuggingFaceClient()
236
  sf_client = SalesforceClient(SALESFORCE_BASE_URL, SALESFORCE_HEADERS)
237
  twilio_client = TwilioClient(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_PHONE)
238
  calendly_client = CalendlyClient(CALENDLY_API_TOKEN)
239
 
240
- # State to store patient phone number
241
- patient_phone_state = gr.State(value=None)
242
-
243
- def register_patient(name_first, name_last, dob, Gender, height, weight, marital, phone, email,
244
- address_city, address_state, postal,
245
- med_yes_no, med_list, emergency_first, emergency_last, emergency_relation,
246
- emergency_number, language, consent_status):
247
- if not all([name_first, name_last, dob, Gender, height, weight, marital, phone, email,
248
- address_city, address_state, postal, language, consent_status]) or \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  (med_yes_no == "Yes" and not med_list):
250
- return "All fields are required.", gr.State(value=None)
251
  try:
252
  patient_data = {
253
  "First_Name__c": name_first,
254
  "Last_Name__c": name_last,
255
- "Date_of_Birth__c": dob,
256
- "Gender__c": Gender,
257
- "Height__c": height,
258
- "Weight__c": weight,
259
- "Marital_Status__c": marital,
260
  "Phone__c": phone,
261
  "Email__c": email,
262
  "City__c": address_city,
263
  "State__c": address_state,
264
- "Postal_Code__c": postal,
265
- "Medications__c": med_list if med_yes_no == "Yes" else "",
266
- "Emergency_First_Name__c": emergency_first or "",
267
- "Emergency_Last_Name__c": emergency_last or "",
268
- "Emergency_Relation__c": emergency_relation or "",
269
- "Emergency_Number__c": emergency_number or "",
270
  "Language__c": language,
271
- "ConsentGiven__c": consent_status
272
  }
273
  result = sf_client.create_record("Patient__c", patient_data)
274
- if isinstance(result, str): # Error message returned
275
- return result, gr.State(value=None)
276
  if result:
277
  logging.info(f"Registered patient: {result}")
278
  message = f"Welcome! You are registered. Your patient ID is {result}."
279
- if consent_status in ["Approved", "Awaiting"]:
280
- twilio_client.send_message(phone, message, method=consent_status.lower().replace(" ", "")[:3] if consent_status == "Awaiting" else "sms")
281
- return f"Patient registered successfully! Your patient ID is {result}. You can use this ID for future actions.", gr.State(value=phone)
282
- return "Failed to register patient. Please try again or contact support.", gr.State(value=None)
 
283
  except Exception as e:
284
  logging.error(f"Error registering patient: {str(e)}")
285
- return f"Error registering patient: {str(e)}. Please try again or contact support.", gr.State(value=None)
286
-
287
- def submit_consent(patient_phone, consent_method):
288
- if not patient_phone or not consent_method:
289
- return "All fields are required."
290
- try:
291
- patients = sf_client.query_records(f"SELECT Id, Name FROM Patient__c WHERE Phone__c = '{patient_phone}'")
292
- if patients:
293
- consent_data = {"Method__c": consent_method, "GivenOn__c": date.today().isoformat()}
294
- result = sf_client.create_record("Consent__c", consent_data)
295
- if isinstance(result, str): # Error message returned
296
- return result
297
- if result:
298
- message = f"Your consent method has been updated to {consent_method}."
299
- twilio_client.send_message(patient_phone, message, method=consent_method.lower())
300
- logging.info(f"Consent recorded for patient: {patients[0]['Name']}")
301
- return "Consent recorded successfully! Your consent is now updated."
302
- return "Failed to record consent. Please try again or contact support."
303
- return "Patient not found. Please register the patient first."
304
- except Exception as e:
305
- logging.error(f"Error submitting consent: {str(e)}")
306
- return f"Error submitting consent: {str(e)}. Please try again or contact support."
307
-
308
- def schedule_followup(patient_phone, message_template, follow_up_date):
309
- if not patient_phone or not message_template or not follow_up_date:
310
- return "All fields are required."
311
- try:
312
- follow_up_date = datetime.strptime(follow_up_date, "%Y-%m-%d").date()
313
- patients = sf_client.query_records(f"SELECT Id, ConsentGiven__c, Language__c, Name FROM Patient__c WHERE Phone__c = '{patient_phone}'")
314
- if not patients:
315
- return "Patient not found. Please register the patient first."
316
- if patients[0]["ConsentGiven__c"] != "Approved":
317
- return "Consent not approved for messaging. Please update consent status."
318
- consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patients[0]['Id']}' ORDER BY CreatedDate DESC LIMIT 1")
319
- consent_method = consent[0]["Method__c"] if consent else "SMS"
320
- followup_data = {
321
- "Patient__c": patients[0]["Id"], "FollowUpDate__c": follow_up_date.isoformat(),
322
- "MessageTemplate__c": message_template, "Status__c": "Scheduled"
323
- }
324
- result = sf_client.create_record("FollowUpPlan__c", followup_data)
325
- if isinstance(result, str): # Error message returned
326
- return result
327
- if not result:
328
- return "Failed to schedule follow-up. Please try again or contact support."
329
- send_result = twilio_client.send_message(patient_phone, message_template, method=consent_method.lower())
330
- if send_result is True:
331
- sf_client.create_record("MessageLog__c", {
332
- "Patient__c": patients[0]["Id"], "MessageText__c": message_template,
333
- "Direction__c": "Outbound", "Timestamp__c": datetime.utcnow().isoformat()
334
- })
335
- logging.info(f"Follow-up scheduled and message sent for patient: {patients[0]['Name']}")
336
- return "Follow-up scheduled successfully, and a message has been sent!"
337
- return f"Failed to send message: {send_result}. Please try again or contact support."
338
- except ValueError:
339
- return "Invalid date format. Please use YYYY-MM-DD."
340
- except Exception as e:
341
- logging.error(f"Error scheduling follow-up: {str(e)}")
342
- return f"Error scheduling follow-up: {str(e)}. Please try again or contact support."
343
 
 
344
  def schedule_appointment(patient_email, event_type_slug="30min"):
345
- if not patient_email or not event_type_slug:
346
- return "All fields are required."
347
  try:
348
- patients = sf_client.query_records(f"SELECT Id, Phone__c, ConsentGiven__c, Language__c, Name FROM Patient__c WHERE Email__c = '{patient_email}'")
349
  if not patients:
350
  return "Patient not found. Please register the patient first."
351
  event_type_uuid = calendly_client.get_event_type_uuid(event_type_slug)
@@ -355,14 +334,16 @@ def schedule_appointment(patient_email, event_type_slug="30min"):
355
  if event_time:
356
  appointment_data = {"DateTime__c": event_time, "Status__c": "Scheduled", "Patient__c": patients[0]["Id"]}
357
  result = sf_client.create_record("Appointment__c", appointment_data)
358
- if isinstance(result, str): # Error message returned
359
  return result
360
  if result:
361
- consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patients[0]['Id']}' ORDER BY CreatedDate DESC LIMIT 1")
362
- consent_method = consent[0]["Method__c"] if consent else "SMS"
363
- message = f"Your appointment is scheduled for {event_time}. Please confirm."
 
 
364
  twilio_client.send_message(patients[0]["Phone__c"], message, method=consent_method.lower())
365
- logging.info(f"Appointment scheduled for {patient_email}")
366
  return f"Appointment scheduled successfully for {event_time}!"
367
  return "Failed to create appointment record. Please try again or contact support."
368
  return "Failed to schedule appointment. Please try again or contact support."
@@ -370,64 +351,66 @@ def schedule_appointment(patient_email, event_type_slug="30min"):
370
  logging.error(f"Error scheduling appointment: {str(e)}")
371
  return f"Error scheduling appointment: {str(e)}. Please try again or contact support."
372
 
 
373
  def submit_survey(patient_phone, answer):
374
  if not patient_phone or not answer:
375
  return "Please provide an answer to the selected question."
376
- default_questions = {
377
- "en": ["How would you rate your visit today?", "Are you experiencing any discomfort?", "How satisfied are you with our staff?"],
378
- "te": ["నిన్ను ఈ రోజు ఎలా అనిపించింది?", "మీకు ఏ రకమైన అసౌకర్యం ఉందా?", "మా సిబ్బందిపై మీ సంతృప్తి ఎంత?"],
379
- "hi": ["आज आपकी यात्रा कैसी रही?", "क्या आपको कोई असुविधा हो रही है?", "हमारी टीम से आप कितने संतुष्ट हैं?"]
380
- }
381
  try:
382
  patients = sf_client.query_records(f"SELECT Id, Language__c, Name FROM Patient__c WHERE Phone__c = '{patient_phone}'")
383
  if not patients:
384
  return "Patient not found. Please register the patient first."
385
- language = patients[0]["Language__c"].lower()
386
- question = default_questions.get(language, default_questions["en"])[0] # Default to first question
387
- survey_data = {"Question__c": question, "Answer__c": answer}
388
  result = sf_client.create_record("Survey__c", survey_data)
389
- if isinstance(result, str): # Error message returned
390
  return result
391
- if not result:
392
- return "Failed to submit survey. Please try again or contact support."
393
- analysis = hf_client.analyze_response(answer, language)
394
- if analysis:
395
- symptom_data = {
396
- "Patient__c": patients[0]["Id"], "ResponseText__c": answer,
397
- "RiskScore__c": analysis["risk_level"], "Severity__c": analysis["severity_score"],
398
- "Sentiment__c": analysis["sentiment_score"]
399
- }
400
- result = sf_client.create_record("SymptomLog__c", symptom_data)
401
- if isinstance(result, str): # Error message returned
402
- return result
403
- if result and analysis["risk_level"] == "High":
404
- case_data = {
405
- "RelatedPatient__c": patients[0]["Id"], "Priority__c": "High",
406
- "Description__c": f"High-risk survey response: {answer}"
407
  }
408
- sf_client.create_record("Case__c", case_data)
409
- consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patients[0]['Id']}' ORDER BY CreatedDate DESC LIMIT 1")
410
- consent_method = consent[0]["Method__c"] if consent else "SMS"
411
- message = f"Thank you for your response to '{question}'. Your risk level is {analysis['risk_level']}."
412
- twilio_client.send_message(patient_phone, message, method=consent_method.lower())
413
- logging.info(f"Survey submitted for patient: {patients[0]['Name']}")
414
- return f"Survey submitted successfully! Risk Level: {analysis['risk_level']}."
415
- return "Failed to analyze survey response. Please try again or contact support."
 
 
 
 
 
 
 
 
 
 
 
416
  except Exception as e:
417
  logging.error(f"Error submitting survey: {str(e)}")
418
  return f"Error submitting survey: {str(e)}. Please try again or contact support."
419
 
420
- def view_risk_dashboard():
 
421
  try:
422
- symptom_logs = sf_client.query_records("SELECT Patient__c, ResponseText__c, RiskScore__c, Severity__c, Sentiment__c FROM SymptomLog__c")
 
 
 
423
  if symptom_logs:
424
  df = pd.DataFrame([
425
- {"Patient": log["Patient__c"], "Response": log["ResponseText__c"],
426
  "Risk Score": log["RiskScore__c"], "Severity": log["Severity__c"],
427
  "Sentiment": log["Sentiment__c"]} for log in symptom_logs
428
  ])
429
  high_risk = df[df["Severity"] == "High"]
430
- high_risk_info = high_risk.to_dict('records') if not high_risk.empty else "No high-risk cases."
431
  logging.info("Risk dashboard loaded successfully.")
432
  return df, high_risk_info
433
  logging.info("No symptom logs found.")
@@ -436,6 +419,7 @@ def view_risk_dashboard():
436
  logging.error(f"Error loading risk dashboard: {str(e)}")
437
  return pd.DataFrame(), f"Error loading risk dashboard: {str(e)}. Please try again or contact support."
438
 
 
439
  def escalate_case(patient_id, response_text):
440
  if not patient_id or not response_text:
441
  return "All fields are required."
@@ -445,13 +429,15 @@ def escalate_case(patient_id, response_text):
445
  "Description__c": f"High-risk response: {response_text}"
446
  }
447
  result = sf_client.create_record("Case__c", case_data)
448
- if isinstance(result, str): # Error message returned
449
  return result
450
  if result:
451
  patients = sf_client.query_records(f"SELECT Phone__c FROM Patient__c WHERE Id = '{patient_id}'")
452
  if patients:
453
- consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patient_id}' ORDER BY CreatedDate DESC LIMIT 1")
454
- consent_method = consent[0]["Method__c"] if consent else "SMS"
 
 
455
  message = "Your case has been escalated due to a high-risk response. A doctor will contact you soon."
456
  twilio_client.send_message(patients[0]["Phone__c"], message, method=consent_method.lower())
457
  logging.info(f"Case created for patient: {patient_id}")
@@ -462,133 +448,110 @@ def escalate_case(patient_id, response_text):
462
  return f"Error escalating case: {str(e)}. Please try again or contact support."
463
 
464
  # Gradio Interface
465
- with gr.Blocks(theme=gr.themes.Soft(), title="AI-Powered Patient Follow-up Agent") as demo:
466
- gr.Markdown("# AI-Powered Patient Follow-up Agent for Clinics")
467
-
 
 
 
 
 
 
468
  with gr.Tabs():
469
- with gr.Tab("Patient Registration"):
 
 
 
 
 
 
 
 
 
470
  with gr.Row():
471
  with gr.Column():
472
- name_first = gr.Textbox(label="First Name", placeholder="First Name")
473
- name_last = gr.Textbox(label="Last Name", placeholder="Last Name")
474
  with gr.Column():
475
- dob_input = gr.Textbox(label="Date of Birth", placeholder="MM-DD-YYYY")
476
- Gender_input = gr.Dropdown(label="Gender", choices=["Please Select", "Male", "Female", "Other"], value="Please Select")
477
  with gr.Row():
478
- height_input = gr.Textbox(label="Height (inches)", placeholder="e.g., 65")
479
- weight_input = gr.Textbox(label="Weight (pounds)", placeholder="e.g., 150")
480
- marital_input = gr.Dropdown(label="Marital Status", choices=["Please Select", "Single", "Married", "Divorced", "Widowed"], value="Please Select")
481
  with gr.Row():
482
- phone_input = gr.Textbox(label="Contact Number", placeholder="(000) 000-0000")
483
- email_input = gr.Textbox(label="Email", placeholder="patient@example.com")
484
  with gr.Row():
485
  with gr.Column():
486
- address_city = gr.Textbox(label="City", placeholder="City")
487
- address_state = gr.Textbox(label="State/Province", placeholder="State/Province")
488
- postal_input = gr.Textbox(label="Postal/Zip Code", placeholder="Postal/Zip Code")
489
  with gr.Row():
490
- med_yes_no = gr.Radio(label="Taking any medications, currently?", choices=["Yes", "No"], value="No")
491
- med_list = gr.Textbox(label="Please list it here", placeholder="List medications here", lines=2, visible=False)
492
- with gr.Accordion("In case of emergency", open=False):
493
  with gr.Row():
494
  with gr.Column():
495
- emergency_first = gr.Textbox(label="First Name", placeholder="First Name")
496
- emergency_last = gr.Textbox(label="Last Name", placeholder="Last Name")
497
  with gr.Column():
498
- emergency_relation = gr.Textbox(label="Relationship", placeholder="e.g., Spouse")
499
- emergency_number = gr.Textbox(label="Contact Number", placeholder="(000) 000-0000")
500
- language_input = gr.Dropdown(["English", "Telugu", "Hindi"], label="Language")
501
- consent_input = gr.Dropdown(["SMS", "WhatsApp", "Awaiting"], label="Consent Method")
502
- register_button = gr.Button("Submit")
503
- register_output = gr.Textbox(label="Result")
504
-
505
- # Update visibility of medication list based on radio selection
506
  def toggle_med_list(med_choice):
507
  return gr.update(visible=med_choice == "Yes")
508
 
509
- med_yes_no.change(
510
- fn=toggle_med_list,
511
- inputs=med_yes_no,
512
- outputs=med_list
513
- )
514
-
515
  register_button.click(
516
  fn=register_patient,
517
- inputs=[name_first, name_last, dob_input, Gender_input, height_input, weight_input, marital_input,
518
- phone_input, email_input, address_city, address_state,
519
- postal_input, med_yes_no, med_list, emergency_first, emergency_last, emergency_relation,
520
- emergency_number, language_input, consent_input],
521
- outputs=[register_output, patient_phone_state]
522
  )
523
 
524
- with gr.Tab("Consent Form"):
525
- consent_phone_input = gr.Textbox(label="Patient Phone Number", value=lambda x: x, interactive=False) if patient_phone_state.value else gr.Textbox(label="Patient Phone Number", placeholder="+1234567890")
526
- consent_method_input = gr.Dropdown(["SMS", "WhatsApp"], label="Consent Method")
527
- consent_button = gr.Button("Submit Consent")
528
- consent_output = gr.Textbox(label="Result")
529
- consent_button.click(
530
- fn=submit_consent,
531
- inputs=[consent_phone_input, consent_method_input],
532
- outputs=consent_output
533
- )
534
-
535
- with gr.Tab("Follow-up Messaging"):
536
- followup_phone_input = gr.Textbox(label="Patient Phone Number", value=lambda x: x, interactive=False) if patient_phone_state.value else gr.Textbox(label="Patient Phone Number", placeholder="+1234567890")
537
- message_template_input = gr.Textbox(label="Message Template", value="How are you feeling today?")
538
- followup_date_input = gr.Textbox(label="Follow-up Date (YYYY-MM-DD)", value=date.today().isoformat())
539
- followup_button = gr.Button("Schedule Follow-up")
540
- followup_output = gr.Textbox(label="Result")
541
- followup_button.click(
542
- fn=schedule_followup,
543
- inputs=[followup_phone_input, message_template_input, followup_date_input],
544
- outputs=followup_output
545
- )
546
-
547
- with gr.Tab("Appointment Scheduling"):
548
- appt_email_input = gr.Textbox(label="Patient Email", placeholder="patient@example.com")
549
- appt_event_input = gr.Textbox(label="Event Type Slug", value="30min", placeholder="e.g., 30min")
550
- appt_button = gr.Button("Schedule Appointment")
551
- appt_output = gr.Textbox(label="Result")
552
  appt_button.click(
553
  fn=schedule_appointment,
554
  inputs=[appt_email_input, appt_event_input],
555
  outputs=appt_output
556
  )
557
 
558
- with gr.Tab("Survey"):
559
- survey_phone_input = gr.Textbox(label="Patient Phone Number", value=lambda x: x, interactive=False) if patient_phone_state.value else gr.Textbox(label="Patient Phone Number", placeholder="+1234567890")
560
  survey_question_input = gr.Dropdown(label="Select Question", choices=[
561
  "How would you rate your visit today?",
562
  "Are you experiencing any discomfort?",
563
  "How satisfied are you with our staff?"
564
- ], interactive=True)
565
- survey_answer_input = gr.Textbox(label="Your Answer", lines=4)
566
  survey_button = gr.Button("Submit Survey")
567
- survey_output = gr.Textbox(label="Result")
568
  survey_button.click(
569
  fn=submit_survey,
570
  inputs=[survey_phone_input, survey_answer_input],
571
  outputs=survey_output
572
  )
573
 
574
- with gr.Tab("Risk Dashboard"):
575
- dashboard_button = gr.Button("Load Dashboard")
576
- dashboard_table = gr.Dataframe(label="Symptom Logs")
577
- high_risk_output = gr.Textbox(label="High-Risk Cases")
578
- dashboard_button.click(
579
- fn=view_risk_dashboard,
580
- outputs=[dashboard_table, high_risk_output]
 
 
581
  )
582
- with gr.Row():
583
- escalate_id_input = gr.Textbox(label="Patient ID (for escalation)")
584
- escalate_response_input = gr.Textbox(label="Response Text")
585
- escalate_button = gr.Button("Escalate Case")
586
- escalate_output = gr.Textbox(label="Result")
587
- escalate_button.click(
588
- fn=escalate_case,
589
- inputs=[escalate_id_input, escalate_response_input],
590
- outputs=escalate_output
591
- )
592
 
593
  # Launch Gradio app
594
  if __name__ == "__main__":
 
6
  import logging
7
  import pandas as pd
8
  import os
9
+ from urllib.parse import quote, urlparse, parse_qs
10
 
11
  # Configure logging for audit purposes (FR4, NFR: Security)
12
  logging.basicConfig(level=logging.INFO, filename='app_log.txt',
 
67
  "Content-Type": "application/json"
68
  }
69
 
70
+ # Hugging Face Client
71
  class HuggingFaceClient:
72
  def __init__(self):
73
  self.api_url = "https://api-inference.huggingface.co/models/"
74
+ self.headers = {}
75
  self.sentiment_model = "distilbert-base-uncased-finetuned-sst-2-english"
76
+ self.severity_model = "bert-base-uncased"
77
  self.translation_model = "facebook/m2m100_418M"
78
 
79
  def query(self, text, model):
 
231
  logging.error(f"Failed to schedule Calendly event: {str(e)}")
232
  return None
233
 
234
+ # Initialize Clients
235
  hf_client = HuggingFaceClient()
236
  sf_client = SalesforceClient(SALESFORCE_BASE_URL, SALESFORCE_HEADERS)
237
  twilio_client = TwilioClient(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_PHONE)
238
  calendly_client = CalendlyClient(CALENDLY_API_TOKEN)
239
 
240
+ # State to store patient context from URL
241
+ patient_state = gr.State(value=None)
242
+
243
+ # Get URL parameters
244
+ def get_url_params():
245
+ query = os.getenv("QUERY_STRING", "")
246
+ params = parse_qs(query)
247
+ patient_id = params.get("patientId", [None])[0]
248
+ patient_name = params.get("patientName", [None])[0]
249
+ if patient_id and patient_name:
250
+ return {"patientId": patient_id, "patientName": patient_name}
251
+ return None
252
+
253
+ # Initialize patient state from URL
254
+ initial_patient = get_url_params()
255
+ if initial_patient:
256
+ patient_state.value = initial_patient
257
+
258
+ # Automated Follow-up Message
259
+ def send_automated_followup(patient_id, action, phone=None):
260
+ try:
261
+ patients = sf_client.query_records(f"SELECT Phone__c, ConsentGiven__c, Name FROM Patient__c WHERE Id = '{patient_id}'")
262
+ if not patients or patients[0]["ConsentGiven__c"] != "Approved":
263
+ return
264
+ phone = phone or patients[0]["Phone__c"]
265
+ consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patient_id}' ORDER BY CreatedDate DESC LIMIT 1")
266
+ consent_method = consent[0]["Method__c"] if consent else "SMS"
267
+ messages = {
268
+ "registration": f"Welcome, {patients[0]['Name']}! Your registration is complete. A follow-up is scheduled.",
269
+ "survey": f"Dear {patients[0]['Name']}, thank you for your survey response. We’ll follow up soon.",
270
+ "appointment": f"Hi {patients[0]['Name']}, your appointment is confirmed. Details will follow."
271
+ }
272
+ message = messages.get(action, "A follow-up action has been triggered.")
273
+ twilio_client.send_message(phone, message, method=consent_method.lower())
274
+ logging.info(f"Automated follow-up sent for {action} to {phone}")
275
+ except Exception as e:
276
+ logging.error(f"Error sending automated follow-up: {str(e)}")
277
+
278
+ # Registration Function
279
+ def register_patient(name_first, name_last, dob, gender, height, weight, marital, phone, email,
280
+ address_city, address_state, postal, med_yes_no, med_list,
281
+ emergency_first, emergency_last, emergency_relation, emergency_number, language, consent_status):
282
+ if not all([name_first, name_last, dob, gender, height, weight, marital, phone, email,
283
+ address_city, address_state, postal, language, consent_status]) or \
284
  (med_yes_no == "Yes" and not med_list):
285
+ return "All fields are required.", None
286
  try:
287
  patient_data = {
288
  "First_Name__c": name_first,
289
  "Last_Name__c": name_last,
290
+ "DateOfBirth__c": dob,
291
+ "Gender__c": gender,
292
+ "Height__c": float(height),
293
+ "Weight__c": float(weight),
294
+ "MaritalStatus__c": marital,
295
  "Phone__c": phone,
296
  "Email__c": email,
297
  "City__c": address_city,
298
  "State__c": address_state,
299
+ "PostalCode__c": postal,
300
+ "Medication__c": med_list if med_yes_no == "Yes" else "None",
301
+ "EmergencyContactName__c": f"{emergency_first} {emergency_last}".strip() or "",
302
+ "EmergencyContactRelationship__c": emergency_relation or "",
303
+ "EmergencyContactNumber__c": emergency_number or "",
 
304
  "Language__c": language,
305
+ "ConsentGiven__c": consent_status == "Approved"
306
  }
307
  result = sf_client.create_record("Patient__c", patient_data)
308
+ if isinstance(result, str):
309
+ return result, None
310
  if result:
311
  logging.info(f"Registered patient: {result}")
312
  message = f"Welcome! You are registered. Your patient ID is {result}."
313
+ if consent_status == "Approved":
314
+ twilio_client.send_message(phone, message, method="sms")
315
+ send_automated_followup(result, "registration", phone)
316
+ return f"Patient registered successfully! Your patient ID is {result}.", {"patientId": result, "patientName": f"{name_first} {name_last}"}
317
+ return "Failed to register patient. Please try again or contact support.", None
318
  except Exception as e:
319
  logging.error(f"Error registering patient: {str(e)}")
320
+ return f"Error registering patient: {str(e)}. Please try again or contact support.", None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
 
322
+ # Appointment Scheduling
323
  def schedule_appointment(patient_email, event_type_slug="30min"):
324
+ if not patient_email:
325
+ return "Patient Email is required."
326
  try:
327
+ patients = sf_client.query_records(f"SELECT Id, Phone__c, ConsentGiven__c, Name FROM Patient__c WHERE Email__c = '{patient_email}'")
328
  if not patients:
329
  return "Patient not found. Please register the patient first."
330
  event_type_uuid = calendly_client.get_event_type_uuid(event_type_slug)
 
334
  if event_time:
335
  appointment_data = {"DateTime__c": event_time, "Status__c": "Scheduled", "Patient__c": patients[0]["Id"]}
336
  result = sf_client.create_record("Appointment__c", appointment_data)
337
+ if isinstance(result, str):
338
  return result
339
  if result:
340
+ consent_method = "SMS"
341
+ if patients[0]["ConsentGiven__c"]:
342
+ consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patients[0]['Id']}' ORDER BY CreatedDate DESC LIMIT 1")
343
+ consent_method = consent[0]["Method__c"] if consent else "SMS"
344
+ message = f"Hi {patients[0]['Name']}, your appointment is scheduled for {event_time}. Please confirm."
345
  twilio_client.send_message(patients[0]["Phone__c"], message, method=consent_method.lower())
346
+ send_automated_followup(patients[0]["Id"], "appointment", patients[0]["Phone__c"])
347
  return f"Appointment scheduled successfully for {event_time}!"
348
  return "Failed to create appointment record. Please try again or contact support."
349
  return "Failed to schedule appointment. Please try again or contact support."
 
351
  logging.error(f"Error scheduling appointment: {str(e)}")
352
  return f"Error scheduling appointment: {str(e)}. Please try again or contact support."
353
 
354
+ # Survey Submission
355
  def submit_survey(patient_phone, answer):
356
  if not patient_phone or not answer:
357
  return "Please provide an answer to the selected question."
 
 
 
 
 
358
  try:
359
  patients = sf_client.query_records(f"SELECT Id, Language__c, Name FROM Patient__c WHERE Phone__c = '{patient_phone}'")
360
  if not patients:
361
  return "Patient not found. Please register the patient first."
362
+ language = patients[0]["Language__c"].lower() or "en"
363
+ question = "How would you rate your visit today?" # Default question
364
+ survey_data = {"Question__c": question, "Answer__c": answer, "Patient__c": patients[0]["Id"]}
365
  result = sf_client.create_record("Survey__c", survey_data)
366
+ if isinstance(result, str):
367
  return result
368
+ if result:
369
+ analysis = hf_client.analyze_response(answer, language)
370
+ if analysis:
371
+ symptom_data = {
372
+ "Patient__c": patients[0]["Id"], "ResponseText__c": answer,
373
+ "RiskScore__c": analysis["risk_level"], "Severity__c": analysis["severity_score"],
374
+ "Sentiment__c": analysis["sentiment_score"]
 
 
 
 
 
 
 
 
 
375
  }
376
+ symptom_result = sf_client.create_record("SymptomLog__c", symptom_data)
377
+ if isinstance(symptom_result, str):
378
+ return symptom_result
379
+ if analysis["risk_level"] == "High":
380
+ case_data = {
381
+ "RelatedPatient__c": patients[0]["Id"], "Priority__c": "High",
382
+ "Description__c": f"High-risk survey response: {answer}"
383
+ }
384
+ sf_client.create_record("Case__c", case_data)
385
+ consent_method = "SMS"
386
+ if patients[0]["ConsentGiven__c"]:
387
+ consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patients[0]['Id']}' ORDER BY CreatedDate DESC LIMIT 1")
388
+ consent_method = consent[0]["Method__c"] if consent else "SMS"
389
+ message = f"Thank you, {patients[0]['Name']}, for your response to '{question}'. Your risk level is {analysis['risk_level']}."
390
+ twilio_client.send_message(patient_phone, message, method=consent_method.lower())
391
+ send_automated_followup(patients[0]["Id"], "survey", patient_phone)
392
+ return f"Survey submitted successfully! Risk Level: {analysis['risk_level']}."
393
+ return "Failed to analyze survey response. Please try again or contact support."
394
+ return "Failed to submit survey. Please try again or contact support."
395
  except Exception as e:
396
  logging.error(f"Error submitting survey: {str(e)}")
397
  return f"Error submitting survey: {str(e)}. Please try again or contact support."
398
 
399
+ # Risk Dashboard
400
+ def view_risk_dashboard(patient_id=None):
401
  try:
402
+ query = "SELECT Patient__c, ResponseText__c, RiskScore__c, Severity__c, Sentiment__c FROM SymptomLog__c"
403
+ if patient_id:
404
+ query += f" WHERE Patient__c = '{patient_id}'"
405
+ symptom_logs = sf_client.query_records(query)
406
  if symptom_logs:
407
  df = pd.DataFrame([
408
+ {"Patient ID": log["Patient__c"], "Response": log["ResponseText__c"],
409
  "Risk Score": log["RiskScore__c"], "Severity": log["Severity__c"],
410
  "Sentiment": log["Sentiment__c"]} for log in symptom_logs
411
  ])
412
  high_risk = df[df["Severity"] == "High"]
413
+ high_risk_info = high_risk.to_dict('records') if not high_risk.empty else "No high-risk cases for this patient."
414
  logging.info("Risk dashboard loaded successfully.")
415
  return df, high_risk_info
416
  logging.info("No symptom logs found.")
 
419
  logging.error(f"Error loading risk dashboard: {str(e)}")
420
  return pd.DataFrame(), f"Error loading risk dashboard: {str(e)}. Please try again or contact support."
421
 
422
+ # Escalate Case
423
  def escalate_case(patient_id, response_text):
424
  if not patient_id or not response_text:
425
  return "All fields are required."
 
429
  "Description__c": f"High-risk response: {response_text}"
430
  }
431
  result = sf_client.create_record("Case__c", case_data)
432
+ if isinstance(result, str):
433
  return result
434
  if result:
435
  patients = sf_client.query_records(f"SELECT Phone__c FROM Patient__c WHERE Id = '{patient_id}'")
436
  if patients:
437
+ consent_method = "SMS"
438
+ if patients[0]["ConsentGiven__c"]:
439
+ consent = sf_client.query_records(f"SELECT Method__c FROM Consent__c WHERE Patient__c = '{patient_id}' ORDER BY CreatedDate DESC LIMIT 1")
440
+ consent_method = consent[0]["Method__c"] if consent else "SMS"
441
  message = "Your case has been escalated due to a high-risk response. A doctor will contact you soon."
442
  twilio_client.send_message(patients[0]["Phone__c"], message, method=consent_method.lower())
443
  logging.info(f"Case created for patient: {patient_id}")
 
448
  return f"Error escalating case: {str(e)}. Please try again or contact support."
449
 
450
  # Gradio Interface
451
+ with gr.Blocks(theme=gr.themes.Soft(), title="Clinic AI Assistant") as demo:
452
+ gr.Markdown("# Clinic AI Assistant - Patient Management Dashboard")
453
+ gr.Markdown("Welcome to your personalized clinic dashboard. Log in via Salesforce to access your data.")
454
+
455
+ # Get patient context from URL or state
456
+ patient_context = patient_state.value or {}
457
+ patient_id = patient_context.get("patientId")
458
+ patient_name = patient_context.get("patientName")
459
+
460
  with gr.Tabs():
461
+ with gr.Tab("Patient Dashboard", id=0):
462
+ gr.Markdown(f"## Welcome, {patient_name or 'Guest'}! (Patient ID: {patient_id or 'Not Logged In'})")
463
+ if patient_id:
464
+ dashboard_table, high_risk_info = view_risk_dashboard(patient_id)
465
+ gr.Dataframe(dashboard_table, label="Your Symptom Logs", interactive=False)
466
+ gr.Textbox(high_risk_info, label="High-Risk Cases", interactive=False)
467
+ else:
468
+ gr.Markdown("Please log in via Salesforce to view your dashboard.")
469
+
470
+ with gr.Tab("Register New Patient", id=1):
471
  with gr.Row():
472
  with gr.Column():
473
+ name_first = gr.Textbox(label="First Name", placeholder="John", info="Enter your first name")
474
+ name_last = gr.Textbox(label="Last Name", placeholder="Doe", info="Enter your last name")
475
  with gr.Column():
476
+ dob_input = gr.Textbox(label="Date of Birth (MM-DD-YYYY)", placeholder="01-15-1990", info="e.g., MM-DD-YYYY")
477
+ gender_input = gr.Dropdown(label="Gender", choices=["Male", "Female", "Other", "Prefer Not to Say"], value="Prefer Not to Say")
478
  with gr.Row():
479
+ height_input = gr.Textbox(label="Height (inches)", placeholder="65", info="e.g., 65 inches")
480
+ weight_input = gr.Textbox(label="Weight (pounds)", placeholder="150", info="e.g., 150 lbs")
481
+ marital_input = gr.Dropdown(label="Marital Status", choices=["Single", "Married", "Divorced", "Widowed"], value="Single")
482
  with gr.Row():
483
+ phone_input = gr.Textbox(label="Contact Number", placeholder="+1234567890", info="e.g., +12025550123")
484
+ email_input = gr.Textbox(label="Email", placeholder="john.doe@clinic.com", info="e.g., your.email@domain.com")
485
  with gr.Row():
486
  with gr.Column():
487
+ address_city = gr.Textbox(label="City", placeholder="New York", info="e.g., New York")
488
+ address_state = gr.Textbox(label="State", placeholder="NY", info="e.g., NY")
489
+ postal_input = gr.Textbox(label="Postal Code", placeholder="10001", info="e.g., 10001")
490
  with gr.Row():
491
+ med_yes_no = gr.Radio(label="Taking Medications?", choices=["Yes", "No"], value="No")
492
+ med_list = gr.Textbox(label="Medication List", placeholder="e.g., Aspirin, 100mg", lines=2, visible=False)
493
+ with gr.Accordion("Emergency Contact", open=False):
494
  with gr.Row():
495
  with gr.Column():
496
+ emergency_first = gr.Textbox(label="First Name", placeholder="Jane")
497
+ emergency_last = gr.Textbox(label="Last Name", placeholder="Doe")
498
  with gr.Column():
499
+ emergency_relation = gr.Textbox(label="Relationship", placeholder="Spouse")
500
+ emergency_number = gr.Textbox(label="Contact Number", placeholder="+1234567890")
501
+ language_input = gr.Dropdown(["English", "Telugu", "Hindi"], label="Preferred Language", value="English")
502
+ consent_input = gr.Dropdown(["SMS", "WhatsApp", "Awaiting"], label="Consent Method", value="SMS")
503
+ register_button = gr.Button("Register Patient")
504
+ register_output = gr.Textbox(label="Registration Result")
505
+
 
506
  def toggle_med_list(med_choice):
507
  return gr.update(visible=med_choice == "Yes")
508
 
509
+ med_yes_no.change(fn=toggle_med_list, inputs=med_yes_no, outputs=med_list)
 
 
 
 
 
510
  register_button.click(
511
  fn=register_patient,
512
+ inputs=[name_first, name_last, dob_input, gender_input, height_input, weight_input, marital_input,
513
+ phone_input, email_input, address_city, address_state, postal_input, med_yes_no, med_list,
514
+ emergency_first, emergency_last, emergency_relation, emergency_number, language_input, consent_input],
515
+ outputs=[register_output, patient_state]
 
516
  )
517
 
518
+ with gr.Tab("Schedule Appointment", id=2):
519
+ appt_email_input = gr.Textbox(label="Patient Email", placeholder="john.doe@clinic.com", value=patient_context.get("email", ""))
520
+ appt_event_input = gr.Dropdown(label="Appointment Duration", choices=["15min", "30min", "60min"], value="30min", info="Select appointment length")
521
+ appt_button = gr.Button("Book Appointment")
522
+ appt_output = gr.Textbox(label="Appointment Result")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  appt_button.click(
524
  fn=schedule_appointment,
525
  inputs=[appt_email_input, appt_event_input],
526
  outputs=appt_output
527
  )
528
 
529
+ with gr.Tab("Submit Survey", id=3):
530
+ survey_phone_input = gr.Textbox(label="Patient Phone Number", placeholder="+1234567890", value=patient_context.get("phone", ""), info="e.g., +12025550123")
531
  survey_question_input = gr.Dropdown(label="Select Question", choices=[
532
  "How would you rate your visit today?",
533
  "Are you experiencing any discomfort?",
534
  "How satisfied are you with our staff?"
535
+ ], value="How would you rate your visit today?")
536
+ survey_answer_input = gr.Textbox(label="Your Answer", lines=4, placeholder="e.g., Very satisfied")
537
  survey_button = gr.Button("Submit Survey")
538
+ survey_output = gr.Textbox(label="Survey Result")
539
  survey_button.click(
540
  fn=submit_survey,
541
  inputs=[survey_phone_input, survey_answer_input],
542
  outputs=survey_output
543
  )
544
 
545
+ with gr.Tab("Escalate Case", id=4):
546
+ escalate_id_input = gr.Textbox(label="Patient ID", placeholder="e.g., 001xxxxxxxxxxxx", value=patient_context.get("patientId", ""), info="Enter Patient ID for escalation")
547
+ escalate_response_input = gr.Textbox(label="Response Text", placeholder="e.g., Severe pain reported", info="Describe the issue")
548
+ escalate_button = gr.Button("Escalate Case")
549
+ escalate_output = gr.Textbox(label="Escalation Result")
550
+ escalate_button.click(
551
+ fn=escalate_case,
552
+ inputs=[escalate_id_input, escalate_response_input],
553
+ outputs=escalate_output
554
  )
 
 
 
 
 
 
 
 
 
 
555
 
556
  # Launch Gradio app
557
  if __name__ == "__main__":