hassan773 commited on
Commit
5861d4b
Β·
verified Β·
1 Parent(s): ebd21d1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -135
app.py CHANGED
@@ -11,189 +11,139 @@ import folium
11
  from streamlit_folium import st_folium
12
  from streamlit_geolocation import streamlit_geolocation
13
 
14
- # --- 1. CORE CONFIG ---
15
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
16
  if not GROQ_API_KEY:
17
- st.error("⚠️ API Key Missing!")
18
  st.stop()
19
 
20
  st.set_page_config(page_title="IntelliCare Portal | Hassan Naseer", layout="wide", page_icon="πŸ₯")
21
 
22
- # --- 2. THE STATIC FRAME ENGINE ---
23
- def inject_static_layout():
24
- st.markdown("""
25
- <style>
26
- @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap');
27
-
28
- /* 1. LOCK THE VIEWPORT */
29
- html, body, [data-testid="stAppViewContainer"] {
30
- overflow: hidden !important;
31
- height: 100vh;
32
- font-family: 'Poppins', sans-serif;
33
- }
34
-
35
- /* 2. STATIC SIDEBAR */
36
- [data-testid="stSidebar"] {
37
- background: linear-gradient(180deg, #0f172a 0%, #1e3a8a 100%);
38
- position: fixed;
39
- height: 100vh;
40
- z-index: 1000;
41
- }
42
- [data-testid="stSidebar"] * { color: #f8fafc !important; }
43
-
44
- /* 3. SCROLLABLE CHAT BOARD */
45
- .chat-board-container {
46
- height: 58vh;
47
- overflow-y: auto;
48
- padding: 20px;
49
- background: #ffffff;
50
- border-radius: 15px;
51
- border: 1px solid #e2e8f0;
52
- display: flex;
53
- flex-direction: column;
54
- margin-top: 10px;
55
- }
56
-
57
- /* 4. STATIC PROMPT BAR (FIXED AT BOTTOM) */
58
- div[data-testid="stChatInput"] {
59
- position: fixed !important;
60
- bottom: 40px !important;
61
- left: 25% !important; /* Adjusted to make room for left icons */
62
- width: 65% !important;
63
- z-index: 999 !important;
64
- border-radius: 30px !important;
65
- border: 2px solid #3b82f6 !important;
66
- background: white !important;
67
- }
68
-
69
- /* Professional Bubbles */
70
- .user-msg {
71
- background: #3b82f6; color: white; padding: 15px;
72
- border-radius: 20px 20px 0 20px; align-self: flex-end;
73
- margin-bottom: 12px; max-width: 80%;
74
- }
75
- .ai-msg {
76
- background: #f8fafc; color: #1e293b; padding: 15px;
77
- border-radius: 20px 20px 20px 0; align-self: flex-start;
78
- margin-bottom: 12px; max-width: 80%; border: 1px solid #e2e8f0;
79
- }
80
- .voice-btn { background: none; border: none; cursor: pointer; float: right; font-size: 1.1rem; }
81
- </style>
82
-
83
- <script>
84
- function speak(text) {
85
- window.speechSynthesis.cancel();
86
- const msg = new SpeechSynthesisUtterance(text);
87
- window.speechSynthesis.speak(msg);
88
- }
89
- </script>
90
- """, unsafe_allow_html=True)
91
-
92
- inject_static_layout()
93
-
94
- # --- 3. SESSION & UTILS ---
95
  USER_DB = "users_secure.csv"
 
 
96
  def hash_pass(pwd): return hashlib.sha256(str.encode(pwd)).hexdigest()
 
97
  def load_db(file, cols):
98
  if os.path.exists(file): return pd.read_csv(file)
99
  return pd.DataFrame(columns=cols)
100
 
 
 
 
 
 
 
101
  if "logged_in" not in st.session_state: st.session_state.logged_in = False
102
  if "msgs" not in st.session_state: st.session_state.msgs = []
103
  if "active_doc" not in st.session_state: st.session_state.active_doc = None
104
  if "last_voice_hash" not in st.session_state: st.session_state.last_voice_hash = None
105
 
106
- # --- 4. AUTHENTICATION ---
 
107
  if not st.session_state.logged_in:
108
- st.markdown("<h1 style='text-align: center; color: #1e3a8a; padding-top: 50px;'>πŸ₯ INTELLICARE PORTAL</h1>", unsafe_allow_html=True)
109
- c2 = st.columns([1, 2, 1])[1]
110
  with c2:
111
- tab1, tab2 = st.tabs(["πŸ” Login", "✨ Register"])
112
  with tab1:
113
  u, p = st.text_input("Username"), st.text_input("Password", type="password")
114
- if st.button("SIGN IN"):
115
  db = load_db(USER_DB, ["username", "password", "role"])
116
  match = db[(db['username'] == u) & (db['password'] == hash_pass(p))]
117
  if not match.empty:
118
  st.session_state.logged_in, st.session_state.username = True, u
119
  st.session_state.role = match.iloc[0]['role']
120
  st.rerun()
 
 
121
  with tab2:
122
- nu, np, nr = st.text_input("New ID"), st.text_input("New Pass", type="password"), st.selectbox("I am a:", ["Patient", "Doctor"])
123
- if st.button("REGISTER"):
124
  df = load_db(USER_DB, ["username", "password", "role"])
125
- if nu not in df['username'].values:
 
126
  pd.concat([df, pd.DataFrame([{"username": nu, "password": hash_pass(np), "role": nr}])]).to_csv(USER_DB, index=False)
127
- st.success("Registered!")
128
  st.stop()
129
 
130
- # --- 5. STATIC SIDEBAR ---
131
  with st.sidebar:
132
- st.markdown(f"### πŸ‘€ {st.session_state.username}\n**{st.session_state.role} Dashboard**")
133
- if st.button("πŸšͺ Logout"): st.session_state.logged_in = False; st.rerun()
134
  st.divider()
135
- nav = st.radio("Navigation", ["πŸ’¬ AI Chat Board", "πŸ§ͺ Health Lab", "πŸ“ Nearby Clinics", "πŸ“ž Video Consult"])
136
-
137
- # --- 6. AI CHAT BOARD (MIC & PLUS ON LEFT) ---
138
- if nav == "πŸ’¬ AI Chat Board":
139
- st.markdown("### πŸ’¬ Clinical Intelligence Assistant")
140
-
141
- # Scrollable Box
142
- st.markdown('<div class="chat-board-container">', unsafe_allow_html=True)
143
  for m in st.session_state.msgs:
144
- bubble = "user-msg" if m["role"] == "user" else "ai-msg"
145
- icon = f'<button class="voice-btn" onclick="speak(\'{m["content"].replace("'", "")}\')">πŸ”Š</button>' if m["role"] == "assistant" else ""
146
- st.markdown(f'<div class="{bubble}">{icon}{m["content"]}</div>', unsafe_allow_html=True)
147
- st.markdown('</div>', unsafe_allow_html=True)
148
 
149
- # UNIFIED INPUT: Mic and Plus on the LEFT
150
  st.divider()
151
- col_mic, col_plus, col_text = st.columns([0.8, 0.8, 10])
152
-
153
- with col_mic:
154
- v = st.audio_input("🎀", label_visibility="collapsed")
155
-
156
- with col_plus:
157
  with st.popover("βž•"):
158
- up = st.file_uploader("Upload PDF", type=['pdf'])
159
  if up:
160
  with pdfplumber.open(up) as f:
161
- st.session_state.active_doc = " ".join([p.extract_text() for p in f.pages])
162
- st.success("PDF Analyzed")
163
-
164
- with col_text:
165
- q = st.chat_input("Enter clinical query...")
166
 
167
- # AI LOGIC
168
  final_q = None
169
- if q:
170
- final_q = q
171
  elif v:
172
  v_hash = hashlib.md5(v.getvalue()).hexdigest()
173
  if v_hash != st.session_state.last_voice_hash:
174
- final_q = Groq(api_key=GROQ_API_KEY).audio.transcriptions.create(
175
- file=("a.wav", v.getvalue()),
176
- model="whisper-large-v3",
177
- response_format="text"
178
- )
179
- st.session_state.last_voice_hash = v_hash
180
 
181
  if final_q:
182
  st.session_state.msgs.append({"role": "user", "content": final_q})
183
- sys_p = "Medical Assistant. Use PDF data if present. Answer medical queries only."
184
- ans = Groq(api_key=GROQ_API_KEY).chat.completions.create(
185
- model="llama-3.3-70b-versatile",
186
- messages=[{"role": "system", "content": sys_p}, {"role": "system", "content": f"CTX: {st.session_state.active_doc}"}] + st.session_state.msgs
187
- )
188
  st.session_state.msgs.append({"role": "assistant", "content": ans.choices[0].message.content})
 
189
  st.rerun()
190
 
191
- # --- 7. OTHER TOOLS (REMAINS STATIC) ---
192
- elif nav == "πŸ§ͺ Health Lab":
193
- st.markdown("### πŸ§ͺ Diagnostics")
194
- tool = st.selectbox("Tool", ["BMI Analyzer", "Glucose Tracker"])
195
- if tool == "BMI Analyzer":
196
-
197
- w, h = st.number_input("Weight (kg)", 70), st.number_input("Height (cm)", 175)
198
- bmi = round(w / ((h/100)**2), 1)
199
- st.metric("BMI", bmi)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  from streamlit_folium import st_folium
12
  from streamlit_geolocation import streamlit_geolocation
13
 
14
+ # --- 1. CORE SYSTEM CONFIGURATION ---
15
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
16
  if not GROQ_API_KEY:
17
+ st.error("⚠️ API Key Missing! Please set it in your environment secrets.")
18
  st.stop()
19
 
20
  st.set_page_config(page_title="IntelliCare Portal | Hassan Naseer", layout="wide", page_icon="πŸ₯")
21
 
22
+ # --- 2. DATA PERSISTENCE & UTILS ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  USER_DB = "users_secure.csv"
24
+ HISTORY_DB = "clinical_history.csv"
25
+
26
  def hash_pass(pwd): return hashlib.sha256(str.encode(pwd)).hexdigest()
27
+
28
  def load_db(file, cols):
29
  if os.path.exists(file): return pd.read_csv(file)
30
  return pd.DataFrame(columns=cols)
31
 
32
+ def save_history(user, text, role):
33
+ df = load_db(HISTORY_DB, ["Time", "User", "Message", "Role"])
34
+ new_entry = pd.DataFrame([{"Time": datetime.now().strftime("%Y-%m-%d %H:%M"), "User": user, "Message": text, "Role": role}])
35
+ pd.concat([df, new_entry]).to_csv(HISTORY_DB, index=False)
36
+
37
+ # Session Initialization
38
  if "logged_in" not in st.session_state: st.session_state.logged_in = False
39
  if "msgs" not in st.session_state: st.session_state.msgs = []
40
  if "active_doc" not in st.session_state: st.session_state.active_doc = None
41
  if "last_voice_hash" not in st.session_state: st.session_state.last_voice_hash = None
42
 
43
+ # --- 3. AUTHENTICATION PORTAL (Account Creation & Login) ---
44
+
45
  if not st.session_state.logged_in:
46
+ st.markdown("<h1 style='text-align: center;'>πŸ₯ IntelliCare Portal</h1>", unsafe_allow_html=True)
47
+ c1, c2, c3 = st.columns([1, 2, 1])
48
  with c2:
49
+ tab1, tab2 = st.tabs(["πŸ” Login", "πŸ“ Create Account"])
50
  with tab1:
51
  u, p = st.text_input("Username"), st.text_input("Password", type="password")
52
+ if st.button("Sign In"):
53
  db = load_db(USER_DB, ["username", "password", "role"])
54
  match = db[(db['username'] == u) & (db['password'] == hash_pass(p))]
55
  if not match.empty:
56
  st.session_state.logged_in, st.session_state.username = True, u
57
  st.session_state.role = match.iloc[0]['role']
58
  st.rerun()
59
+ else:
60
+ st.error("Invalid credentials.")
61
  with tab2:
62
+ nu, np, nr = st.text_input("New ID"), st.text_input("New Pass", type="password"), st.selectbox("Role", ["Patient", "Doctor"])
63
+ if st.button("Register Account"):
64
  df = load_db(USER_DB, ["username", "password", "role"])
65
+ if nu in df['username'].values: st.warning("User already exists.")
66
+ else:
67
  pd.concat([df, pd.DataFrame([{"username": nu, "password": hash_pass(np), "role": nr}])]).to_csv(USER_DB, index=False)
68
+ st.success("Account Registered! Please Login.")
69
  st.stop()
70
 
71
+ # --- 4. SIDEBAR NAVIGATION ---
72
  with st.sidebar:
73
+ st.markdown(f"### πŸ‘€ {st.session_state.username} ({st.session_state.role})")
74
+ if st.button("Logout Session"): st.session_state.logged_in = False; st.rerun()
75
  st.divider()
76
+ if st.session_state.role == "Patient":
77
+ nav = st.radio("Menu", ["πŸ’¬ AI Chat", "πŸ§ͺ Health Lab", "πŸ“ Nearby Clinics", "πŸ“ž Video Consult", "πŸ“œ My History"])
78
+ else:
79
+ nav = st.radio("Menu", ["πŸ–₯️ Consultation Desk", "πŸ“‹ Patient Archive", "πŸ“œ Session Logs"])
80
+
81
+ # --- 5. PATIENT PORTAL MODULES ---
82
+ if nav == "πŸ’¬ AI Chat":
83
+ st.markdown("### πŸ’¬ Clinical AI Assistant")
84
  for m in st.session_state.msgs:
85
+ role_label = "πŸ§‘β€πŸ’» Patient" if m["role"] == "user" else "πŸ€– AI Assistant"
86
+ st.write(f"**{role_label}:** {m['content']}")
 
 
87
 
 
88
  st.divider()
89
+ # MIC AND PLUS ON LEFT OF PROMPT
90
+ col_v, col_up, col_q = st.columns([1, 1, 8])
91
+ with col_v: v = st.audio_input("🎀", label_visibility="collapsed")
92
+ with col_up:
 
 
93
  with st.popover("βž•"):
94
+ up = st.file_uploader("Upload Medical PDF", type=['pdf'])
95
  if up:
96
  with pdfplumber.open(up) as f:
97
+ st.session_state.active_doc = " ".join([p.extract_text() for p in f.pages if p.extract_text()])
98
+ st.success("PDF Context Loaded.")
99
+ with col_q: q = st.chat_input("Ask a clinical query...")
 
 
100
 
101
+ # Unified Processing Logic with Voice-Lock
102
  final_q = None
103
+ if q: final_q = q
 
104
  elif v:
105
  v_hash = hashlib.md5(v.getvalue()).hexdigest()
106
  if v_hash != st.session_state.last_voice_hash:
107
+ try:
108
+ final_q = Groq(api_key=GROQ_API_KEY).audio.transcriptions.create(file=("a.wav", v.getvalue()), model="whisper-large-v3", response_format="text")
109
+ st.session_state.last_voice_hash = v_hash
110
+ except: st.error("Mic error. Please retry.")
 
 
111
 
112
  if final_q:
113
  st.session_state.msgs.append({"role": "user", "content": final_q})
114
+ save_history(st.session_state.username, final_q, "Patient")
115
+ sys_p = "Medical Assistant. Use PDF data if present. Provide professional clinical advice."
116
+ ans = Groq(api_key=GROQ_API_KEY).chat.completions.create(model="llama-3.3-70b-versatile", messages=[{"role": "system", "content": sys_p}, {"role": "system", "content": f"CTX: {st.session_state.active_doc}"}] + st.session_state.msgs)
 
 
117
  st.session_state.msgs.append({"role": "assistant", "content": ans.choices[0].message.content})
118
+ save_history(st.session_state.username, ans.choices[0].message.content, "AI")
119
  st.rerun()
120
 
121
+ elif nav == "πŸ“ž Video Consult":
122
+ st.markdown("### πŸ“ž Specialist Live Call")
123
+ db = load_db(USER_DB, ["username", "role"])
124
+ doctors = db[db['role'] == 'Doctor']['username'].tolist() # Patient sees registered Doctors
125
+ sel_doc = st.selectbox("Select Available Specialist", doctors)
126
+ if st.button("Initialize Meeting"):
127
+ st.session_state.call_room = f"IntelliCare-{st.session_state.username}-{sel_doc}"
128
+ if "call_room" in st.session_state:
129
+ st.components.v1.html(f'<iframe src="https://meet.jit.si/{st.session_state.call_room}" width="100%" height="600px"></iframe>', height=650)
130
+
131
+ # --- 6. DOCTOR PORTAL MODULES ---
132
+ elif nav == "πŸ–₯️ Consultation Desk":
133
+ st.markdown("### πŸ–₯️ Doctor Consultation Desk")
134
+ room_id = st.text_input("Enter Meeting ID (Provided by Patient)")
135
+ if st.button("JOIN SESSION"):
136
+ st.components.v1.html(f'<iframe src="https://meet.jit.si/{room_id}" width="100%" height="600px"></iframe>', height=650)
137
+
138
+ elif nav == "πŸ“‹ Patient Archive":
139
+ st.markdown("### πŸ“‹ Clinical Records Archive")
140
+ st.dataframe(load_db(HISTORY_DB, ["Time", "User", "Message", "Role"]), use_container_width=True)
141
+
142
+ # --- 7. SHARED HISTORY LOGS ---
143
+ elif nav in ["πŸ“œ My History", "πŸ“œ Session Logs"]:
144
+ st.markdown("### πŸ“œ Session History Logs")
145
+ history = load_db(HISTORY_DB, ["Time", "User", "Message", "Role"])
146
+ if st.session_state.role == "Patient":
147
+ st.dataframe(history[history['User'] == st.session_state.username]) # Patients see only their own history
148
+ else:
149
+ st.dataframe(history) # Doctors can view all clinical history