hassan773 commited on
Commit
83233e7
Β·
verified Β·
1 Parent(s): a0aa324

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +208 -67
app.py CHANGED
@@ -1,76 +1,217 @@
1
  import streamlit as st
2
  import pandas as pd
3
  import numpy as np
 
 
 
 
 
 
 
 
4
  import folium
5
  from streamlit_folium import st_folium
6
  from streamlit_geolocation import streamlit_geolocation
7
- import requests
8
- import json
9
-
10
- # --- 1. THE ADVANCED LIVE MAP ENGINE ---
11
- def render_live_map():
12
- st.markdown('<div class="clinical-card"><h3>πŸ“ Live Geo-Medical Intel</h3><p>Detect your location to find real-time hospitals and clinics nearby.</p></div>', unsafe_allow_html=True)
13
-
14
- # 1. Capture User Coordinates
15
- location = streamlit_geolocation()
16
-
17
- if location and location.get("latitude"):
18
- lat = location["latitude"]
19
- lon = location["longitude"]
20
-
21
- st.success(f"Location Detected: {lat:.4f}, {lon:.4f}")
22
-
23
- # 2. Fetch Live Data from OpenStreetMap (Overpass API)
24
- # We search for 'hospital' and 'clinic' within a 5000m radius
25
- with st.spinner("Searching for nearby medical facilities..."):
26
- overpass_url = "http://overpass-api.de/api/interpreter"
27
- overpass_query = f"""
28
- [out:json];
29
- (
30
- node["amenity"="hospital"](around:5000,{lat},{lon});
31
- node["amenity"="clinic"](around:5000,{lat},{lon});
32
- );
33
- out body;
34
- """
35
- response = requests.get(overpass_url, params={'data': overpass_query})
36
- data = response.json()
37
-
38
- # 3. Process Data & Create Map
39
- m = folium.Map(location=[lat, lon], zoom_start=14)
40
-
41
- # Add User Marker
42
- folium.Marker(
43
- [lat, lon],
44
- popup="You are here",
45
- icon=folium.Icon(color="blue", icon="user")
46
- ).add_to(m)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
- hosp_list = []
49
- for element in data.get('elements', []):
50
- h_lat = element['lat']
51
- h_lon = element['lon']
52
- name = element.get('tags', {}).get('name', 'Unnamed Medical Facility')
53
- hosp_list.append({"Name": name, "Lat": h_lat, "Lon": h_lon})
54
-
55
- folium.Marker(
56
- [h_lat, h_lon],
57
- popup=name,
58
- icon=folium.Icon(color="red", icon="plus-sign")
59
- ).add_to(m)
60
 
61
- # 4. Display Map and Data Table
62
- st_folium(m, width=1200, height=500)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- if hosp_list:
65
- st.markdown("#### πŸ“‹ Found Facilities")
66
- st.dataframe(pd.DataFrame(hosp_list))
67
- else:
68
- st.info("No hospitals found within 5km of your location.")
69
-
70
- else:
71
- st.info("Please click the button above to allow location access.")
72
-
73
- # --- 2. INTEGRATION INTO YOUR APP ---
74
- # Replace your previous "πŸ“ Nearby Hospitals" logic with this:
75
- if nav == "πŸ“ Nearby Hospitals":
76
- render_live_map()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
  import pandas as pd
3
  import numpy as np
4
+ import os
5
+ import hashlib
6
+ import requests
7
+ from datetime import datetime
8
+ from PIL import Image
9
+ from fpdf import FPDF
10
+ import plotly.graph_objects as go
11
+ import google.generativeai as genai
12
  import folium
13
  from streamlit_folium import st_folium
14
  from streamlit_geolocation import streamlit_geolocation
15
+ from groq import Groq
16
+
17
+ # --- 1. CORE SYSTEM CONFIG ---
18
+ GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
19
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
20
+
21
+ if not GROQ_API_KEY or not GOOGLE_API_KEY:
22
+ st.error("⚠️ API Keys Missing! Please set them in Space Secrets.")
23
+ st.stop()
24
+
25
+ genai.configure(api_key=GOOGLE_API_KEY)
26
+ vision_model = genai.GenerativeModel('gemini-2.0-flash')
27
+
28
+ st.set_page_config(page_title="IntelliCare Portal | Hassan Naseer", layout="wide", page_icon="πŸ₯")
29
+
30
+ # --- 2. THEME & PDF GENERATOR ---
31
+ if "theme" not in st.session_state: st.session_state.theme = "Light"
32
+
33
+ def generate_medical_pdf(chat_history, username):
34
+ pdf = FPDF()
35
+ pdf.add_page()
36
+ pdf.set_font("Arial", 'B', 16)
37
+ pdf.cell(200, 10, txt="IntelliCare Portal - Clinical Summary", ln=True, align='C')
38
+ pdf.set_font("Arial", size=10)
39
+ pdf.cell(200, 10, txt=f"Patient: {username} | Date: {datetime.now().strftime('%Y-%m-%d %H:%M')}", ln=True, align='C')
40
+ pdf.ln(10)
41
+ for msg in chat_history:
42
+ role = "PATIENT" if msg["role"] == "user" else "CLINICAL AI"
43
+ pdf.set_font("Arial", 'B', 10)
44
+ pdf.cell(0, 10, txt=f"{role}:", ln=True)
45
+ pdf.set_font("Arial", size=10)
46
+ pdf.multi_cell(0, 8, txt=msg["content"])
47
+ pdf.ln(4)
48
+ pdf.ln(10)
49
+ pdf.set_font("Arial", 'I', 8)
50
+ pdf.multi_cell(0, 5, txt="DISCLAIMER: This is an AI-generated summary for information purposes only. Consult a human professional for definitive diagnosis.")
51
+ return pdf.output(dest='S').encode('latin-1')
52
+
53
+ def inject_theme():
54
+ bg = "#000000" if st.session_state.theme == "Dark" else "#FFFFFF"
55
+ txt = "#FFFFFF" if st.session_state.theme == "Dark" else "#000000"
56
+ card = "#111111" if st.session_state.theme == "Dark" else "#F0F2F6"
57
+ brd = "#FFFFFF" if st.session_state.theme == "Dark" else "#000000"
58
+ st.markdown(f"""<style>.stApp {{ background-color: {bg} !important; color: {txt} !important; }}
59
+ div[data-baseweb="input"], div[data-baseweb="textarea"], select {{ border: 2px solid {brd} !important; border-radius: 10px !important; }}
60
+ .chat-bubble {{ padding: 15px; border-radius: 15px; margin-bottom: 10px; border: 1px solid {brd}; }}
61
+ .user-msg {{ background-color: rgba(59, 130, 246, 0.1); border-left: 5px solid #3b82f6; }}
62
+ .ai-msg {{ background-color: rgba(16, 185, 129, 0.1); border-left: 5px solid #10b981; }}
63
+ .clinical-card {{ background-color: {card}; border: 2px solid {brd}; padding: 25px; border-radius: 15px; color: {txt}; }}
64
+ [data-testid="stSidebar"] * {{ color: {txt} !important; }}</style>""", unsafe_allow_html=True)
65
+
66
+ inject_theme()
67
+
68
+ # --- 3. DATA PERSISTENCE ---
69
+ USER_DB, HISTORY_DB = "users_secure.csv", "clinical_history.csv"
70
+ def hash_pass(pwd): return hashlib.sha256(str.encode(pwd)).hexdigest()
71
+ def load_db(file, cols):
72
+ if os.path.exists(file): return pd.read_csv(file)
73
+ return pd.DataFrame(columns=cols)
74
+
75
+ def get_vector_db():
76
+ import chromadb
77
+ from chromadb.utils import embedding_functions
78
+ client = chromadb.PersistentClient(path="./med_vector_db")
79
+ emb_fn = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="paraphrase-MiniLM-L3-v2")
80
+ main = client.get_or_create_collection(name="intellicare_vFinal", embedding_function=emb_fn)
81
+ signal = client.get_or_create_collection(name="signals_targeted", embedding_function=emb_fn)
82
+ return main, signal
83
+
84
+ # --- 4. AUTHENTICATION ---
85
+ if "logged_in" not in st.session_state: st.session_state.logged_in = False
86
+ if "last_processed_audio" not in st.session_state: st.session_state.last_processed_audio = None
87
+
88
+ if not st.session_state.logged_in:
89
+ st.markdown("<h1 style='text-align: center;'>πŸ₯ IntelliCare Portal</h1>", unsafe_allow_html=True)
90
+ c2 = st.columns([1, 2, 1])[1]
91
+ with c2:
92
+ tab1, tab2 = st.tabs(["πŸ” Login", "πŸ“ Create Account"])
93
+ with tab1:
94
+ u, p = st.text_input("Username", key="l_u"), st.text_input("Password", type="password", key="l_p")
95
+ if st.button("Sign In"):
96
+ users = load_db(USER_DB, ["username", "password", "role"])
97
+ match = users[(users['username'] == u) & (users['password'] == hash_pass(p))]
98
+ if not match.empty:
99
+ st.session_state.logged_in, st.session_state.username = True, u
100
+ st.session_state.role, st.session_state.msgs = match.iloc[0]['role'], []
101
+ st.rerun()
102
+ with tab2:
103
+ nu, np, nr = st.text_input("New User"), st.text_input("New Pass", type="password"), st.selectbox("Role", ["Patient", "Doctor"])
104
+ if st.button("Register"):
105
+ df = load_db(USER_DB, ["username", "password", "role"])
106
+ if nu not in df['username'].values:
107
+ pd.concat([df, pd.DataFrame([{"username": nu, "password": hash_pass(np), "role": nr}])]).to_csv(USER_DB, index=False)
108
+ st.success("βœ… Registered!")
109
+ st.stop()
110
+
111
+ # --- 5. SIDEBAR NAVIGATION ---
112
+ with st.sidebar:
113
+ st.markdown(f"### πŸ‘€ {st.session_state.username} ({st.session_state.role})")
114
+ if st.button("πŸŒ“ Toggle Theme"):
115
+ st.session_state.theme = "Dark" if st.session_state.theme == "Light" else "Light"
116
+ st.rerun()
117
+ if st.button("Logout"): st.session_state.logged_in = False; st.rerun()
118
+ st.divider()
119
+ nav = st.radio("Menu", ["πŸ’¬ AI Chat", "πŸ§ͺ Health Lab", "πŸ“ Nearby Hospitals", "πŸ“ž Video Call", "πŸ“œ History"]) if st.session_state.role == "Patient" else st.radio("Menu", ["πŸ–₯️ Consultation Desk", "πŸ“‹ Patient Records"])
120
+
121
+ # --- 6. PATIENT PORTAL (STRICT MEDICAL AI) ---
122
+ if st.session_state.role == "Patient":
123
+ if nav == "πŸ’¬ AI Chat":
124
+ st.markdown('<div class="clinical-card"><h3>πŸ’¬ Clinical AI Specialist</h3></div>', unsafe_allow_html=True)
125
+ for m in st.session_state.msgs:
126
+ bubble = "user-msg" if m["role"] == "user" else "ai-msg"
127
+ st.markdown(f'<div class="chat-bubble {bubble}"><b>{m["role"].upper()}:</b><br>{m["content"]}</div>', unsafe_allow_html=True)
128
 
129
+ q = st.chat_input("Explain symptoms...")
130
+ c1, c2, c3 = st.columns(3)
131
+ with c1: v = st.audio_input("🎀 Voice", key=f"v_{len(st.session_state.msgs)}")
132
+ with c2: up_pdf = st.file_uploader("πŸ“„ PDF", type=['pdf'], key=f"p_{len(st.session_state.msgs)}")
133
+ with c3: up_img = st.file_uploader("πŸ“Έ Scan", type=['png', 'jpg'], key=f"i_{len(st.session_state.msgs)}")
 
 
 
 
 
 
 
134
 
135
+ final_q = q if q else None
136
+ if up_pdf:
137
+ import pdfplumber
138
+ with pdfplumber.open(up_pdf) as f: final_q = "Analyze these medical records: " + " ".join([p.extract_text() for p in f.pages if p.extract_text()])
139
+ elif up_img:
140
+ res = vision_model.generate_content(["Extract clinical data from this medical image:", Image.open(up_img)])
141
+ final_q = "Medical Image Data: " + res.text
142
+ elif v:
143
+ v_hash = hashlib.md5(v.getvalue()).hexdigest()
144
+ if v_hash != st.session_state.last_processed_audio:
145
+ final_q = Groq(api_key=GROQ_API_KEY).audio.transcriptions.create(file=("a.wav", v.getvalue()), model="whisper-large-v3", response_format="text")
146
+ st.session_state.last_processed_audio = v_hash
147
+
148
+ if final_q:
149
+ st.session_state.msgs.append({"role": "user", "content": final_q})
150
+ # --- STRICT MEDICAL GUARDRAIL ---
151
+ sys_prompt = "You are a specialized Clinical AI. Only answer medical, health, and pharmacological questions. Politely decline non-medical topics. Be precise and ground answers in provided context."
152
+ main_col, _ = get_vector_db()
153
+ res = main_col.query(query_texts=[final_q], n_results=1)
154
+ ctx = res['documents'][0][0] if (res.get('documents') and len(res['documents'][0]) > 0) else "N/A"
155
+ ans = Groq(api_key=GROQ_API_KEY).chat.completions.create(model="llama-3.3-70b-versatile", messages=[{"role": "system", "content": sys_prompt}, {"role": "system", "content": f"Context: {ctx}"}] + st.session_state.msgs)
156
+ st.session_state.msgs.append({"role": "assistant", "content": ans.choices[0].message.content})
157
+ st.rerun()
158
+
159
+ if st.session_state.msgs:
160
+ if st.download_button("πŸ“₯ Download Summary (PDF)", generate_medical_pdf(st.session_state.msgs, st.session_state.username), f"IntelliCare_Summary_{st.session_state.username}.pdf", "application/pdf"):
161
+ st.success("Summary Generated!")
162
+
163
+ elif nav == "πŸ§ͺ Health Lab":
164
+ st.markdown('<div class="clinical-card"><h3>πŸ§ͺ Clinical Diagnostics</h3></div>', unsafe_allow_html=True)
165
+ col1, col2 = st.columns(2)
166
+ with col1:
167
+ w, h_cm = st.number_input("Weight (kg)", 30, 200, 70), st.number_input("Height (cm)", 100, 250, 175)
168
+ bmi = round(w / ((h_cm/100)**2), 1)
169
+ fig_bmi = go.Figure(go.Indicator(mode="gauge+number", value=bmi, domain={'x': [0, 1], 'y': [0, 1]}, gauge={'axis': {'range': [10, 40]}, 'bar': {'color': "#10b981"}, 'steps': [{'range': [10, 18.5], 'color': "lightblue"}, {'range': [25, 40], 'color': "orange"}]}))
170
+ st.plotly_chart(fig_bmi, use_container_width=True)
171
+ with col2:
172
+ hr = st.slider("Heart Rate (BPM)", 40, 180, 72)
173
+ t = np.linspace(0, 2, 200)
174
+ y = np.sin(2 * np.pi * (hr/60) * t) + 0.5 * np.sin(4 * np.pi * (hr/60) * t)
175
+ st.plotly_chart(go.Figure(data=go.Scatter(y=y, mode='lines', line=dict(color='#ff4b4b', width=3))), use_container_width=True)
176
 
177
+
178
+ elif nav == "πŸ“ Nearby Hospitals":
179
+ loc = streamlit_geolocation()
180
+ if loc and loc.get("latitude"):
181
+ lat, lon = loc["latitude"], loc["longitude"]
182
+ query = f'[out:json];node["amenity"~"hospital|clinic"](around:5000,{lat},{lon});out body;'
183
+ data = requests.get("http://overpass-api.de/api/interpreter", params={'data': query}).json()
184
+ m = folium.Map(location=[lat, lon], zoom_start=14)
185
+ folium.Marker([lat, lon], popup="You", icon=folium.Icon(color="blue")).add_to(m)
186
+ for e in data.get('elements', []): folium.Marker([e['lat'], e['lon']], popup=e.get('tags', {}).get('name', 'Clinic'), icon=folium.Icon(color="red")).add_to(m)
187
+ st_folium(m, width=1200, height=500)
188
+ else: st.info("Enable location to scan local clinics.")
189
+
190
+ elif nav == "πŸ“ž Video Call":
191
+ docs = load_db(USER_DB, ["username", "role"])
192
+ sel = st.selectbox("Select Doctor", docs[docs['role'] == 'Doctor']['username'].tolist())
193
+ if st.button("Request Connection"):
194
+ room = f"IntelliCare-{st.session_state.username}-{sel}"
195
+ _, signal_col = get_vector_db()
196
+ signal_col.upsert(documents=[f"CALL|{sel}|{room}|{st.session_state.username}"], ids=["latest"])
197
+ st.session_state.active_room = room
198
+ if "active_room" in st.session_state:
199
+ st.components.v1.html(f'<iframe src="https://meet.jit.si/{st.session_state.active_room}" width="100%" height="550px"></iframe>', height=600)
200
+
201
+ # --- 7. DOCTOR PORTAL ---
202
+ elif st.session_state.role == "Doctor":
203
+ if nav == "πŸ–₯️ Consultation Desk":
204
+ st.markdown('<div class="clinical-card"><h3>πŸ–₯️ Consultation Desk</h3></div>', unsafe_allow_html=True)
205
+ _, signal_col = get_vector_db()
206
+ res = signal_col.get(ids=["latest"])
207
+ if res.get('documents'):
208
+ parts = res['documents'][0].split("|") # CALL|DocName|Room|Patient
209
+ if parts[1] == st.session_state.username:
210
+ st.warning(f"πŸ”” {parts[3]} is requesting a call.")
211
+ if st.button("βœ… Join Call"): st.session_state.active_call = parts[2]
212
+ if "active_call" in st.session_state:
213
+ st.components.v1.html(f'<iframe src="https://meet.jit.si/{parts[2]}" width="100%" height="600px"></iframe>', height=650)
214
+ if st.button("πŸ”΄ End & Archive"):
215
+ df = load_db(HISTORY_DB, ["Time", "Patient", "Doctor", "Status"])
216
+ pd.concat([df, pd.DataFrame([{"Time": datetime.now().strftime("%Y-%m-%d %H:%M"), "Patient": parts[3], "Doctor": st.session_state.username, "Status": "Completed"}])]).to_csv(HISTORY_DB, index=False)
217
+ signal_col.delete(ids=["latest"]); del st.session_state.active_call; st.rerun()