MediScan / app.py
hassan773's picture
Update app.py
c63dce6 verified
import streamlit as st
import pandas as pd
import numpy as np
import os
import hashlib
from datetime import datetime
from groq import Groq
import pdfplumber
import plotly.graph_objects as go
import folium
from streamlit_folium import st_folium
from streamlit_geolocation import streamlit_geolocation
# --- 1. CORE SYSTEM CONFIG ---
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
if not GROQ_API_KEY:
st.error("⚠️ API Key Missing! Please check your environment variables.")
st.stop()
st.set_page_config(page_title="IntelliCare Portal | Hassan Naseer", layout="wide", page_icon="πŸ₯")
# --- 2. PREMIUM CSS ---
st.markdown("""
<style>
[data-testid="stSidebar"] { background: #1e3a8a !important; }
[data-testid="stSidebar"] * { color: #ffffff !important; }
[data-testid="stSidebar"] .stRadio > div { display: none; }
div.stButton > button, div.stDownloadButton > button {
background-color: #2563eb !important; color: white !important;
border: 2px solid #3b82f6 !important; border-radius: 12px !important;
transition: 0.3s; font-weight: bold !important; width: 100% !important;
margin-bottom: 10px !important;
}
div.stButton > button:hover { background-color: #1e40af !important; border-color: #60a5fa !important; box-shadow: 0 0 10px #3b82f6 !important; }
.main .block-container { padding-bottom: 180px; }
.metric-card { background: white; border-radius: 12px; padding: 20px; border-left: 5px solid #2563eb; box-shadow: 0 4px 15px rgba(0,0,0,0.05); color: #1e293b; margin-bottom: 15px; }
.user-msg { background: #3b82f6; color: white; padding: 15px; border-radius: 15px 15px 2px 15px; margin-bottom: 10px; margin-left: 15%; }
.ai-msg { background: #f1f5f9; color: #1e293b; padding: 15px; border-radius: 15px 15px 15px 2px; margin-bottom: 10px; margin-right: 15%; border: 1px solid #e2e8f0; }
</style>
""", unsafe_allow_html=True)
# --- 3. DATABASE HANDLERS ---
USER_DB, CALL_LOG_DB, CALL_SIGNAL_DB, FEEDBACK_DB, RX_DB = "users_secure.csv", "call_history.csv", "active_calls.csv", "feedback.csv", "prescriptions.csv"
def hash_pass(pwd): return hashlib.sha256(str.encode(pwd)).hexdigest()
def load_db(file, cols):
if os.path.exists(file): return pd.read_csv(file)
return pd.DataFrame(columns=cols)
# Session Initialization
if "logged_in" not in st.session_state: st.session_state.logged_in = False
if "msgs" not in st.session_state: st.session_state.msgs = []
if "active_doc" not in st.session_state: st.session_state.active_doc = ""
if "audio_key" not in st.session_state: st.session_state.audio_key = 0
if "nav" not in st.session_state: st.session_state.nav = "Home"
# --- 4. AUTHENTICATION ---
if not st.session_state.logged_in:
st.markdown("<h1 style='text-align: center; color: #1e3a8a;'>πŸ₯ IntelliCare Portal</h1>", unsafe_allow_html=True)
c2 = st.columns([1, 2, 1])[1]
with c2:
tab1, tab2 = st.tabs(["πŸ” Login", "πŸ“ Create Account"])
with tab1:
u, p = st.text_input("Username"), st.text_input("Password", type="password")
if st.button("Sign In"):
db = load_db(USER_DB, ["username", "password", "role"])
match = db[(db['username'] == u) & (db['password'] == hash_pass(p))]
if not match.empty:
st.session_state.logged_in, st.session_state.username, st.session_state.role = True, u, match.iloc[0]['role']
st.session_state.nav = "πŸ“Š Analytics" if st.session_state.role == "Doctor" else "πŸ’¬ AI Chat"
st.rerun()
with tab2:
nu, np = st.text_input("New ID"), st.text_input("New Pass", type="password")
nr = st.selectbox("Role", ["Patient", "Doctor"])
if st.button("Register Account"):
df = load_db(USER_DB, ["username", "password", "role"])
pd.concat([df, pd.DataFrame([{"username": nu, "password": hash_pass(np), "role": nr}])]).to_csv(USER_DB, index=False)
st.success("Registration Successful!")
st.stop()
# --- 5. SIDEBAR ---
with st.sidebar:
st.markdown(f"### πŸ‘€ {st.session_state.username} ({st.session_state.role})")
lang = st.radio("Language", ["English", "Urdu"], label_visibility="collapsed")
st.divider()
if st.session_state.role == "Patient":
if st.button("πŸ’¬ AI Chat"): st.session_state.nav = "πŸ’¬ AI Chat"
if st.button("πŸ’Š My Prescriptions"): st.session_state.nav = "πŸ’Š Prescriptions"
if st.button("πŸ§ͺ Health Lab"): st.session_state.nav = "πŸ§ͺ Health Lab"
if st.button("πŸ“ Nearby Clinics"): st.session_state.nav = "πŸ“ Clinics"
if st.button("πŸ“ž Video Call"): st.session_state.nav = "πŸ“ž Video"
if st.button("⭐ Give Feedback"): st.session_state.nav = "⭐ Feedback"
else:
if st.button("πŸ“Š Clinical Analytics"): st.session_state.nav = "πŸ“Š Analytics"
if st.button("πŸ–₯️ Consultation Desk"): st.session_state.nav = "πŸ–₯️ Desk"
if st.button("✍️ Issue Prescription"): st.session_state.nav = "✍️ Prescription"
if st.button("πŸ“‚ AI Summarizer"): st.session_state.nav = "πŸ“‚ Summarizer"
st.divider()
if st.button("πŸ—‘οΈ Clear Chat"):
st.session_state.msgs, st.session_state.active_doc = [], ""
st.rerun()
if st.button("πŸšͺ Logout"): st.session_state.logged_in = False; st.rerun()
# --- 6. DOCTOR PORTAL (LIVE ANALYTICS) ---
if st.session_state.nav == "πŸ“Š Analytics":
st.markdown("### πŸ“Š Live Physician Practice Analytics")
u_db = load_db(USER_DB, ["username", "role"])
f_db = load_db(FEEDBACK_DB, ["Doctor", "Rating"])
total_patients = len(u_db[u_db['role'] == 'Patient'])
my_rating = f_db[f_db['Doctor'] == st.session_state.username]['Rating'].mean() if not f_db.empty else 5.0
c1, c2, c3 = st.columns(3)
with c1: st.markdown(f'<div class="metric-card"><b>Registered Patients</b><br><h2>{total_patients}</h2></div>', unsafe_allow_html=True)
with c2: st.markdown(f'<div class="metric-card"><b>Average Rating</b><br><h2>{my_rating:.1f}/5</h2></div>', unsafe_allow_html=True)
with c3: st.markdown('<div class="metric-card"><b>Server Status</b><br><h2>Online</h2></div>', unsafe_allow_html=True)
g1, g2 = st.columns(2)
with g1:
st.plotly_chart(go.Figure(go.Indicator(mode="gauge+number", value=total_patients, gauge={'axis': {'range': [0, 100]}, 'bar': {'color': "#1e3a8a"}}, title={'text': "Total Patient Registry"})), use_container_width=True)
with g2:
st.plotly_chart(go.Figure(data=[go.Pie(labels=['Patients', 'Doctors'], values=[total_patients, len(u_db[u_db['role'] == 'Doctor'])], hole=.4)]), use_container_width=True)
elif st.session_state.nav == "πŸ–₯️ Desk":
st.markdown("### πŸ–₯️ Consultation Desk")
sig = load_db(CALL_SIGNAL_DB, ["Caller", "Receiver", "RoomID", "Status"])
active = sig[(sig['Receiver'] == st.session_state.username) & (sig['Status'] == 'Active')]
if not active.empty:
st.info(f"πŸ”” Incoming Request: **{active.iloc[0]['Caller']}**")
if st.button("βœ… Join Consultation"): st.session_state.d_room = active.iloc[0]['RoomID']
if "d_room" in st.session_state:
st.components.v1.html(f'<iframe src="https://meet.jit.si/{st.session_state.d_room}" width="100%" height="650px" allow="camera; microphone; fullscreen; display-capture"></iframe>', height=700)
elif st.session_state.nav == "πŸ“‚ Summarizer":
st.markdown("### πŸ“‚ AI Clinical Summarizer")
up_s = st.file_uploader("Upload Report (PDF)", type=['pdf'])
if up_s:
with pdfplumber.open(up_s) as f:
text_s = " ".join([p.extract_text() for p in f.pages if p.extract_text()])
if st.button("πŸš€ Process Clinical Summary"):
res = Groq(api_key=GROQ_API_KEY).chat.completions.create(model="llama-3.3-70b-versatile", messages=[{"role": "system", "content": "You are a physician. Summarize this report for a doctor's review."}, {"role": "user", "content": text_s}])
st.info(res.choices[0].message.content)
# --- 7. PATIENT CONTENT & TOOLS ---
elif st.session_state.nav == "πŸ’¬ AI Chat":
up_p = st.file_uploader("Upload Report", type=['pdf'])
if up_p:
with pdfplumber.open(up_p) as f:
st.session_state.active_doc = " ".join([p.extract_text() for p in f.pages if p.extract_text()])
for m in st.session_state.msgs:
st.markdown(f'<div class="{"user-msg" if m["role"]=="user" else "ai-msg"}">{m["content"]}</div>', unsafe_allow_html=True)
v = st.audio_input("🎀", key=f"v_{st.session_state.audio_key}")
q = st.chat_input("Ask health query...")
if q or v:
final_q = q if q else Groq(api_key=GROQ_API_KEY).audio.transcriptions.create(file=("a.wav", v.getvalue()), model="whisper-large-v3", response_format="text")
st.session_state.msgs.append({"role": "user", "content": final_q})
ans = Groq(api_key=GROQ_API_KEY).chat.completions.create(model="llama-3.3-70b-versatile", messages=[{"role": "system", "content": f"Medical AI. Context: {st.session_state.active_doc}"}] + st.session_state.msgs)
st.session_state.msgs.append({"role": "assistant", "content": ans.choices[0].message.content})
st.session_state.audio_key += 1
st.rerun()
elif st.session_state.nav == "πŸ§ͺ Health Lab":
st.markdown("### πŸ§ͺ Personal Health Lab")
tool = st.selectbox("Tool", ["βš–οΈ BMI Analyzer", "🩸 Glucose Tracker"])
if tool == "βš–οΈ BMI Analyzer":
w, h = st.number_input("Weight (kg)", 70), st.number_input("Height (cm)", 175)
bmi = round(w / ((h/100)**2), 1)
st.plotly_chart(go.Figure(go.Indicator(mode="gauge+number", value=bmi, title={'text': "Current BMI"})), use_container_width=True)
elif st.session_state.nav == "πŸ“ž Video":
u_db = load_db(USER_DB, ["username", "role"])
docs = u_db[u_db['role'] == "Doctor"]['username'].tolist()
target = st.selectbox("Select Specialist", docs)
if st.button("Start Call"):
room = f"IntelliCare-{st.session_state.username}-{target}"
pd.DataFrame([{"Caller": st.session_state.username, "Receiver": target, "RoomID": room, "Status": "Active"}]).to_csv(CALL_SIGNAL_DB, index=False)
st.session_state.p_room = room
if "p_room" in st.session_state:
st.components.v1.html(f'<iframe src="https://meet.jit.si/{st.session_state.p_room}" width="100%" height="650px" allow="camera; microphone; fullscreen; display-capture"></iframe>', height=700)
elif st.session_state.nav == "✍️ Prescription":
u_db = load_db(USER_DB, ["username", "role"])
patients = u_db[u_db['role'] == "Patient"]['username'].tolist()
with st.form("rx"):
target = st.selectbox("Select Patient", patients)
meds = st.text_area("Prescription Notes")
if st.form_submit_button("Send"):
pd.concat([load_db(RX_DB, ["Date", "Doctor", "Patient", "Notes"]), pd.DataFrame([{"Date": datetime.now().strftime("%Y-%m-%d"), "Doctor": st.session_state.username, "Patient": target, "Notes": meds}])]).to_csv(RX_DB, index=False)
st.success("Sent!")