Innovator89 commited on
Commit
fe23844
·
verified ·
1 Parent(s): 034f146

after changes

Browse files
Files changed (1) hide show
  1. app.py +70 -59
app.py CHANGED
@@ -2,7 +2,6 @@ import io
2
  import os
3
  import re
4
  from datetime import datetime, timedelta
5
-
6
  import numpy as np
7
  import pandas as pd
8
  import requests
@@ -13,10 +12,49 @@ from groq import Groq
13
  from reportlab.lib.pagesizes import letter
14
  from reportlab.pdfgen import canvas
15
 
16
- # Import database and user management functions
17
- from database import get_user_history
18
- from user_management import show_user_management, save_history_if_logged_in
19
- from utils import extract_keyword # Ensure utils.py has the extract_keyword function
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  # Load environment variables from .env.local for local development.
22
  # On Hugging Face Spaces, st.secrets will be used.
@@ -33,10 +71,6 @@ try:
33
  GROQ_API_KEY = st.secrets.get("GROQ_API_KEY")
34
  if not GROQ_API_KEY:
35
  # Fallback to environment variable for local development if not in secrets
36
- # You might not want to write raw file content to the app in production
37
- # f = open(".env", "r")
38
- # st.write(f.read())
39
- # f.close()
40
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
41
  if GROQ_API_KEY:
42
  groq_client = Groq(api_key=GROQ_API_KEY)
@@ -78,7 +112,7 @@ if "feedback_input" not in st.session_state: # For clearing feedback text area
78
  # Ensure user_id is initialized for saving history
79
  if "user_id" not in st.session_state:
80
  st.session_state.user_id = None
81
- if "logged_in_user" not in st.session_state:
82
  st.session_state.logged_in_user = None
83
 
84
  # --- HARDCODED DATA LOADING FROM CSVs ---
@@ -90,6 +124,7 @@ def load_csv_data():
90
  description_df = pd.read_csv('symptom_Description.csv').fillna('')
91
  precaution_df = pd.read_csv('symptom_precaution.csv').fillna('')
92
  severity_df = pd.read_csv('Symptom-severity.csv').fillna('') # Load symptom severity
 
93
  # Prepare data for quick lookup
94
  # Dataset mapping diseases to their symptoms
95
  disease_symptoms_map = {}
@@ -98,13 +133,17 @@ def load_csv_data():
98
  # Get all symptoms for this disease, filtering out empty strings and 'Disease' column itself
99
  symptoms = [s.strip().replace('_', ' ') for s in row.values[1:] if s.strip()]
100
  disease_symptoms_map[disease] = symptoms
 
101
  # Disease descriptions map
102
  disease_description_map = {row['Disease']: row['Description'] for index, row in description_df.iterrows()}
 
103
  # Disease precautions map
104
  disease_precaution_map = {row['Disease']: [p.strip() for p in row.values[1:] if p.strip()] for index, row in precaution_df.iterrows()}
 
105
  # Symptom severity map
106
  # Ensure symptom names are consistent (e.g., lowercase and spaces instead of underscores)
107
  symptom_severity_map = {row['Symptom'].strip().replace('_', ' ').lower(): row['weight'] for index, row in severity_df.iterrows()}
 
108
  # Extract all unique symptoms for the multiselect
109
  all_unique_symptoms = sorted(list(set(symptom for symptoms_list in disease_symptoms_map.values() for symptom in symptoms_list)))
110
  return disease_symptoms_map, disease_description_map, disease_precaution_map, all_unique_symptoms, symptom_severity_map
@@ -137,7 +176,6 @@ st.markdown("""
137
  margin-bottom: 2rem;
138
  background-color: #ffffff; /* White background */
139
  }
140
-
141
  /* Headers */
142
  h1, h2, h3, h4, h5, h6 {
143
  color: #004d99; /* A professional darker blue */
@@ -148,7 +186,6 @@ st.markdown("""
148
  h1 { font-size: 2.8em; text-align: center; color: #003366; } /* Larger, darker blue for main title */
149
  h2 { font-size: 2.2em; border-bottom: 2px solid #e0e7ee; padding-bottom: 0.5em; margin-bottom: 1em; } /* Subtle line under h2 */
150
  h3 { font-size: 1.6em; }
151
-
152
  /* Main Tabs Styling (Home, History, Feedback, About, Insights) */
153
  .stTabs [data-baseweb="tab-list"] {
154
  gap: 20px; /* More space between tabs */
@@ -191,7 +228,6 @@ st.markdown("""
191
  outline: none !important;
192
  box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25) !important; /* Subtle focus glow if needed */
193
  }
194
-
195
  /* Nested Tabs Styling (Symptom Checker, Chat with MediBot) */
196
  /* This targets tabs that are children of other tabs, making them smaller */
197
  .stTabs [data-baseweb="tab-list"]:has(.stTabs [data-baseweb="tab-list"]) [data-baseweb="tab"],
@@ -224,17 +260,16 @@ st.markdown("""
224
  box-shadow: 0 2px 8px rgba(0,0,0,0.15); /* Subtle shadow for selected sub-tab */
225
  outline: none !important; /* Ensure no outline when selected */
226
  }
227
-
228
  /* General Button Styling */
229
  .stButton>button {
230
  background-color: #007bff; /* Primary blue button */
231
  color: white;
232
- border-radius: 10px; /* More rounded */ /* Corrected: was border_radius */
233
  padding: 12px 28px; /* More padding */
234
  font-weight: 600; /* Semi-bold */
235
  border: none;
236
  cursor: pointer;
237
- box-shadow: 0 5px 15px rgba(0,0,0,0.1); /* Softer, more diffuse shadow */ /* Corrected: was box_shadow */
238
  transition: all 0.3s ease-in-out; /* Smooth transitions */
239
  font-size: 1.05em; /* Slightly larger text */
240
  outline: none; /* Remove outline on focus */
@@ -243,7 +278,7 @@ st.markdown("""
243
  }
244
  .stButton>button:hover {
245
  background-color: #0056b3; /* Darker blue on hover */
246
- box-shadow: 0 8px 20px rgba(0,0,0,0.15); /* Larger shadow on hover */ /* Corrected: was box_shadow */
247
  transform: translateY(-3px); /* More pronounced lift */
248
  outline: none; /* Ensure no outline on hover either */
249
  }
@@ -252,17 +287,15 @@ st.markdown("""
252
  width: 100%; /* Full width buttons in sidebar */
253
  margin-bottom: 10px;
254
  }
255
-
256
-
257
  /* Input Fields, Selects, etc. */
258
  .stTextInput>div>div>input,
259
  .stTextArea>div>div>textarea, /* Target textarea as well */
260
  .stDateInput>div>div>input,
261
  .stMultiSelect>div>div {
262
- border-radius: 10px; /* More rounded */ /* Corrected: was border_radius */
263
  border: 1px solid #ced4da; /* Light grey border */
264
  padding: 10px 15px; /* More padding */
265
- box-shadow: inset 0 1px 4px rgba(0,0,0,0.05); /* Inner shadow */ /* Corrected: was box_shadow */
266
  transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
267
  background-color: #fefefe; /* Slightly off-white background */
268
  outline: none; /* Remove default outline */
@@ -286,10 +319,10 @@ st.markdown("""
286
  /* Expander (for disease info) */
287
  .stExpander {
288
  border: 1px solid #e0e7ee; /* Lighter grey border */
289
- border-radius: 12px; /* More rounded */ /* Corrected: was border_radius */
290
  margin-bottom: 15px; /* More space below */
291
  background-color: #ffffff; /* White background */
292
- box-shadow: 0 4px 12px rgba(0,0,0,0.05); /* Softer shadow */ /* Corrected: was box_shadow */
293
  overflow: hidden; /* Ensure rounded corners clip content */
294
  }
295
  .stExpander > div > div > .streamlit-expanderHeader {
@@ -341,7 +374,6 @@ st.markdown("""
341
  .stAlert.st-error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
342
  /* Spinners */
343
  .stSpinner { color: #007bff; } /* Spinner color matching primary button */
344
-
345
  /* Disclaimer Box - using a more reliable target */
346
  div[data-testid="stVerticalBlock"] > div:first-child > div:has(p > strong:contains("Disclaimer")) {
347
  background-color: #fff8e1; /* Light yellow for warnings/disclaimers */
@@ -356,7 +388,6 @@ st.markdown("""
356
  .stAlert[data-testid="stAlert"] p strong:contains("Important Disclaimer") {
357
  color: #664d03;
358
  }
359
-
360
  /* General text improvements */
361
  p {
362
  line-height: 1.7;
@@ -373,7 +404,6 @@ st.markdown("""
373
  .stExpander > div > div > .streamlit-expanderHeader {
374
  font-size: 1.2em; /* Increased font size */
375
  }
376
-
377
  /* Media queries for specific mobile adjustments if needed (example) */
378
  @media (max-width: 768px) {
379
  h1 {
@@ -398,8 +428,7 @@ st.markdown("""
398
  padding: 3px 8px !important;
399
  }
400
  }
401
- </style>
402
- """, unsafe_allow_html=True)
403
 
404
  # --- Groq API Response Function (updated system prompt) ---
405
  def get_groq_response(user_query, severity_label="Undetermined Severity", model="llama-3.3-70b-versatile"):
@@ -409,7 +438,6 @@ def get_groq_response(user_query, severity_label="Undetermined Severity", model=
409
  """
410
  if not GROQ_AVAILABLE or groq_client is None:
411
  return "AI assistant is not available."
412
-
413
  # Augment the system prompt with severity information from clinical diagnosis model
414
  # Note: 'severity_label' comes from the symptom checker. If asking a direct question,
415
  # it might default to "Undetermined Severity" unless explicitly passed from a prior analysis.
@@ -455,7 +483,6 @@ def get_diagnosis_and_severity_from_model(symptoms_text):
455
  if not HF_MODEL_AVAILABLE or not HF_API_TOKEN:
456
  st.warning("Hugging Face API key not available. Cannot assess severity.")
457
  return "Severity Assessment Unavailable (API Key Missing)", []
458
-
459
  API_URL = "https://api-inference.huggingface.co/models/DATEXIS/CORe-clinical-diagnosis-prediction"
460
  headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
461
  payload = {"inputs": symptoms_text}
@@ -463,7 +490,6 @@ def get_diagnosis_and_severity_from_model(symptoms_text):
463
  response = requests.post(API_URL, headers=headers, json=payload)
464
  response.raise_for_status()
465
  result = response.json()
466
-
467
  raw_predicted_diagnoses = []
468
  threshold = 0.5
469
  if result and isinstance(result, list) and len(result) > 0 and isinstance(result[0], list):
@@ -488,7 +514,6 @@ def get_diagnosis_and_severity_from_model(symptoms_text):
488
  'unlikely', 'possible', 'likely',
489
  'symptom', 'sign', 'pain', 'fever', 'cough', 'vomiting', 'nausea', 'rash', 'headache', 'fatigue', 'diarrhea', 'sore throat', 'hemorrhage'
490
  ]
491
-
492
  for diagnosis_label in raw_predicted_diagnoses:
493
  lower_diag = diagnosis_label.lower()
494
  is_generic = False
@@ -500,7 +525,6 @@ def get_diagnosis_and_severity_from_model(symptoms_text):
500
  is_generic = True
501
  if len(lower_diag.split()) <= 2 and lower_diag in ['pain', 'fever', 'cough', 'vomiting', 'nausea', 'rash', 'headache', 'fatigue', 'diarrhea', 'sore throat', 'hemorrhage']:
502
  is_generic = True
503
-
504
  if not is_generic:
505
  filtered_diagnoses.append(diagnosis_label)
506
 
@@ -527,9 +551,7 @@ def get_diagnosis_and_severity_from_model(symptoms_text):
527
  overall_severity = "Moderate Severity"
528
  # Keep looping to see if a High Severity diagnosis is found later in the filtered list
529
  # Don't return here if a Moderate is found, continue to check for High
530
-
531
  return f"Overall Symptom Severity (AI Assessment): {overall_severity}", filtered_diagnoses
532
-
533
  except requests.exceptions.RequestException as e:
534
  st.error(f"Network or API Error during clinical diagnosis model call: {e}. Check API key and internet connection.")
535
  return "Severity Assessment Unavailable (Network Error)", []
@@ -547,11 +569,9 @@ def generate_pdf_report(history_data):
547
  c.setFont("Helvetica", 10)
548
  c.drawString(50, height - 70, f"Report Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
549
  y = height - 100
550
-
551
  for entry in history_data:
552
  # history_data comes as list of tuples: (history_id, symptoms, predicted_diseases, query_timestamp)
553
  timestamp, symptoms_text, predicted_diseases = entry[3], entry[1], entry[2]
554
-
555
  if y < 100:
556
  c.showPage()
557
  c.setFont("Helvetica-Bold", 16)
@@ -563,7 +583,6 @@ def generate_pdf_report(history_data):
563
  c.drawString(50, y, f"Timestamp: {timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
564
  y -= 15
565
  c.setFont("Helvetica", 10)
566
-
567
  symptoms_line = f"Symptoms/Question: {symptoms_text}"
568
  insight_line = f"Insight/Response: {predicted_diseases}"
569
 
@@ -600,7 +619,6 @@ def generate_pdf_report(history_data):
600
  draw_wrapped_text(textobject, insight_line, width - 100)
601
  c.drawText(textobject)
602
  y -= 20
603
-
604
  c.save()
605
  buffer.seek(0)
606
  return buffer
@@ -670,7 +688,6 @@ def parse_insight_text_for_conditions(insight_text: str) -> list[str]:
670
  if "feedback_input" not in st.session_state:
671
  st.session_state.feedback_input = ""
672
 
673
-
674
  if st.session_state.show_welcome:
675
  # A more visually appealing welcome page
676
  st.markdown("<h1 style='color: #0056b3; font-size: 3.5em; margin-top: 50px; text-shadow: 2px 2px 4px rgba(0,0,0,0.1);'>MediBot - Your Personal Health Assistant 🏥</h1>", unsafe_allow_html=True)
@@ -711,10 +728,12 @@ else:
711
  "</p>"
712
  "</div>", unsafe_allow_html=True)
713
 
714
- # Sidebar for User Management (remains in sidebar)
715
  with st.sidebar:
716
  st.header("User Management")
717
- show_user_management()
 
 
718
  st.markdown("---")
719
  st.header("Navigation")
720
  # Add a button to clear chat history
@@ -723,9 +742,10 @@ else:
723
  st.session_state.last_chat_response = ""
724
  st.session_state.chat_input_value = ""
725
  st.success("Chat history cleared!")
 
726
  # PDF Download Button - available if user is logged in and has history
727
- if st.session_state.get("logged_in_user"):
728
- user_history = get_user_history(st.session_state["logged_in_user"])
729
  if user_history:
730
  pdf_buffer = generate_pdf_report(user_history)
731
  st.download_button(
@@ -737,9 +757,10 @@ else:
737
  )
738
  else:
739
  st.info("No history to download yet. Interact with MediBot to generate a report.")
 
 
740
 
741
  st.markdown("<h1 style='text-align: center; color: #0056b3;'>MediBot - Your Health Assistant 🩺</h1>", unsafe_allow_html=True)
742
-
743
  # Top-level tabs: Home, History, Feedback, About, Insights
744
  # Re-ordered tabs slightly to put Insights closer to History
745
  main_tab_home, main_tab_history, main_tab_insights, main_tab_feedback, main_tab_about = st.tabs(["Home", "History", "Insights", "Feedback", "About"])
@@ -832,9 +853,8 @@ else:
832
  predicted_diseases_str_for_history = f"{severity_assessment_text.replace('Overall Symptom Severity (AI Assessment): ', '')}. No specific insights found."
833
 
834
  if st.session_state.get("user_id"):
835
- # This needs to be called correctly. The original line had a missing argument.
836
- # Assuming save_history_if_logged_in takes (user_object, query, response)
837
- save_history_if_logged_in(st.session_state.get("logged_in_user"), ", ".join(selected_symptoms), predicted_diseases_str_for_history)
838
  st.success("Analysis saved to your history.")
839
  else:
840
  st.info("Log in to save this analysis to your history.")
@@ -857,23 +877,18 @@ else:
857
  # For the chatbot, if it's a direct question, severity is undetermined
858
  # unless passed from a preceding symptom check.
859
  # For simplicity, if not from symptom checker, pass "Undetermined Severity".
860
- # You could enhance this to try to infer severity from the question itself if needed.
861
  severity_for_groq_prompt = "Undetermined Severity"
862
- # If you want to use the severity from a recent symptom check,
863
- # you'd need to store it in session_state and pass it here.
864
- # For now, we're keeping the Q&A separate.
865
  with st.spinner("MediBot is thinking..."):
866
  groq_answer = get_groq_response(user_question, severity_label=severity_for_groq_prompt)
867
  st.markdown("**MediBot's Answer:**")
868
  st.write(groq_answer)
869
-
870
  # Save Q&A to chat history (if desired, not necessarily DB)
871
  st.session_state.chat_history.append({"user": user_question, "bot": groq_answer})
872
 
873
  # Save to database history if logged in
874
  if st.session_state.get("user_id"):
875
  # Prepend "Question: " to the input for history tracking
876
- save_history_if_logged_in(st.session_state.get("logged_in_user"), f"Question: {user_question}", groq_answer)
877
  st.success("Your question and answer have been saved to history.")
878
  else:
879
  st.info("Log in to save this Q&A to your history.")
@@ -886,13 +901,11 @@ else:
886
  if user_id:
887
  st.info("This section shows your saved symptom analyses and health questions.")
888
  history_data = get_user_history(user_id) # Fetch history from database
889
-
890
  if history_data:
891
  st.subheader("Past Interactions:")
892
  # Display history in reverse chronological order using expanders
893
  for entry in reversed(history_data):
894
  timestamp, symptoms_text, insight_text = entry[3], entry[1], entry[2]
895
-
896
  # Determine summary title based on the nature of the input
897
  summary_title_prefix = ""
898
  expander_icon = "" # Initialize icon here
@@ -924,7 +937,6 @@ else:
924
  expander_icon = "🩺"
925
 
926
  # MODIFIED LINE: Use Markdown heading (e.g., ###) for larger font and bolding
927
- # Experiment with ## or ### to find the desired size.
928
  expander_label = f"### **{timestamp.strftime('%Y-%m-%d %H:%M')}** - {expander_icon} *{summary_title_prefix}*"
929
  with st.expander(expander_label):
930
  st.write(f"**Your Input:** {symptoms_text}")
@@ -956,7 +968,6 @@ else:
956
  entry for entry in history_data
957
  if entry[3] and entry[3] > thirty_days_ago # entry[3] is query_timestamp
958
  ]
959
-
960
  if recent_history:
961
  all_conditions = []
962
  for entry in recent_history:
@@ -1024,4 +1035,4 @@ else:
1024
  st.markdown("[Learn more about Hugging Face](https://huggingface.co/)")
1025
 
1026
  st.markdown("---")
1027
- st.markdown("For professional medical advice, always consult a qualified healthcare provider.")
 
2
  import os
3
  import re
4
  from datetime import datetime, timedelta
 
5
  import numpy as np
6
  import pandas as pd
7
  import requests
 
12
  from reportlab.lib.pagesizes import letter
13
  from reportlab.pdfgen import canvas
14
 
15
+ # Import database functions directly here.
16
+ # These functions should NOT import anything from app.py or user_management.py
17
+ # If you don't have database.py, you'll need to create it with these functions.
18
+ # For demonstration, I'll assume they exist.
19
+ try:
20
+ from database import get_user_history, register_user, user_exists, check_user_credentials, save_user_history
21
+ # Assuming get_user_history, register_user, user_exists, check_user_credentials, save_user_history are in database.py
22
+ except ImportError:
23
+ st.error("Could not import database functions. Please ensure database.py exists and contains required functions.")
24
+ # Define dummy functions to allow the app to run without a real database.py for now
25
+ def get_user_history(user_id):
26
+ st.warning("Database function 'get_user_history' not implemented. History will not be persistent.")
27
+ return []
28
+ def register_user(user_id, full_name, dob, email, password):
29
+ st.warning("Database function 'register_user' not implemented. User registration will not be persistent.")
30
+ return True # Simulate success
31
+ def user_exists(user_id):
32
+ st.warning("Database function 'user_exists' not implemented. User existence check will not work.")
33
+ return False # Simulate user not existing
34
+ def check_user_credentials(user_id, password):
35
+ st.warning("Database function 'check_user_credentials' not implemented. Login will not work.")
36
+ return False # Simulate login failure
37
+ def save_user_history(user_id, symptoms, predicted_diseases):
38
+ st.warning("Database function 'save_user_history' not implemented. History saving will not be persistent.")
39
+ pass
40
+
41
+
42
+ # Import user management functions from user_management.py
43
+ # These functions will now receive st.session_state as an argument
44
+ from user_management import render_user_management_sidebar, save_history_to_db_if_logged_in
45
+
46
+ # Import utility functions
47
+ try:
48
+ from utils import extract_keyword # Ensure utils.py has the extract_keyword function
49
+ except ImportError:
50
+ st.error("Could not import utility functions. Please ensure utils.py exists and contains required functions.")
51
+ def extract_keyword(text, keywords):
52
+ # Dummy implementation if utils.py is missing
53
+ for kw in keywords:
54
+ if kw.lower() in text.lower():
55
+ return kw
56
+ return "Unknown"
57
+
58
 
59
  # Load environment variables from .env.local for local development.
60
  # On Hugging Face Spaces, st.secrets will be used.
 
71
  GROQ_API_KEY = st.secrets.get("GROQ_API_KEY")
72
  if not GROQ_API_KEY:
73
  # Fallback to environment variable for local development if not in secrets
 
 
 
 
74
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
75
  if GROQ_API_KEY:
76
  groq_client = Groq(api_key=GROQ_API_KEY)
 
112
  # Ensure user_id is initialized for saving history
113
  if "user_id" not in st.session_state:
114
  st.session_state.user_id = None
115
+ if "logged_in_user" not in st.session_state: # This will hold the user_id after successful login
116
  st.session_state.logged_in_user = None
117
 
118
  # --- HARDCODED DATA LOADING FROM CSVs ---
 
124
  description_df = pd.read_csv('symptom_Description.csv').fillna('')
125
  precaution_df = pd.read_csv('symptom_precaution.csv').fillna('')
126
  severity_df = pd.read_csv('Symptom-severity.csv').fillna('') # Load symptom severity
127
+
128
  # Prepare data for quick lookup
129
  # Dataset mapping diseases to their symptoms
130
  disease_symptoms_map = {}
 
133
  # Get all symptoms for this disease, filtering out empty strings and 'Disease' column itself
134
  symptoms = [s.strip().replace('_', ' ') for s in row.values[1:] if s.strip()]
135
  disease_symptoms_map[disease] = symptoms
136
+
137
  # Disease descriptions map
138
  disease_description_map = {row['Disease']: row['Description'] for index, row in description_df.iterrows()}
139
+
140
  # Disease precautions map
141
  disease_precaution_map = {row['Disease']: [p.strip() for p in row.values[1:] if p.strip()] for index, row in precaution_df.iterrows()}
142
+
143
  # Symptom severity map
144
  # Ensure symptom names are consistent (e.g., lowercase and spaces instead of underscores)
145
  symptom_severity_map = {row['Symptom'].strip().replace('_', ' ').lower(): row['weight'] for index, row in severity_df.iterrows()}
146
+
147
  # Extract all unique symptoms for the multiselect
148
  all_unique_symptoms = sorted(list(set(symptom for symptoms_list in disease_symptoms_map.values() for symptom in symptoms_list)))
149
  return disease_symptoms_map, disease_description_map, disease_precaution_map, all_unique_symptoms, symptom_severity_map
 
176
  margin-bottom: 2rem;
177
  background-color: #ffffff; /* White background */
178
  }
 
179
  /* Headers */
180
  h1, h2, h3, h4, h5, h6 {
181
  color: #004d99; /* A professional darker blue */
 
186
  h1 { font-size: 2.8em; text-align: center; color: #003366; } /* Larger, darker blue for main title */
187
  h2 { font-size: 2.2em; border-bottom: 2px solid #e0e7ee; padding-bottom: 0.5em; margin-bottom: 1em; } /* Subtle line under h2 */
188
  h3 { font-size: 1.6em; }
 
189
  /* Main Tabs Styling (Home, History, Feedback, About, Insights) */
190
  .stTabs [data-baseweb="tab-list"] {
191
  gap: 20px; /* More space between tabs */
 
228
  outline: none !important;
229
  box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25) !important; /* Subtle focus glow if needed */
230
  }
 
231
  /* Nested Tabs Styling (Symptom Checker, Chat with MediBot) */
232
  /* This targets tabs that are children of other tabs, making them smaller */
233
  .stTabs [data-baseweb="tab-list"]:has(.stTabs [data-baseweb="tab-list"]) [data-baseweb="tab"],
 
260
  box-shadow: 0 2px 8px rgba(0,0,0,0.15); /* Subtle shadow for selected sub-tab */
261
  outline: none !important; /* Ensure no outline when selected */
262
  }
 
263
  /* General Button Styling */
264
  .stButton>button {
265
  background-color: #007bff; /* Primary blue button */
266
  color: white;
267
+ border-radius: 10px; /* More rounded */
268
  padding: 12px 28px; /* More padding */
269
  font-weight: 600; /* Semi-bold */
270
  border: none;
271
  cursor: pointer;
272
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1); /* Softer, more diffuse shadow */
273
  transition: all 0.3s ease-in-out; /* Smooth transitions */
274
  font-size: 1.05em; /* Slightly larger text */
275
  outline: none; /* Remove outline on focus */
 
278
  }
279
  .stButton>button:hover {
280
  background-color: #0056b3; /* Darker blue on hover */
281
+ box-shadow: 0 8px 20px rgba(0,0,0,0.15); /* Larger shadow on hover */
282
  transform: translateY(-3px); /* More pronounced lift */
283
  outline: none; /* Ensure no outline on hover either */
284
  }
 
287
  width: 100%; /* Full width buttons in sidebar */
288
  margin-bottom: 10px;
289
  }
 
 
290
  /* Input Fields, Selects, etc. */
291
  .stTextInput>div>div>input,
292
  .stTextArea>div>div>textarea, /* Target textarea as well */
293
  .stDateInput>div>div>input,
294
  .stMultiSelect>div>div {
295
+ border-radius: 10px; /* More rounded */
296
  border: 1px solid #ced4da; /* Light grey border */
297
  padding: 10px 15px; /* More padding */
298
+ box-shadow: inset 0 1px 4px rgba(0,0,0,0.05); /* Inner shadow */
299
  transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
300
  background-color: #fefefe; /* Slightly off-white background */
301
  outline: none; /* Remove default outline */
 
319
  /* Expander (for disease info) */
320
  .stExpander {
321
  border: 1px solid #e0e7ee; /* Lighter grey border */
322
+ border-radius: 12px; /* More rounded */
323
  margin-bottom: 15px; /* More space below */
324
  background-color: #ffffff; /* White background */
325
+ box-shadow: 0 4px 12px rgba(0,0,0,0.05); /* Softer shadow */
326
  overflow: hidden; /* Ensure rounded corners clip content */
327
  }
328
  .stExpander > div > div > .streamlit-expanderHeader {
 
374
  .stAlert.st-error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
375
  /* Spinners */
376
  .stSpinner { color: #007bff; } /* Spinner color matching primary button */
 
377
  /* Disclaimer Box - using a more reliable target */
378
  div[data-testid="stVerticalBlock"] > div:first-child > div:has(p > strong:contains("Disclaimer")) {
379
  background-color: #fff8e1; /* Light yellow for warnings/disclaimers */
 
388
  .stAlert[data-testid="stAlert"] p strong:contains("Important Disclaimer") {
389
  color: #664d03;
390
  }
 
391
  /* General text improvements */
392
  p {
393
  line-height: 1.7;
 
404
  .stExpander > div > div > .streamlit-expanderHeader {
405
  font-size: 1.2em; /* Increased font size */
406
  }
 
407
  /* Media queries for specific mobile adjustments if needed (example) */
408
  @media (max-width: 768px) {
409
  h1 {
 
428
  padding: 3px 8px !important;
429
  }
430
  }
431
+ </style>""", unsafe_allow_html=True)
 
432
 
433
  # --- Groq API Response Function (updated system prompt) ---
434
  def get_groq_response(user_query, severity_label="Undetermined Severity", model="llama-3.3-70b-versatile"):
 
438
  """
439
  if not GROQ_AVAILABLE or groq_client is None:
440
  return "AI assistant is not available."
 
441
  # Augment the system prompt with severity information from clinical diagnosis model
442
  # Note: 'severity_label' comes from the symptom checker. If asking a direct question,
443
  # it might default to "Undetermined Severity" unless explicitly passed from a prior analysis.
 
483
  if not HF_MODEL_AVAILABLE or not HF_API_TOKEN:
484
  st.warning("Hugging Face API key not available. Cannot assess severity.")
485
  return "Severity Assessment Unavailable (API Key Missing)", []
 
486
  API_URL = "https://api-inference.huggingface.co/models/DATEXIS/CORe-clinical-diagnosis-prediction"
487
  headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
488
  payload = {"inputs": symptoms_text}
 
490
  response = requests.post(API_URL, headers=headers, json=payload)
491
  response.raise_for_status()
492
  result = response.json()
 
493
  raw_predicted_diagnoses = []
494
  threshold = 0.5
495
  if result and isinstance(result, list) and len(result) > 0 and isinstance(result[0], list):
 
514
  'unlikely', 'possible', 'likely',
515
  'symptom', 'sign', 'pain', 'fever', 'cough', 'vomiting', 'nausea', 'rash', 'headache', 'fatigue', 'diarrhea', 'sore throat', 'hemorrhage'
516
  ]
 
517
  for diagnosis_label in raw_predicted_diagnoses:
518
  lower_diag = diagnosis_label.lower()
519
  is_generic = False
 
525
  is_generic = True
526
  if len(lower_diag.split()) <= 2 and lower_diag in ['pain', 'fever', 'cough', 'vomiting', 'nausea', 'rash', 'headache', 'fatigue', 'diarrhea', 'sore throat', 'hemorrhage']:
527
  is_generic = True
 
528
  if not is_generic:
529
  filtered_diagnoses.append(diagnosis_label)
530
 
 
551
  overall_severity = "Moderate Severity"
552
  # Keep looping to see if a High Severity diagnosis is found later in the filtered list
553
  # Don't return here if a Moderate is found, continue to check for High
 
554
  return f"Overall Symptom Severity (AI Assessment): {overall_severity}", filtered_diagnoses
 
555
  except requests.exceptions.RequestException as e:
556
  st.error(f"Network or API Error during clinical diagnosis model call: {e}. Check API key and internet connection.")
557
  return "Severity Assessment Unavailable (Network Error)", []
 
569
  c.setFont("Helvetica", 10)
570
  c.drawString(50, height - 70, f"Report Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
571
  y = height - 100
 
572
  for entry in history_data:
573
  # history_data comes as list of tuples: (history_id, symptoms, predicted_diseases, query_timestamp)
574
  timestamp, symptoms_text, predicted_diseases = entry[3], entry[1], entry[2]
 
575
  if y < 100:
576
  c.showPage()
577
  c.setFont("Helvetica-Bold", 16)
 
583
  c.drawString(50, y, f"Timestamp: {timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
584
  y -= 15
585
  c.setFont("Helvetica", 10)
 
586
  symptoms_line = f"Symptoms/Question: {symptoms_text}"
587
  insight_line = f"Insight/Response: {predicted_diseases}"
588
 
 
619
  draw_wrapped_text(textobject, insight_line, width - 100)
620
  c.drawText(textobject)
621
  y -= 20
 
622
  c.save()
623
  buffer.seek(0)
624
  return buffer
 
688
  if "feedback_input" not in st.session_state:
689
  st.session_state.feedback_input = ""
690
 
 
691
  if st.session_state.show_welcome:
692
  # A more visually appealing welcome page
693
  st.markdown("<h1 style='color: #0056b3; font-size: 3.5em; margin-top: 50px; text-shadow: 2px 2px 4px rgba(0,0,0,0.1);'>MediBot - Your Personal Health Assistant 🏥</h1>", unsafe_allow_html=True)
 
728
  "</p>"
729
  "</div>", unsafe_allow_html=True)
730
 
731
+ # Sidebar for User Management
732
  with st.sidebar:
733
  st.header("User Management")
734
+ # Call the refactored user management function, passing st.session_state
735
+ render_user_management_sidebar(st.session_state)
736
+
737
  st.markdown("---")
738
  st.header("Navigation")
739
  # Add a button to clear chat history
 
742
  st.session_state.last_chat_response = ""
743
  st.session_state.chat_input_value = ""
744
  st.success("Chat history cleared!")
745
+
746
  # PDF Download Button - available if user is logged in and has history
747
+ if st.session_state.get("user_id"):
748
+ user_history = get_user_history(st.session_state["user_id"]) # Directly call get_user_history
749
  if user_history:
750
  pdf_buffer = generate_pdf_report(user_history)
751
  st.download_button(
 
757
  )
758
  else:
759
  st.info("No history to download yet. Interact with MediBot to generate a report.")
760
+ else:
761
+ st.info("Log in to download your health report.")
762
 
763
  st.markdown("<h1 style='text-align: center; color: #0056b3;'>MediBot - Your Health Assistant 🩺</h1>", unsafe_allow_html=True)
 
764
  # Top-level tabs: Home, History, Feedback, About, Insights
765
  # Re-ordered tabs slightly to put Insights closer to History
766
  main_tab_home, main_tab_history, main_tab_insights, main_tab_feedback, main_tab_about = st.tabs(["Home", "History", "Insights", "Feedback", "About"])
 
853
  predicted_diseases_str_for_history = f"{severity_assessment_text.replace('Overall Symptom Severity (AI Assessment): ', '')}. No specific insights found."
854
 
855
  if st.session_state.get("user_id"):
856
+ # Call the refactored save history function
857
+ save_history_to_db_if_logged_in(st.session_state.user_id, ", ".join(selected_symptoms), predicted_diseases_str_for_history)
 
858
  st.success("Analysis saved to your history.")
859
  else:
860
  st.info("Log in to save this analysis to your history.")
 
877
  # For the chatbot, if it's a direct question, severity is undetermined
878
  # unless passed from a preceding symptom check.
879
  # For simplicity, if not from symptom checker, pass "Undetermined Severity".
 
880
  severity_for_groq_prompt = "Undetermined Severity"
 
 
 
881
  with st.spinner("MediBot is thinking..."):
882
  groq_answer = get_groq_response(user_question, severity_label=severity_for_groq_prompt)
883
  st.markdown("**MediBot's Answer:**")
884
  st.write(groq_answer)
 
885
  # Save Q&A to chat history (if desired, not necessarily DB)
886
  st.session_state.chat_history.append({"user": user_question, "bot": groq_answer})
887
 
888
  # Save to database history if logged in
889
  if st.session_state.get("user_id"):
890
  # Prepend "Question: " to the input for history tracking
891
+ save_history_to_db_if_logged_in(st.session_state.user_id, f"Question: {user_question}", groq_answer)
892
  st.success("Your question and answer have been saved to history.")
893
  else:
894
  st.info("Log in to save this Q&A to your history.")
 
901
  if user_id:
902
  st.info("This section shows your saved symptom analyses and health questions.")
903
  history_data = get_user_history(user_id) # Fetch history from database
 
904
  if history_data:
905
  st.subheader("Past Interactions:")
906
  # Display history in reverse chronological order using expanders
907
  for entry in reversed(history_data):
908
  timestamp, symptoms_text, insight_text = entry[3], entry[1], entry[2]
 
909
  # Determine summary title based on the nature of the input
910
  summary_title_prefix = ""
911
  expander_icon = "" # Initialize icon here
 
937
  expander_icon = "🩺"
938
 
939
  # MODIFIED LINE: Use Markdown heading (e.g., ###) for larger font and bolding
 
940
  expander_label = f"### **{timestamp.strftime('%Y-%m-%d %H:%M')}** - {expander_icon} *{summary_title_prefix}*"
941
  with st.expander(expander_label):
942
  st.write(f"**Your Input:** {symptoms_text}")
 
968
  entry for entry in history_data
969
  if entry[3] and entry[3] > thirty_days_ago # entry[3] is query_timestamp
970
  ]
 
971
  if recent_history:
972
  all_conditions = []
973
  for entry in recent_history:
 
1035
  st.markdown("[Learn more about Hugging Face](https://huggingface.co/)")
1036
 
1037
  st.markdown("---")
1038
+ st.markdown("For professional medical advice, always consult a qualified healthcare provider.")