Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -65,56 +65,6 @@ SALESFORCE_HEADERS = {
|
|
| 65 |
"Content-Type": "application/json"
|
| 66 |
}
|
| 67 |
|
| 68 |
-
# Hugging Face Client
|
| 69 |
-
class HuggingFaceClient:
|
| 70 |
-
def __init__(self):
|
| 71 |
-
self.api_url = "https://api-inference.huggingface.co/models/"
|
| 72 |
-
self.headers = {}
|
| 73 |
-
self.sentiment_model = "distilbert-base-uncased-finetuned-sst-2-english"
|
| 74 |
-
self.severity_model = "bert-base-uncased"
|
| 75 |
-
|
| 76 |
-
def query(self, text, model):
|
| 77 |
-
payload = {"inputs": text}
|
| 78 |
-
try:
|
| 79 |
-
response = requests.post(f"{self.api_url}{model}", headers=self.headers, json=payload, timeout=10)
|
| 80 |
-
response.raise_for_status()
|
| 81 |
-
return response.json()
|
| 82 |
-
except requests.exceptions.RequestException as e:
|
| 83 |
-
logging.error(f"API request failed: {str(e)}")
|
| 84 |
-
return None
|
| 85 |
-
|
| 86 |
-
def analyze_response(self, response_text):
|
| 87 |
-
sentiment_result = self.query(response_text, self.sentiment_model)
|
| 88 |
-
severity_result = self.query(response_text, self.severity_model)
|
| 89 |
-
if not sentiment_result or not severity_result:
|
| 90 |
-
return None
|
| 91 |
-
sentiment_score = self.process_sentiment(sentiment_result)
|
| 92 |
-
severity_score = self.process_severity(severity_result)
|
| 93 |
-
risk_level = self.determine_risk_level(sentiment_score, severity_score)
|
| 94 |
-
return {"sentiment_score": sentiment_score, "severity_score": severity_score, "risk_level": risk_level}
|
| 95 |
-
|
| 96 |
-
def process_sentiment(self, result):
|
| 97 |
-
if result and isinstance(result, list) and len(result) > 0:
|
| 98 |
-
for item in result[0]:
|
| 99 |
-
if item["label"] == "POSITIVE":
|
| 100 |
-
return int(item["score"] * 100)
|
| 101 |
-
elif item["label"] == "NEGATIVE":
|
| 102 |
-
return int((1 - item["score"]) * 100)
|
| 103 |
-
return 0
|
| 104 |
-
|
| 105 |
-
def process_severity(self, result):
|
| 106 |
-
if result and isinstance(result, list) and len(result) > 0:
|
| 107 |
-
return int(result[0].get("score", 0) * 100)
|
| 108 |
-
return 0
|
| 109 |
-
|
| 110 |
-
def determine_risk_level(self, sentiment_score, severity_score):
|
| 111 |
-
combined_score = (sentiment_score + severity_score) / 2
|
| 112 |
-
if combined_score >= 75:
|
| 113 |
-
return "High"
|
| 114 |
-
elif combined_score >= 50:
|
| 115 |
-
return "Medium"
|
| 116 |
-
return "Low"
|
| 117 |
-
|
| 118 |
# Salesforce Client
|
| 119 |
class SalesforceClient:
|
| 120 |
def __init__(self, base_url, headers):
|
|
@@ -171,7 +121,6 @@ class TwilioClient:
|
|
| 171 |
return False
|
| 172 |
|
| 173 |
# Initialize Clients
|
| 174 |
-
hf_client = HuggingFaceClient()
|
| 175 |
sf_client = SalesforceClient(SALESFORCE_BASE_URL, SALESFORCE_HEADERS)
|
| 176 |
twilio_client = TwilioClient(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_PHONE)
|
| 177 |
|
|
@@ -202,27 +151,68 @@ def follow_up_scheduler():
|
|
| 202 |
sf_client.update_record("FollowUpPlan__c", plan["Id"], {"Status__c": "Sent"})
|
| 203 |
time.sleep(86400) # Run daily
|
| 204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
# Schedule Appointment
|
| 206 |
-
def schedule_appointment(patient_id, doctor_name,
|
| 207 |
-
if not all([patient_id, doctor_name,
|
| 208 |
return "All fields are required."
|
| 209 |
doctors = [doc["Name"] for doc in sf_client.query_records("SELECT Name FROM Doctor__c")]
|
| 210 |
-
if doctor_name not in doctors
|
| 211 |
return "Invalid doctor selected."
|
| 212 |
appointment_data = {
|
| 213 |
"Patient__c": patient_id,
|
| 214 |
-
"
|
| 215 |
-
"
|
|
|
|
| 216 |
"Status__c": "Scheduled",
|
| 217 |
-
"
|
|
|
|
| 218 |
}
|
| 219 |
-
if reason: appointment_data["Reason__c"] = reason
|
| 220 |
-
if special_requests: appointment_data["SpecialRequests__c"] = special_requests
|
| 221 |
result = sf_client.create_record("Appointment__c", appointment_data)
|
| 222 |
if result:
|
| 223 |
-
return f"Appointment scheduled with {doctor_name} on {
|
| 224 |
return "Failed to schedule appointment."
|
| 225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
# Gradio Interface
|
| 227 |
with gr.Blocks(theme=gr.themes.Soft(), title="HealthPortal - Patient Care") as demo:
|
| 228 |
gr.Markdown("# HealthPortal - Patient Care System")
|
|
@@ -231,50 +221,106 @@ with gr.Blocks(theme=gr.themes.Soft(), title="HealthPortal - Patient Care") as d
|
|
| 231 |
|
| 232 |
with gr.Tabs():
|
| 233 |
with gr.Tab("Home"):
|
| 234 |
-
gr.Markdown("## Welcome to Your Dashboard!")
|
| 235 |
if not patient_id_state.value:
|
| 236 |
gr.Markdown("**You are not logged in.** Please log in or register to access your personalized dashboard.")
|
| 237 |
with gr.Row():
|
| 238 |
gr.Button("Login", link=f"{SALESFORCE_INSTANCE_URL}/apex/PatientLoginForm")
|
| 239 |
gr.Button("Register", link=f"{SALESFORCE_INSTANCE_URL}/apex/PatientRegistrationForm")
|
|
|
|
| 240 |
else:
|
| 241 |
with gr.Row():
|
| 242 |
-
with gr.Column():
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
health_score = sf_client.query_records(f"SELECT AVG(RiskScore__c) FROM SymptomLog__c WHERE Patient__c = '{patient_id_state.value}'")[0].get("expr0", 0)
|
| 252 |
-
gr.Markdown(f"**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
with gr.Tab("Appointments"):
|
| 255 |
if not patient_id_state.value:
|
| 256 |
gr.Markdown("Please log in to manage appointments.")
|
| 257 |
else:
|
| 258 |
-
gr.Markdown("##
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
|
|
|
|
|
|
|
|
|
| 262 |
with gr.Row():
|
| 263 |
-
gr.
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
time_input = gr.Dropdown(time_slots, label="Available Time Slots", value=time_slots[0])
|
| 269 |
reason_input = gr.Textbox(label="Reason for Visit")
|
| 270 |
special_input = gr.Textbox(label="Special Instructions")
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
fn=schedule_appointment,
|
| 275 |
-
inputs=[patient_id_state, doctor_input, date_input, time_input, reason_input, special_input],
|
| 276 |
-
outputs=
|
| 277 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
|
| 279 |
# Launch with Scheduler
|
| 280 |
if __name__ == "__main__":
|
|
|
|
| 65 |
"Content-Type": "application/json"
|
| 66 |
}
|
| 67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
# Salesforce Client
|
| 69 |
class SalesforceClient:
|
| 70 |
def __init__(self, base_url, headers):
|
|
|
|
| 121 |
return False
|
| 122 |
|
| 123 |
# Initialize Clients
|
|
|
|
| 124 |
sf_client = SalesforceClient(SALESFORCE_BASE_URL, SALESFORCE_HEADERS)
|
| 125 |
twilio_client = TwilioClient(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_PHONE)
|
| 126 |
|
|
|
|
| 151 |
sf_client.update_record("FollowUpPlan__c", plan["Id"], {"Status__c": "Sent"})
|
| 152 |
time.sleep(86400) # Run daily
|
| 153 |
|
| 154 |
+
# Update Patient Profile
|
| 155 |
+
def update_patient_profile(patient_id, height, weight, emergency_name, emergency_number, emergency_relationship, address, dob):
|
| 156 |
+
if not patient_id:
|
| 157 |
+
return "Please log in to update your profile."
|
| 158 |
+
update_data = {}
|
| 159 |
+
if height is not None and height != "": update_data["Height__c"] = float(height)
|
| 160 |
+
if weight is not None and weight != "": update_data["Weight__c"] = float(weight)
|
| 161 |
+
if emergency_name: update_data["EmergencyContactName__c"] = emergency_name
|
| 162 |
+
if emergency_number: update_data["EmergencyContactNumber__c"] = emergency_number
|
| 163 |
+
if emergency_relationship: update_data["EmergencyContactRelationship__c"] = emergency_relationship
|
| 164 |
+
if address: update_data["Address__c"] = address
|
| 165 |
+
if dob: update_data["DateOfBirth__c"] = dob
|
| 166 |
+
if update_data:
|
| 167 |
+
if sf_client.update_record("Patient__c", patient_id, update_data):
|
| 168 |
+
return "Profile updated successfully!"
|
| 169 |
+
return "Failed to update profile."
|
| 170 |
+
return "No changes made."
|
| 171 |
+
|
| 172 |
# Schedule Appointment
|
| 173 |
+
def schedule_appointment(patient_id, doctor_name, appointment_type, preferred_date, time_slot, reason, special_requests):
|
| 174 |
+
if not all([patient_id, doctor_name, appointment_type, preferred_date, time_slot]):
|
| 175 |
return "All fields are required."
|
| 176 |
doctors = [doc["Name"] for doc in sf_client.query_records("SELECT Name FROM Doctor__c")]
|
| 177 |
+
if doctor_name not in doctors:
|
| 178 |
return "Invalid doctor selected."
|
| 179 |
appointment_data = {
|
| 180 |
"Patient__c": patient_id,
|
| 181 |
+
"Doctor__c": doctor_name,
|
| 182 |
+
"AppointmentType__c": appointment_type,
|
| 183 |
+
"DateTime__c": f"{preferred_date} {time_slot}:00",
|
| 184 |
"Status__c": "Scheduled",
|
| 185 |
+
"Reason__c": reason,
|
| 186 |
+
"SpecialRequests__c": special_requests
|
| 187 |
}
|
|
|
|
|
|
|
| 188 |
result = sf_client.create_record("Appointment__c", appointment_data)
|
| 189 |
if result:
|
| 190 |
+
return f"Appointment scheduled with {doctor_name} on {preferred_date} at {time_slot}:00!"
|
| 191 |
return "Failed to schedule appointment."
|
| 192 |
|
| 193 |
+
# Get Next Appointment
|
| 194 |
+
def get_next_appointment(patient_id):
|
| 195 |
+
if not patient_id:
|
| 196 |
+
return None
|
| 197 |
+
next_appt = sf_client.query_records(f"SELECT DateTime__c, Doctor__c FROM Appointment__c WHERE Patient__c = '{patient_id}' AND Status__c = 'Scheduled' ORDER BY DateTime__c ASC LIMIT 1")
|
| 198 |
+
return next_appt[0] if next_appt else None
|
| 199 |
+
|
| 200 |
+
# Get Pending Fields
|
| 201 |
+
def get_pending_fields(patient_id):
|
| 202 |
+
if not patient_id:
|
| 203 |
+
return []
|
| 204 |
+
patient_data = sf_client.query_records(f"SELECT Height__c, Weight__c, EmergencyContactName__c, EmergencyContactNumber__c, EmergencyContactRelationship__c, Address__c, DateOfBirth__c FROM Patient__c WHERE Id = '{patient_id}'")[0]
|
| 205 |
+
required_fields = ["Height__c", "Weight__c", "EmergencyContactName__c", "EmergencyContactNumber__c", "EmergencyContactRelationship__c", "Address__c", "DateOfBirth__c"]
|
| 206 |
+
pending = [field for field in required_fields if not patient_data.get(field)]
|
| 207 |
+
return pending
|
| 208 |
+
|
| 209 |
+
# Get All Appointments
|
| 210 |
+
def get_all_appointments(patient_id):
|
| 211 |
+
if not patient_id:
|
| 212 |
+
return []
|
| 213 |
+
appointments = sf_client.query_records(f"SELECT DateTime__c, Doctor__c, AppointmentType__c, Status__c, Reason__c FROM Appointment__c WHERE Patient__c = '{patient_id}' ORDER BY DateTime__c DESC")
|
| 214 |
+
return appointments if appointments else []
|
| 215 |
+
|
| 216 |
# Gradio Interface
|
| 217 |
with gr.Blocks(theme=gr.themes.Soft(), title="HealthPortal - Patient Care") as demo:
|
| 218 |
gr.Markdown("# HealthPortal - Patient Care System")
|
|
|
|
| 221 |
|
| 222 |
with gr.Tabs():
|
| 223 |
with gr.Tab("Home"):
|
|
|
|
| 224 |
if not patient_id_state.value:
|
| 225 |
gr.Markdown("**You are not logged in.** Please log in or register to access your personalized dashboard.")
|
| 226 |
with gr.Row():
|
| 227 |
gr.Button("Login", link=f"{SALESFORCE_INSTANCE_URL}/apex/PatientLoginForm")
|
| 228 |
gr.Button("Register", link=f"{SALESFORCE_INSTANCE_URL}/apex/PatientRegistrationForm")
|
| 229 |
+
# Note: Login and register links unchanged; relies on iframe/Salesforce flow
|
| 230 |
else:
|
| 231 |
with gr.Row():
|
| 232 |
+
with gr.Column(scale=1):
|
| 233 |
+
gr.Markdown("### New Appointment")
|
| 234 |
+
next_appt = get_next_appointment(patient_id_state.value)
|
| 235 |
+
if next_appt:
|
| 236 |
+
gr.Markdown(f"**Date:** {next_appt['DateTime__c'].split(' ')[0]}\n**Time:** {next_appt['DateTime__c'].split(' ')[1][:5]}\n**Doctor:** {next_appt['Doctor__c']}")
|
| 237 |
+
else:
|
| 238 |
+
gr.Markdown("No upcoming appointments.")
|
| 239 |
+
with gr.Column(scale=1):
|
| 240 |
+
gr.Markdown("### Pending Forms")
|
| 241 |
+
pending_fields = get_pending_fields(patient_id_state.value)
|
| 242 |
+
if pending_fields:
|
| 243 |
+
gr.Markdown(f"**Unfilled Fields:** {', '.join(pending_fields)}")
|
| 244 |
+
with gr.Row():
|
| 245 |
+
details_button = gr.Button("View Details")
|
| 246 |
+
else:
|
| 247 |
+
gr.Markdown("No pending forms.")
|
| 248 |
+
with gr.Column(scale=1):
|
| 249 |
+
gr.Markdown("### Health Score")
|
| 250 |
health_score = sf_client.query_records(f"SELECT AVG(RiskScore__c) FROM SymptomLog__c WHERE Patient__c = '{patient_id_state.value}'")[0].get("expr0", 0)
|
| 251 |
+
gr.Markdown(f"**Score:** {int(health_score)}/100\n{'Excellent' if health_score >= 80 else 'Monitor'}")
|
| 252 |
+
|
| 253 |
+
if pending_fields:
|
| 254 |
+
def show_details_page():
|
| 255 |
+
return gr.update(visible=True)
|
| 256 |
+
details_page = gr.Column(visible=False)
|
| 257 |
+
with details_page:
|
| 258 |
+
gr.Markdown("## Update Patient Details")
|
| 259 |
+
patient_data = sf_client.query_records(f"SELECT Height__c, Weight__c, EmergencyContactName__c, EmergencyContactNumber__c, EmergencyContactRelationship__c, Address__c, DateOfBirth__c FROM Patient__c WHERE Id = '{patient_id_state.value}'")[0]
|
| 260 |
+
height_input = gr.Number(label="Height (cm)", value=patient_data.get("Height__c", 0), interactive=True)
|
| 261 |
+
weight_input = gr.Number(label="Weight (kg)", value=patient_data.get("Weight__c", 0), interactive=True)
|
| 262 |
+
emergency_name_input = gr.Textbox(label="Emergency Contact Name", value=patient_data.get("EmergencyContactName__c", ""))
|
| 263 |
+
emergency_number_input = gr.Textbox(label="Emergency Contact Number", value=patient_data.get("EmergencyContactNumber__c", ""))
|
| 264 |
+
emergency_rel_input = gr.Textbox(label="Emergency Contact Relationship", value=patient_data.get("EmergencyContactRelationship__c", ""))
|
| 265 |
+
address_input = gr.Textbox(label="Address", value=patient_data.get("Address__c", ""))
|
| 266 |
+
dob_input = gr.Textbox(label="Date of Birth (YYYY-MM-DD)", value=patient_data.get("DateOfBirth__c", ""))
|
| 267 |
+
update_button = gr.Button("Save")
|
| 268 |
+
back_button = gr.Button("Back")
|
| 269 |
+
output = gr.Textbox(label="Result")
|
| 270 |
+
update_button.click(
|
| 271 |
+
fn=update_patient_profile,
|
| 272 |
+
inputs=[patient_id_state, height_input, weight_input, emergency_name_input, emergency_number_input, emergency_rel_input, address_input, dob_input],
|
| 273 |
+
outputs=output
|
| 274 |
+
)
|
| 275 |
+
back_button.click(
|
| 276 |
+
fn=lambda: gr.update(visible=False),
|
| 277 |
+
outputs=details_page
|
| 278 |
+
)
|
| 279 |
+
details_button.click(
|
| 280 |
+
fn=show_details_page,
|
| 281 |
+
outputs=details_page
|
| 282 |
+
)
|
| 283 |
|
| 284 |
with gr.Tab("Appointments"):
|
| 285 |
if not patient_id_state.value:
|
| 286 |
gr.Markdown("Please log in to manage appointments.")
|
| 287 |
else:
|
| 288 |
+
gr.Markdown("## Appointment History")
|
| 289 |
+
appointments = get_all_appointments(patient_id_state.value)
|
| 290 |
+
if appointments:
|
| 291 |
+
for appt in appointments:
|
| 292 |
+
gr.Markdown(f"**{appt['AppointmentType__c']} with {appt['Doctor__c']}\nDate:** {appt['DateTime__c'].split(' ')[0]}\n**Time:** {appt['DateTime__c'].split(' ')[1][:5]}\n**Status:** {appt['Status__c']}\n**Reason:** {appt.get('Reason__c', 'N/A')}")
|
| 293 |
+
else:
|
| 294 |
+
gr.Markdown("You have not booked any appointments.")
|
| 295 |
with gr.Row():
|
| 296 |
+
with gr.Column(scale=1, min_width=100):
|
| 297 |
+
schedule_button = gr.Button("Schedule Appointment", variant="primary")
|
| 298 |
+
with gr.Column(scale=3):
|
| 299 |
+
pass
|
| 300 |
+
def show_schedule_page():
|
| 301 |
+
return gr.update(visible=True)
|
| 302 |
+
schedule_page = gr.Column(visible=False)
|
| 303 |
+
with schedule_page:
|
| 304 |
+
gr.Markdown("## Schedule New Appointment")
|
| 305 |
+
doctors = [doc["Name"] for doc in sf_client.query_records("SELECT Name FROM Doctor__c")]
|
| 306 |
+
doctor_input = gr.Dropdown(doctors, label="Select Provider", value=doctors[0] if doctors else "")
|
| 307 |
+
appointment_type_input = gr.Dropdown(["Consultation", "Follow-up", "Procedure"], label="Appointment Type", value="Consultation")
|
| 308 |
+
date_input = gr.Date(label="Preferred Date", value=date.today())
|
| 309 |
+
time_slots = ["09:00", "10:00", "11:00", "14:00", "15:00", "16:00"] # Mock, to be replaced with real availability
|
| 310 |
time_input = gr.Dropdown(time_slots, label="Available Time Slots", value=time_slots[0])
|
| 311 |
reason_input = gr.Textbox(label="Reason for Visit")
|
| 312 |
special_input = gr.Textbox(label="Special Instructions")
|
| 313 |
+
schedule_submit = gr.Button("Schedule")
|
| 314 |
+
output = gr.Textbox(label="Result")
|
| 315 |
+
schedule_submit.click(
|
| 316 |
fn=schedule_appointment,
|
| 317 |
+
inputs=[patient_id_state, doctor_input, appointment_type_input, date_input, time_input, reason_input, special_input],
|
| 318 |
+
outputs=output
|
| 319 |
)
|
| 320 |
+
schedule_button.click(
|
| 321 |
+
fn=show_schedule_page,
|
| 322 |
+
outputs=schedule_page
|
| 323 |
+
)
|
| 324 |
|
| 325 |
# Launch with Scheduler
|
| 326 |
if __name__ == "__main__":
|