Med_AI / src /streamlit_app.py
Arko007's picture
Update src/streamlit_app.py
103aa6d verified
import os
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
import datetime
from typing import Dict, List, Optional, Tuple
import json
from dataclasses import dataclass, asdict
import google.generativeai as genai
from PIL import Image
import pytesseract
import keras
from huggingface_hub import hf_hub_download
import warnings
warnings.filterwarnings("ignore")
# =============================================================================
# CONFIGURATION FOR HUGGING FACE SPACES
# =============================================================================
# Configure Streamlit page
st.set_page_config(
page_title="πŸ₯ MedAI Suite - AI-Powered Medical Platform",
page_icon="πŸ₯",
layout="wide",
initial_sidebar_state="expanded"
)
# Get API key from HF Secrets
try:
GEMINI_API_KEY = st.secrets["GEMINI_API_KEY"]
genai.configure(api_key=GEMINI_API_KEY)
except:
st.error("πŸ”‘ Gemini API key not found in secrets. Please configure it in HF Space settings.")
st.stop()
#Custom CSS
def load_custom_css():
st.markdown("""
<style>
/* Main theme colors */
:root {
--primary-color: #2E8B57;
--secondary-color: #4682B4;
--accent-color: #32CD32;
--success-color: #28a745;
--warning-color: #ffc107;
--danger-color: #dc3545;
--info-color: #17a2b8;
--dark-bg: #1e1e1e;
--light-bg: #f8f9fa;
}
/* Header styling */
.main-header {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
padding: 2rem;
border-radius: 12px;
color: white;
text-align: center;
margin-bottom: 2rem;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.main-header h1 {
margin: 0;
font-size: 2.5rem;
font-weight: 700;
}
.main-header p {
margin: 0.5rem 0 0 0;
font-size: 1.1rem;
opacity: 0.9;
}
/* Feature cards - FIXED VISIBILITY */
.feature-card {
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
padding: 2rem;
border-radius: 12px;
border: 2px solid var(--primary-color);
margin: 1rem 0;
box-shadow: 0 4px 20px rgba(46, 139, 87, 0.15);
transition: transform 0.2s ease;
color: #333333 !important;
}
.feature-card:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(46, 139, 87, 0.25);
border-color: var(--secondary-color);
}
.feature-card h1, .feature-card h2, .feature-card h3, .feature-card h4 {
color: var(--primary-color) !important;
margin: 0.5rem 0 !important;
}
.feature-card p, .feature-card li {
color: #444444 !important;
font-weight: 500;
line-height: 1.6;
}
.feature-card ul {
margin: 1rem 0;
padding-left: 1.5rem;
}
/* Metric cards */
.metric-card {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
padding: 1.5rem;
border-radius: 12px;
color: white;
text-align: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
margin-bottom: 1rem;
}
.metric-card h3 {
margin: 0 0 0.5rem 0;
font-size: 1rem;
opacity: 0.9;
}
.metric-card h2 {
margin: 0;
font-size: 2rem;
font-weight: 700;
}
.metric-card small {
opacity: 0.8;
font-size: 0.9rem;
}
/* Sidebar styling - FIXED */
.css-1d391kg {
background: linear-gradient(180deg, var(--primary-color) 0%, var(--secondary-color) 100%) !important;
}
/* Patient info box - FIXED */
.patient-info-card {
background: linear-gradient(135deg, #e8f5e8 0%, #c8e6c9 100%);
border: 2px solid var(--primary-color);
padding: 1rem;
border-radius: 12px;
margin-bottom: 1rem;
color: #2d5016 !important;
}
.patient-info-card h4 {
color: var(--primary-color) !important;
margin-bottom: 0.5rem !important;
}
.patient-info-card p {
color: #2d5016 !important;
font-weight: 600;
margin: 0;
}
/* Button styling */
.stButton > button {
background: linear-gradient(90deg, var(--primary-color) 0%, var(--secondary-color) 100%);
color: white;
border: none;
border-radius: 8px;
padding: 0.7rem 1.5rem;
font-weight: 600;
transition: all 0.2s ease;
}
.stButton > button:hover {
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(46, 139, 87, 0.3);
}
/* Alert styling */
.success-alert {
background: linear-gradient(90deg, #d4edda, #c3e6cb);
border: 2px solid var(--success-color);
border-left: 6px solid var(--success-color);
padding: 1.5rem;
border-radius: 8px;
margin: 1rem 0;
color: #155724 !important;
}
.success-alert h3, .success-alert h4 {
color: #155724 !important;
margin-bottom: 0.5rem !important;
}
.warning-alert {
background: linear-gradient(90deg, #fff3cd, #ffeaa7);
border: 2px solid var(--warning-color);
border-left: 6px solid var(--warning-color);
padding: 1.5rem;
border-radius: 8px;
margin: 1rem 0;
color: #856404 !important;
}
.warning-alert h3, .warning-alert h4 {
color: #856404 !important;
margin-bottom: 0.5rem !important;
}
.error-alert {
background: linear-gradient(90deg, #f8d7da, #f5c6cb);
border: 2px solid var(--danger-color);
border-left: 6px solid var(--danger-color);
padding: 1.5rem;
border-radius: 8px;
margin: 1rem 0;
color: #721c24 !important;
}
.error-alert h3, .error-alert h4 {
color: #721c24 !important;
margin-bottom: 0.5rem !important;
}
/* Chat styling */
.chat-message {
padding: 1.5rem;
border-radius: 12px;
margin: 1rem 0;
border: 1px solid #ddd;
}
.user-message {
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
border-left: 4px solid #2196f3;
color: #0d47a1 !important;
}
.assistant-message {
background: linear-gradient(135deg, #e8f5e8, #c8e6c9);
border-left: 4px solid var(--accent-color);
color: #2d5016 !important;
}
.chat-message strong {
font-weight: 700;
}
/* Hide Streamlit default elements */
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
header {visibility: hidden;}
/* Professional medical disclaimer */
.medical-disclaimer {
background: linear-gradient(135deg, #fff3e0, #ffe0b2);
border: 3px solid #ff9800;
border-radius: 12px;
padding: 2rem;
margin: 2rem 0;
text-align: center;
color: #e65100 !important;
}
.medical-disclaimer h4 {
color: #e65100 !important;
margin-bottom: 1rem !important;
font-weight: 700;
}
.medical-disclaimer p {
color: #bf360c !important;
font-weight: 500;
line-height: 1.6;
}
/* Sidebar text colors - FIXED */
.css-1d391kg .css-10trblm {
color: white !important;
}
/* Info box styling - FIXED */
.stInfo {
background: linear-gradient(135deg, #e1f5fe, #b3e5fc);
border: 2px solid var(--info-color);
color: #01579b !important;
}
/* Expander styling */
.streamlit-expanderHeader {
background: var(--light-bg);
border: 1px solid var(--primary-color);
border-radius: 8px;
}
/* Table styling */
.stDataFrame {
border: 2px solid var(--primary-color);
border-radius: 8px;
}
/* Progress bar styling */
.stProgress .st-bo {
background: var(--primary-color);
}
</style>
""", unsafe_allow_html=True)
# =============================================================================
# DATA CLASSES
# =============================================================================
@dataclass
class UserProfile:
"""Enhanced user medical profile"""
user_id: str
name: str
age: int
height: float # cm
weight: float # kg
sex: str
blood_sugar: float # mg/dL
blood_pressure: str # systolic/diastolic
past_medications: List[str]
current_conditions: List[str]
allergies: List[str]
created_at: str
bmi: float = 0.0
def __post_init__(self):
if self.height > 0:
height_m = self.height / 100
self.bmi = round(self.weight / (height_m ** 2), 2)
# =============================================================================
# MEDICAL AI BACKEND
# =============================================================================
class MedicalAI:
"""Complete Medical AI System"""
def __init__(self):
self.user_profiles = {}
self.conversation_history = {}
self.gemini_model = None
self.dr_model = None
self.dr_model_loaded = False
# Initialize Gemini
self._init_gemini()
# Initialize DR model
self._init_dr_model()
def _init_gemini(self):
"""Initialize Gemini model"""
try:
self.gemini_model = genai.GenerativeModel("gemini-2.0-flash")
# Don't show success message to avoid clutter
except Exception as e:
st.error(f"❌ Gemini connection failed: {str(e)}")
def _init_dr_model(self):
"""Initialize diabetic retinopathy model"""
try:
repo_id = "Arko007/diabetic-retinopathy-v1"
weights_filename = "best_model.h5"
local_weights_path = hf_hub_download(repo_id=repo_id, filename=weights_filename)
self.dr_model = keras.saving.load_model(local_weights_path)
self.dr_model_loaded = True
except Exception as e:
# Silent failure - model will be unavailable but won't show error
self.dr_model_loaded = False
def validate_user_input(self, user_data: Dict) -> Tuple[bool, List[str]]:
"""Validate user profile data"""
errors = []
required_fields = ['name', 'age', 'height', 'weight', 'sex']
for field in required_fields:
if not user_data.get(field):
errors.append(f"❌ {field.title()} is required")
# Age validation
try:
age = int(user_data.get('age', 0))
if not 1 <= age <= 150:
errors.append("❌ Age must be between 1-150 years")
except:
errors.append("❌ Age must be a valid number")
# Height validation
try:
height = float(user_data.get('height', 0))
if not 50 <= height <= 300:
errors.append("❌ Height must be between 50-300 cm")
except:
errors.append("❌ Height must be a valid number")
# Weight validation
try:
weight = float(user_data.get('weight', 0))
if not 10 <= weight <= 500:
errors.append("❌ Weight must be between 10-500 kg")
except:
errors.append("❌ Weight must be a valid number")
return len(errors) == 0, errors
def create_user_profile(self, user_data: Dict) -> Tuple[Optional[UserProfile], List[str]]:
"""Create new user profile"""
is_valid, errors = self.validate_user_input(user_data)
if not is_valid:
return None, errors
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
user_id = f"medai_user_{timestamp}"
try:
profile = UserProfile(
user_id=user_id,
name=user_data.get('name', '').strip().title(),
age=int(user_data.get('age', 0)),
height=float(user_data.get('height', 0)),
weight=float(user_data.get('weight', 0)),
sex=user_data.get('sex', '').lower().strip(),
blood_sugar=float(user_data.get('blood_sugar') or 0),
blood_pressure=user_data.get('blood_pressure', '').strip(),
past_medications=self._clean_list(user_data.get('past_medications', [])),
current_conditions=self._clean_list(user_data.get('current_conditions', [])),
allergies=self._clean_list(user_data.get('allergies', [])),
created_at=datetime.datetime.now().isoformat()
)
self.user_profiles[user_id] = profile
self.conversation_history[user_id] = []
return profile, []
except Exception as e:
return None, [f"❌ Profile creation error: {str(e)}"]
def _clean_list(self, input_list) -> List[str]:
"""Clean list inputs"""
if isinstance(input_list, str):
return [item.strip() for item in input_list.split(',') if item.strip()]
elif isinstance(input_list, list):
return [str(item).strip() for item in input_list if str(item).strip()]
return []
def get_bmi_category(self, bmi: float) -> str:
"""Get BMI category"""
if bmi < 18.5:
return "Underweight"
elif bmi < 23:
return "Normal weight"
elif bmi < 25:
return "Overweight"
elif bmi < 30:
return "Obese Class I"
else:
return "Obese Class II+"
def generate_medical_context(self, user_id: str) -> str:
"""Generate medical context for consultation"""
if user_id not in self.user_profiles:
return ""
profile = self.user_profiles[user_id]
return f"""
Patient: {profile.name}, {profile.age} years old, {profile.sex.title()}
Physical: Height {profile.height}cm, Weight {profile.weight}kg, BMI {profile.bmi}
Vitals: Blood Sugar {profile.blood_sugar} mg/dL, BP {profile.blood_pressure}
Medical History: {', '.join(profile.current_conditions) if profile.current_conditions else 'None'}
Medications: {', '.join(profile.past_medications) if profile.past_medications else 'None'}
Allergies: {', '.join(profile.allergies) if profile.allergies else 'None'}
"""
def ask_medical_question(self, user_id: str, question: str) -> Dict:
"""Medical consultation using Gemini"""
if not self.gemini_model:
return {'success': False, 'error': "AI consultation unavailable"}
if user_id not in self.user_profiles:
return {'success': False, 'error': "Please create a profile first"}
medical_context = self.generate_medical_context(user_id)
prompt = f"""You are MedAI, a professional medical AI assistant for healthcare in India.
PATIENT CONTEXT:
{medical_context}
PATIENT QUESTION: {question}
INSTRUCTIONS:
1. Provide helpful, evidence-based medical guidance
2. Consider the patient's profile and Indian healthcare context
3. Use clear, supportive language
4. Always emphasize consulting healthcare professionals
5. If urgent symptoms, immediately advise emergency care
6. Keep response comprehensive but accessible (300-400 words)
Provide a detailed, caring response that helps the patient understand their health."""
try:
response = self.gemini_model.generate_content(prompt)
answer = response.text
# Store conversation
conversation = {
'question': question,
'answer': answer,
'timestamp': datetime.datetime.now().isoformat()
}
self.conversation_history[user_id].append(conversation)
return {'success': True, 'answer': answer, 'timestamp': conversation['timestamp']}
except Exception as e:
return {'success': False, 'error': f"Consultation error: {str(e)}"}
def analyze_medical_report(self, image: Image.Image) -> Dict:
"""Analyze medical reports using Gemini Vision (no OCR needed)"""
if not self.gemini_model:
return {'success': False, 'error': "Report analysis unavailable"}
try:
prompt = f"""You are a medical AI analyzing a healthcare report image for Indian patients.
ANALYSIS INSTRUCTIONS:
1. Identify the type of medical report from the image
2. Extract and read all visible text, numbers, and values
3. Highlight values outside normal ranges for Indian population
4. Explain abnormal findings in simple terms with analogies
5. Provide lifestyle recommendations for concerning values
6. Use markdown formatting for clear structure
7. Always end with medical disclaimer
Provide a comprehensive, patient-friendly analysis of this medical report image."""
# Use Gemini Vision to analyze image directly
response = self.gemini_model.generate_content([prompt, image])
return {
'success': True,
'analysis': response.text,
'extracted_text': "Analysis performed using AI vision - no text extraction needed",
'timestamp': datetime.datetime.now().isoformat()
}
except Exception as e:
return {'success': False, 'error': f"Analysis error: {str(e)}"}
def analyze_diabetic_retinopathy(self, image: Image.Image) -> Dict:
"""Diabetic retinopathy screening using custom model + Gemini explanation"""
if not self.dr_model_loaded:
return {'success': False, 'error': "Eye screening model unavailable"}
try:
# Preprocess image
img_resized = image.resize((384, 384))
image_array = np.array(img_resized).astype(np.float32) / 255.0
image_array = np.expand_dims(image_array, axis=0)
# Get prediction
predictions = self.dr_model.predict(image_array, verbose=0)
predicted_class_index = np.argmax(predictions, axis=1)[0]
confidence = float(np.max(predictions))
class_labels = ["No DR", "Mild DR", "Moderate DR", "Severe DR", "Proliferative DR"]
predicted_label = class_labels[predicted_class_index]
# Generate detailed analysis
if self.gemini_model:
prompt = f"""You are an ophthalmology AI providing educational analysis for Indian healthcare.
SCREENING RESULT:
- AI Classification: {predicted_label}
- Model Confidence: {confidence:.2%}
- Custom Model: Arko007/diabetic-retinopathy-v1
ANALYSIS INSTRUCTIONS:
1. Explain what this classification means
2. Describe typical changes for this stage
3. Provide follow-up recommendations
4. Include lifestyle guidance
5. Use supportive, clear language
6. Always emphasize this is screening, not diagnosis
Provide comprehensive analysis with clear sections."""
try:
gemini_response = self.gemini_model.generate_content(prompt)
detailed_analysis = gemini_response.text
except:
detailed_analysis = f"**Classification:** {predicted_label}\n**Confidence:** {confidence:.2%}\n\nPlease consult an eye specialist for detailed evaluation."
else:
detailed_analysis = f"**Classification:** {predicted_label}\n**Confidence:** {confidence:.2%}"
return {
'success': True,
'classification': predicted_label,
'confidence': confidence,
'analysis': detailed_analysis,
'timestamp': datetime.datetime.now().isoformat()
}
except Exception as e:
return {'success': False, 'error': f"Screening error: {str(e)}"}
def get_conversation_history(self, user_id: str, limit: int = 10) -> List[Dict]:
"""Get conversation history"""
history = self.conversation_history.get(user_id, [])
return history[-limit:] if limit else history
# =============================================================================
# SESSION STATE INITIALIZATION
# =============================================================================
def initialize_session_state():
"""Initialize all session state variables"""
if 'medical_ai' not in st.session_state:
st.session_state.medical_ai = MedicalAI()
if 'current_user' not in st.session_state:
st.session_state.current_user = None
if 'chat_history' not in st.session_state:
st.session_state.chat_history = []
if 'page' not in st.session_state:
st.session_state.page = "πŸ₯ Dashboard"
# =============================================================================
# UI COMPONENTS
# =============================================================================
def display_main_header():
"""Display main application header"""
st.markdown("""
<div class="main-header">
<h1>πŸ₯ MedAI Suite</h1>
<p>AI-Powered Medical Platform | Consultation β€’ Report Analysis β€’ Eye Screening β€’ Health Analytics</p>
</div>
""", unsafe_allow_html=True)
def create_sidebar():
"""Enhanced sidebar navigation"""
with st.sidebar:
# App branding
st.markdown("""
<div style="text-align: center; padding: 1rem; background: linear-gradient(135deg, #2E8B57 0%, #4682B4 100%); border-radius: 12px; color: white; margin-bottom: 1rem;">
<h2>πŸ₯ MedAI Suite</h2>
<p>Your AI Health Assistant</p>
</div>
""", unsafe_allow_html=True)
# User info
# Replace the user info section in create_sidebar() with this:
if st.session_state.current_user:
user = st.session_state.current_user
st.markdown(f"""
<div class="patient-info-card">
<h4>πŸ‘€ Current Patient</h4>
<p><strong>{user.name}</strong><br>
Age: {user.age} | BMI: {user.bmi}</p>
</div>
""", unsafe_allow_html=True)
if st.button("πŸ”„ New Patient", use_container_width=True):
st.session_state.current_user = None
st.session_state.chat_history = []
st.rerun()
else:
st.markdown("""
<div class="patient-info-card">
<h4>πŸ‘€ No Patient Selected</h4>
<p>Create a profile to get started</p>
</div>
""", unsafe_allow_html=True)
# Navigation
st.markdown("### 🧭 Navigation")
page_options = [
"πŸ₯ Dashboard",
"πŸ‘€ Patient Profile",
"πŸ€– Medical Consultation",
"πŸ“‹ Report Analysis",
"πŸ‘οΈ Eye Screening",
"πŸ“Š Health Analytics"
]
selected_page = st.selectbox(
"Go to:",
page_options,
index=page_options.index(st.session_state.page) if st.session_state.page in page_options else 0,
key="navigation_selectbox"
)
if selected_page != st.session_state.page:
st.session_state.page = selected_page
st.rerun()
# Quick actions
st.markdown("### ⚑ Quick Actions")
if st.session_state.current_user:
if st.button("🚨 Emergency Consultation", use_container_width=True, type="primary"):
st.session_state.page = "πŸ€– Medical Consultation"
st.rerun()
if st.button("πŸ“Š Health Dashboard", use_container_width=True):
st.session_state.page = "πŸ“Š Health Analytics"
st.rerun()
# Footer
st.markdown("---")
st.markdown("""
<div style="text-align: center; font-size: 0.8em; color: #666;">
<p>πŸ₯ MedAI Suite<br>
AI-Powered Healthcare Assistant<br>
Made with ❀️ for Better Health</p>
</div>
""", unsafe_allow_html=True)
def create_patient_profile_form():
"""Patient profile creation with FIXED numeric types"""
st.markdown("## πŸ‘€ Create Patient Profile")
with st.form("patient_profile_form", clear_on_submit=False):
st.markdown("### Basic Information")
col1, col2 = st.columns(2)
with col1:
name = st.text_input("πŸ‘€ Full Name*", placeholder="Enter patient name")
age = st.number_input("πŸŽ‚ Age*", min_value=1, max_value=150, value=30, step=1)
height = st.number_input("πŸ“ Height (cm)*", min_value=50.0, max_value=300.0, value=170.0, step=1.0)
weight = st.number_input("βš–οΈ Weight (kg)*", min_value=10.0, max_value=300.0, value=70.0, step=0.1)
with col2:
sex = st.selectbox("⚧ Sex*", ["Male", "Female", "Other"], key="sex_selectbox")
blood_sugar = st.number_input("🍬 Blood Sugar (mg/dL)", min_value=0.0, value=95.0, step=1.0)
# Blood pressure - FIXED
st.markdown("🩺 **Blood Pressure**")
bp_col1, bp_col2 = st.columns(2)
with bp_col1:
bp_sys = st.number_input("Systolic", min_value=60, max_value=250, value=120, step=1, key="bp_sys")
with bp_col2:
bp_dia = st.number_input("Diastolic", min_value=40, max_value=150, value=80, step=1, key="bp_dia")
st.markdown("### Medical History (Optional)")
col3, col4 = st.columns(2)
with col3:
conditions = st.text_area("πŸ₯ Current Conditions",
placeholder="e.g., Diabetes, Hypertension (comma-separated)")
medications = st.text_area("πŸ’Š Past Medications",
placeholder="e.g., Metformin, Aspirin (comma-separated)")
with col4:
allergies = st.text_area("⚠️ Known Allergies",
placeholder="e.g., Penicillin, Shellfish (comma-separated)")
notes = st.text_area("πŸ“ Additional Notes",
placeholder="Any other relevant information")
# FIXED - Submit button added
submitted = st.form_submit_button("βœ… Create Profile", type="primary", use_container_width=True)
if submitted and name and age and height and weight and sex:
user_data = {
'name': name,
'age': age,
'height': height,
'weight': weight,
'sex': sex.lower(),
'blood_sugar': blood_sugar,
'blood_pressure': f"{bp_sys}/{bp_dia}",
'current_conditions': conditions,
'past_medications': medications,
'allergies': allergies
}
profile, errors = st.session_state.medical_ai.create_user_profile(user_data)
if profile:
st.session_state.current_user = profile
st.markdown(f"""
<div class="success-alert">
<h4>βœ… Profile Created Successfully!</h4>
<p>Welcome <strong>{name}</strong>! Your medical profile is ready.</p>
</div>
""", unsafe_allow_html=True)
st.balloons()
st.rerun()
else:
for error in errors:
st.error(error)
def display_patient_profile():
"""Display patient profile dashboard"""
if not st.session_state.current_user:
return
user = st.session_state.current_user
# Profile header
st.markdown(f"""
<div class="main-header">
<h2>πŸ‘€ {user.name}</h2>
<p>Patient ID: {user.user_id} | Created: {user.created_at[:10]}</p>
</div>
""", unsafe_allow_html=True)
# Metrics
col1, col2, col3, col4, col5, col6 = st.columns(6)
metrics = [
(col1, "Age", f"{user.age}", "years"),
(col2, "Height", f"{user.height}", "cm"),
(col3, "Weight", f"{user.weight}", "kg"),
(col4, "BMI", f"{user.bmi}", st.session_state.medical_ai.get_bmi_category(user.bmi)),
(col5, "Blood Sugar", f"{user.blood_sugar}", "mg/dL"),
(col6, "Blood Pressure", user.blood_pressure, "mmHg")
]
for col, title, value, unit in metrics:
with col:
st.markdown(f"""
<div class="metric-card">
<h3>{title}</h3>
<h2>{value}</h2>
<small>{unit}</small>
</div>
""", unsafe_allow_html=True)
# Medical history
if any([user.current_conditions, user.past_medications, user.allergies]):
st.markdown("### πŸ“‹ Medical Summary")
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("**πŸ₯ Current Conditions**")
if user.current_conditions:
for condition in user.current_conditions:
st.markdown(f"β€’ {condition}")
else:
st.markdown("β€’ None reported")
with col2:
st.markdown("**πŸ’Š Past Medications**")
if user.past_medications:
for medication in user.past_medications:
st.markdown(f"β€’ {medication}")
else:
st.markdown("β€’ None reported")
with col3:
st.markdown("**⚠️ Known Allergies**")
if user.allergies:
for allergy in user.allergies:
st.markdown(f"β€’ {allergy}")
else:
st.markdown("β€’ None reported")
def create_medical_consultation():
"""AI Medical Consultation Interface"""
st.markdown("## πŸ€– AI Medical Consultation")
if not st.session_state.current_user:
st.markdown("""
<div class="warning-alert">
<h4>⚠️ Patient Profile Required</h4>
<p>Please create a patient profile first to receive personalized medical consultation.</p>
</div>
""", unsafe_allow_html=True)
return
user = st.session_state.current_user
# Patient context
with st.expander("πŸ‘€ Current Patient Context", expanded=False):
col1, col2 = st.columns(2)
with col1:
st.write(f"**Name:** {user.name}")
st.write(f"**Age:** {user.age} years")
st.write(f"**BMI:** {user.bmi} ({st.session_state.medical_ai.get_bmi_category(user.bmi)})")
with col2:
st.write(f"**Blood Sugar:** {user.blood_sugar} mg/dL")
st.write(f"**Blood Pressure:** {user.blood_pressure}")
if user.current_conditions:
st.write(f"**Conditions:** {', '.join(user.current_conditions)}")
# Quick consultation topics
st.markdown("#### πŸ” Quick Consultation Topics")
col1, col2, col3, col4 = st.columns(4)
quick_topics = [
("πŸ“Š Health Review", "Please review my current health metrics and provide insights."),
("🩺 Blood Pressure", "What should I know about managing my blood pressure?"),
("🍎 Lifestyle Tips", "What lifestyle changes would you recommend for me?"),
("⚠️ Risk Assessment", "Are there any health risks I should be aware of?")
]
selected_topic = None
for i, (col, (title, question)) in enumerate(zip([col1, col2, col3, col4], quick_topics)):
with col:
if st.button(title, use_container_width=True, key=f"topic_{i}"):
selected_topic = question
# Chat interface
st.markdown("### πŸ’¬ Ask Your Medical Questions")
# Display chat history
for msg in st.session_state.chat_history[-5:]: # Show last 5 messages
if msg["role"] == "user":
st.markdown(f"""
<div class="chat-message user-message">
<strong>πŸ‘€ You:</strong> {msg["content"]}
</div>
""", unsafe_allow_html=True)
else:
st.markdown(f"""
<div class="chat-message assistant-message">
<strong>πŸ€– MedAI:</strong> {msg["content"]}
</div>
""", unsafe_allow_html=True)
# Input form - FIXED with submit button
with st.form("medical_consultation_form", clear_on_submit=True):
question = st.text_area(
"✍️ Your Medical Question:",
value=selected_topic if selected_topic else "",
placeholder="Describe your symptoms or ask about your health...",
height=100
)
col1, col2 = st.columns([3, 1])
with col1:
priority = st.selectbox("🚨 Priority Level", ["Normal", "Urgent", "Emergency"], key="priority_selectbox")
submitted = st.form_submit_button("πŸ€– Get AI Consultation", type="primary", use_container_width=True)
if submitted and question.strip():
# Add user message
st.session_state.chat_history.append({"role": "user", "content": question})
# Emergency check
if priority == "Emergency" or any(word in question.lower() for word in ['chest pain', "can't breathe", 'severe pain', 'bleeding']):
st.markdown("""
<div class="error-alert">
<h4>🚨 MEDICAL EMERGENCY ALERT</h4>
<p><strong>If this is a medical emergency, please call emergency services immediately (102/108) or visit the nearest hospital!</strong></p>
</div>
""", unsafe_allow_html=True)
# Get AI response
with st.spinner("🧠 MedAI is analyzing your question..."):
response = st.session_state.medical_ai.ask_medical_question(user.user_id, question)
if response['success']:
# Add AI response
st.session_state.chat_history.append({"role": "assistant", "content": response['answer']})
st.rerun()
else:
st.error(f"❌ Consultation error: {response['error']}")
# Medical disclaimer
st.markdown("""
<div class="medical-disclaimer">
<h4>⚠️ IMPORTANT MEDICAL DISCLAIMER</h4>
<p>This AI assistant provides general health information for educational purposes only.
It is not a substitute for professional medical advice, diagnosis, or treatment.
Always consult qualified healthcare professionals for medical concerns.</p>
</div>
""", unsafe_allow_html=True)
def create_report_analysis():
"""Medical Report Analysis Interface"""
st.markdown("## πŸ“‹ Medical Report Analysis")
st.markdown("""
<div class="feature-card">
<h4>πŸ”¬ AI-Powered Report Analysis</h4>
<p>Upload images of your medical reports for AI analysis. Supports blood tests, urine tests,
lipid profiles, liver function tests, kidney function tests, and more.</p>
<p><strong>Supported formats:</strong> PNG, JPG, JPEG</p>
</div>
""", unsafe_allow_html=True)
# File upload
uploaded_file = st.file_uploader(
"πŸ“ Choose Medical Report Image",
type=['png', 'jpg', 'jpeg'],
help="Upload a clear image of your medical report"
)
if uploaded_file:
image = Image.open(uploaded_file)
col1, col2 = st.columns([1, 1])
with col1:
st.markdown("### πŸ“· Uploaded Report")
st.image(image, caption="Your medical report", use_column_width=True)
# Image quality check
width, height = image.size
if width < 800 or height < 600:
st.warning("⚠️ Low resolution detected. Higher resolution images provide better analysis.")
with col2:
if st.button("πŸ” Analyze Report", type="primary", use_container_width=True):
with st.spinner("🧠 Analyzing your medical report..."):
result = st.session_state.medical_ai.analyze_medical_report(image)
if result['success']:
st.markdown("### πŸ“Š AI Analysis Results")
st.markdown(result['analysis'])
# Show extracted text
with st.expander("πŸ“ Extracted Text Preview", expanded=False):
st.text(result.get('extracted_text', 'Text extraction unavailable'))
if st.session_state.current_user:
st.success("βœ… Analysis complete! Results ready for consultation.")
else:
st.error(f"❌ Analysis failed: {result['error']}")
# Tips for better results
with st.expander("πŸ’‘ Tips for Better Analysis", expanded=False):
st.markdown("""
**For optimal results:**
1. **πŸ“Έ Clear Image:** Ensure good lighting and sharp text
2. **πŸ“ Straight Orientation:** Keep the report upright
3. **πŸ” High Resolution:** Use at least 800x600 pixels
4. **πŸ“‹ Complete Report:** Include all sections and reference ranges
5. **πŸ₯ Official Reports:** Use certified laboratory reports
**Supported Report Types:**
- Complete Blood Count (CBC)
- Blood Chemistry Panels
- Lipid Profiles
- Liver & Kidney Function Tests
- Thyroid Function Tests
- Diabetes Monitoring (HbA1c, Glucose)
""")
def create_diabetic_retinopathy_screening():
"""Diabetic Retinopathy Screening Interface"""
st.markdown("## πŸ‘οΈ AI Eye Screening")
if not st.session_state.medical_ai.dr_model_loaded:
st.markdown("""
<div class="error-alert">
<h4>❌ Eye Screening Unavailable</h4>
<p>The diabetic retinopathy screening model could not be loaded. Please try refreshing the page.</p>
</div>
""", unsafe_allow_html=True)
return
st.markdown("""
<div class="feature-card">
<h4>πŸ”¬ AI-Powered Diabetic Retinopathy Screening</h4>
<p>Upload retinal fundus images for AI screening of diabetic retinopathy.
Our custom deep learning model can detect signs of eye complications in diabetic patients.</p>
<p><strong>Model:</strong> Arko007/diabetic-retinopathy-v1 (Custom trained for Indian population)</p>
</div>
""", unsafe_allow_html=True)
# Upload interface
uploaded_image = st.file_uploader(
"πŸ“ Upload Retinal Fundus Image",
type=['png', 'jpg', 'jpeg'],
help="Upload a clear retinal photograph",
key="dr_image_upload"
)
if uploaded_image:
image = Image.open(uploaded_image)
col1, col2 = st.columns([1, 1])
with col1:
st.markdown("### πŸ‘οΈ Retinal Image")
st.image(image, caption="Retinal fundus image", use_column_width=True)
# Image info
width, height = image.size
st.info(f"πŸ“ Image dimensions: {width}Γ—{height} pixels")
if width < 384 or height < 384:
st.warning("⚠️ Minimum recommended resolution: 384Γ—384 pixels")
with col2:
if st.button("πŸ” Screen for Diabetic Retinopathy", type="primary", use_container_width=True):
with st.spinner("🧠 AI is analyzing the retinal image..."):
result = st.session_state.medical_ai.analyze_diabetic_retinopathy(image)
if result['success']:
classification = result['classification']
confidence = result['confidence']
# Severity color coding
severity_colors = {
"No DR": "🟒",
"Mild DR": "🟑",
"Moderate DR": "🟠",
"Severe DR": "πŸ”΄",
"Proliferative DR": "πŸ”΄"
}
color = severity_colors.get(classification, "βšͺ")
st.markdown(f"""
<div class="success-alert">
<h3>{color} AI Screening Result: {classification}</h3>
<p><strong>Model Confidence:</strong> {confidence:.1%}</p>
</div>
""", unsafe_allow_html=True)
# Confidence bar
st.progress(confidence)
# Detailed analysis
st.markdown("### πŸ“Š Detailed Analysis")
st.markdown(result['analysis'])
# Recommendations
st.markdown("### πŸ’‘ Recommendations")
if classification == "No DR":
st.success("βœ… No diabetic retinopathy detected. Continue regular eye checkups.")
elif classification == "Mild DR":
st.warning("⚠️ Mild diabetic retinopathy detected. Schedule follow-up in 6-12 months.")
elif classification in ["Moderate DR", "Severe DR"]:
st.error("πŸ”΄ Significant diabetic retinopathy detected. Consult an ophthalmologist promptly.")
elif classification == "Proliferative DR":
st.error("🚨 Advanced diabetic retinopathy detected. URGENT ophthalmologist consultation required!")
if st.session_state.current_user:
st.info("πŸ’Ύ Screening results saved to your profile.")
else:
st.error(f"❌ Screening failed: {result['error']}")
# Educational information
st.markdown("### πŸ“š Understanding Diabetic Retinopathy")
with st.expander("ℹ️ About Diabetic Retinopathy", expanded=False):
st.markdown("""
**What is Diabetic Retinopathy?**
A diabetes complication affecting the eyes, caused by damage to retinal blood vessels.
**Screening Stages:**
1. **🟒 No DR:** No signs detected
2. **🟑 Mild DR:** Early changes (microaneurysms)
3. **🟠 Moderate DR:** Blood vessels become blocked
4. **πŸ”΄ Severe DR:** Significant blood vessel blockage
5. **πŸ”΄ Proliferative DR:** Advanced stage with new vessel growth
**Risk Factors:**
- Duration of diabetes
- Poor blood sugar control
- High blood pressure
- Pregnancy in diabetic women
**Prevention:**
- Maintain good blood sugar control
- Regular eye examinations
- Control blood pressure and cholesterol
- Healthy lifestyle choices
""")
# Screening disclaimer
st.markdown("""
<div class="medical-disclaimer">
<h4>⚠️ SCREENING DISCLAIMER</h4>
<p>This AI screening tool is for educational purposes only. It is not a medical device
and cannot replace professional eye examination. Always consult qualified ophthalmologists
for proper diagnosis and treatment of eye conditions.</p>
</div>
""", unsafe_allow_html=True)
def generate_health_metrics(profile_data=None, days=30):
"""Generate sample health data for analytics"""
dates = pd.date_range(start=datetime.date.today() - datetime.timedelta(days=days),
periods=days, freq='D')
# Base values from profile
if profile_data:
base_weight = profile_data.weight
base_sugar = profile_data.blood_sugar if profile_data.blood_sugar > 0 else 95
bp_parts = profile_data.blood_pressure.split('/')
base_bp_sys = float(bp_parts[0]) if len(bp_parts) == 2 else 120
base_bp_dia = float(bp_parts[1]) if len(bp_parts) == 2 else 80
else:
base_weight, base_sugar, base_bp_sys, base_bp_dia = 70, 95, 120, 80
# Generate realistic variations
np.random.seed(42)
weight = np.random.normal(base_weight, 0.5, days)
blood_sugar = np.clip(np.random.normal(base_sugar, 10, days), 70, 300)
bp_systolic = np.clip(np.random.normal(base_bp_sys, 8, days), 90, 180)
bp_diastolic = np.clip(np.random.normal(base_bp_dia, 5, days), 60, 120)
steps = np.maximum(2000, np.random.normal(8000, 1500, days)).astype(int)
sleep_hours = np.clip(np.random.normal(7.5, 1, days), 5, 10)
return pd.DataFrame({
'date': dates,
'weight': weight.round(1),
'blood_sugar': blood_sugar.round(0),
'bp_systolic': bp_systolic.round(0),
'bp_diastolic': bp_diastolic.round(0),
'steps': steps,
'sleep_hours': sleep_hours.round(1)
})
def create_health_analytics():
"""Health Analytics Dashboard"""
st.markdown("## πŸ“Š Health Analytics Dashboard")
if not st.session_state.current_user:
st.markdown("""
<div class="warning-alert">
<h4>⚠️ Patient Profile Required</h4>
<p>Create a patient profile to view personalized health analytics.</p>
</div>
""", unsafe_allow_html=True)
return
user = st.session_state.current_user
health_data = generate_health_metrics(user)
# Health score calculation
bmi_score = 25 if 18.5 <= user.bmi <= 25 else 15
sugar_score = 25 if user.blood_sugar <= 100 else 15
bp_parts = user.blood_pressure.split('/')
bp_score = 25 if len(bp_parts) == 2 and int(bp_parts[0]) <= 120 and int(bp_parts[1]) <= 80 else 15
activity_score = 25 # Assume good activity
total_score = bmi_score + sugar_score + bp_score + activity_score
# Dashboard tabs
tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Overview", "πŸ“Š Detailed Metrics", "🎯 Health Score"])
with tab1:
st.subheader("πŸ“Š Health Overview")
# Key metrics
col1, col2, col3, col4 = st.columns(4)
latest = health_data.iloc[-1]
with col1:
st.metric("Current Weight", f"{latest['weight']:.1f} kg",
delta=f"{latest['weight'] - health_data['weight'].mean():+.1f}")
with col2:
st.metric("Blood Sugar", f"{latest['blood_sugar']:.0f} mg/dL",
delta=f"{latest['blood_sugar'] - health_data['blood_sugar'].mean():+.0f}")
with col3:
st.metric("Blood Pressure", f"{latest['bp_systolic']:.0f}/{latest['bp_diastolic']:.0f}",
help="Systolic/Diastolic pressure in mmHg")
with col4:
st.metric("Daily Steps", f"{latest['steps']:,}",
delta=f"{latest['steps'] - health_data['steps'].mean():+,.0f}")
# Trends chart
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('Weight Trend', 'Blood Sugar', 'Blood Pressure', 'Activity'),
specs=[[{"secondary_y": False}, {"secondary_y": False}],
[{"secondary_y": False}, {"secondary_y": False}]]
)
fig.add_trace(go.Scatter(x=health_data['date'], y=health_data['weight'],
name='Weight', line=dict(color='#2E8B57')), row=1, col=1)
fig.add_trace(go.Scatter(x=health_data['date'], y=health_data['blood_sugar'],
name='Blood Sugar', line=dict(color='#DC143C')), row=1, col=2)
fig.add_trace(go.Scatter(x=health_data['date'], y=health_data['bp_systolic'],
name='Systolic', line=dict(color='#FF8C00')), row=2, col=1)
fig.add_trace(go.Scatter(x=health_data['date'], y=health_data['steps'],
name='Steps', line=dict(color='#32CD32')), row=2, col=2)
fig.update_layout(height=600, showlegend=False, title_text="30-Day Health Trends")
st.plotly_chart(fig, use_container_width=True)
with tab2:
st.subheader("πŸ“ˆ Detailed Health Metrics")
# Data table
st.markdown("#### Recent Health Records")
display_data = health_data.tail(7).copy()
display_data['date'] = display_data['date'].dt.strftime('%Y-%m-%d')
st.dataframe(display_data, use_container_width=True)
# Correlation analysis
st.markdown("#### Health Metrics Correlation")
corr_data = health_data[['weight', 'blood_sugar', 'bp_systolic', 'steps', 'sleep_hours']].corr()
fig_corr = px.imshow(corr_data, title="Health Metrics Correlation Matrix",
color_continuous_scale='RdBu_r', aspect="auto")
st.plotly_chart(fig_corr, use_container_width=True)
with tab3:
st.subheader("🎯 Personal Health Score")
# Health score gauge
fig_gauge = go.Figure(go.Indicator(
mode="gauge+number",
value=total_score,
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': "Overall Health Score"},
gauge={
'axis': {'range': [None, 100]},
'bar': {'color': "#2E8B57"},
'steps': [
{'range': [0, 60], 'color': "lightgray"},
{'range': [60, 80], 'color': "yellow"},
{'range': [80, 100], 'color': "#32CD32"}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 90
}
}
))
st.plotly_chart(fig_gauge, use_container_width=True)
# Score breakdown
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("BMI Score", f"{bmi_score}/25")
with col2:
st.metric("Blood Sugar Score", f"{sugar_score}/25")
with col3:
st.metric("Blood Pressure Score", f"{bp_score}/25")
with col4:
st.metric("Activity Score", f"{activity_score}/25")
# Recommendations
st.markdown("### πŸ’‘ Personalized Health Recommendations")
recommendations = []
if bmi_score < 25:
recommendations.append("πŸ‹οΈ Focus on achieving a healthy BMI through balanced nutrition and exercise")
if sugar_score < 25:
recommendations.append("🍎 Monitor blood sugar levels and consider dietary adjustments")
if bp_score < 25:
recommendations.append("🩺 Keep track of blood pressure and practice stress management")
if not recommendations:
recommendations.append("πŸŽ‰ Excellent health metrics! Continue your healthy lifestyle.")
for rec in recommendations:
st.success(rec)
def create_dashboard_home():
"""Main dashboard home page"""
display_main_header()
if not st.session_state.current_user:
# Welcome screen
st.markdown("## 🌟 Welcome to MedAI Suite")
col1, col2 = st.columns([2, 1])
with col1:
st.markdown("""
<div class="feature-card">
<h3>πŸš€ Your AI-Powered Health Platform</h3>
<p>Get started with comprehensive healthcare AI tools:</p>
<ul>
<li><strong>πŸ€– Medical Consultation:</strong> Personalized health guidance from AI</li>
<li><strong>πŸ“‹ Report Analysis:</strong> AI analysis of blood tests and medical reports</li>
<li><strong>πŸ‘οΈ Eye Screening:</strong> Diabetic retinopathy detection using custom AI</li>
<li><strong>πŸ“Š Health Analytics:</strong> Track and visualize your health metrics</li>
</ul>
<h4>πŸ‘€ Create your patient profile to begin!</h4>
</div>
""", unsafe_allow_html=True)
with col2:
st.markdown("### 🎯 Quick Start")
if st.button("πŸ‘€ Create Patient Profile", use_container_width=True, type="primary"):
st.session_state.page = "πŸ‘€ Patient Profile"
st.rerun()
st.markdown("### πŸ”§ System Status")
status_items = [
("πŸ€– AI Consultation", "Ready"),
("πŸ“‹ Report Analysis", "Ready"),
("πŸ‘οΈ Eye Screening", "Ready" if st.session_state.medical_ai.dr_model_loaded else "Loading"),
("πŸ“Š Health Analytics", "Ready")
]
for item, status in status_items:
color = "🟒" if status == "Ready" else "🟑"
st.markdown(f"{color} {item}: {status}")
# Feature showcase
st.markdown("## πŸ₯ Platform Features")
col1, col2, col3, col4 = st.columns(4)
features = [
("πŸ€–", "AI Consultation", "Get personalized medical advice"),
("πŸ“‹", "Report Analysis", "Understand your medical reports"),
("πŸ‘οΈ", "Eye Screening", "Detect diabetic retinopathy"),
("πŸ“Š", "Health Analytics", "Track your health progress")
]
for col, (icon, title, desc) in zip([col1, col2, col3, col4], features):
with col:
st.markdown(f"""
<div class="feature-card" style="text-align: center; height: 200px; display: flex; flex-direction: column; justify-content: center;">
<h1 style="font-size: 3rem; margin: 0;">{icon}</h1>
<h4 style="margin: 0.5rem 0;">{title}</h4>
<p style="margin: 0; font-size: 0.9rem;">{desc}</p>
</div>
""", unsafe_allow_html=True)
else:
# User dashboard
user = st.session_state.current_user
st.markdown(f"## πŸ‘‹ Welcome back, {user.name}!")
# Quick stats
col1, col2, col3, col4 = st.columns(4)
consultations = len(st.session_state.medical_ai.get_conversation_history(user.user_id))
days_active = (datetime.datetime.now() - datetime.datetime.fromisoformat(user.created_at)).days
with col1:
st.metric("πŸ’¬ Consultations", consultations)
with col2:
st.metric("πŸ“… Days Active", days_active)
with col3:
st.metric("🎯 Health Score", "85/100")
with col4:
st.metric("βš–οΈ BMI Status", st.session_state.medical_ai.get_bmi_category(user.bmi))
# Quick actions
st.markdown("### ⚑ Quick Actions")
col1, col2, col3, col4 = st.columns(4)
action_buttons = [
("πŸ€– AI Consultation", "πŸ€– Medical Consultation"),
("πŸ“‹ Analyze Report", "πŸ“‹ Report Analysis"),
("πŸ‘οΈ Eye Screening", "πŸ‘οΈ Eye Screening"),
("πŸ“Š Health Dashboard", "πŸ“Š Health Analytics")
]
for col, (title, page) in zip([col1, col2, col3, col4], action_buttons):
with col:
if st.button(title, use_container_width=True):
st.session_state.page = page
st.rerun()
# Recent activity
st.markdown("### πŸ“‹ Recent Activity")
recent_history = st.session_state.medical_ai.get_conversation_history(user.user_id, limit=3)
if recent_history:
for i, conv in enumerate(recent_history[::-1], 1):
with st.expander(f"πŸ’¬ Consultation {i} - {conv['timestamp'][:16]}"):
st.markdown(f"**Question:** {conv['question'][:150]}...")
st.markdown(f"**Response:** {conv['answer'][:200]}...")
else:
st.info("πŸ” No consultations yet. Start your first AI consultation!")
# =============================================================================
# MAIN APPLICATION LOGIC
# =============================================================================
def main():
"""Main application entry point"""
# Load custom CSS
load_custom_css()
# Initialize session state
initialize_session_state()
# Create sidebar
create_sidebar()
# Route to appropriate page
page = st.session_state.page
if page == "πŸ₯ Dashboard":
create_dashboard_home()
elif page == "πŸ‘€ Patient Profile":
if st.session_state.current_user:
display_patient_profile()
st.markdown("---")
if st.button("✏️ Create New Profile", type="secondary"):
st.session_state.current_user = None
st.session_state.chat_history = []
st.rerun()
else:
create_patient_profile_form()
elif page == "πŸ€– Medical Consultation":
create_medical_consultation()
elif page == "πŸ“‹ Report Analysis":
create_report_analysis()
elif page == "πŸ‘οΈ Eye Screening":
create_diabetic_retinopathy_screening()
elif page == "πŸ“Š Health Analytics":
create_health_analytics()
else:
create_dashboard_home()
# =============================================================================
# APPLICATION ENTRY POINT
# =============================================================================
if __name__ == "__main__":
main()