SmartHealthAI / app.py
jsakshi's picture
Update app.py
3e1f533 verified
"""
Streamlit frontend for Smart Health Monitoring Agent
"""
import streamlit as st
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from datetime import datetime
import sys
import os
import numpy as np
# Page configuration - MUST be first Streamlit command
st.set_page_config(
page_title="Smart Health Monitoring Agent",
page_icon="πŸ₯",
layout="wide",
initial_sidebar_state="expanded"
)
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Import custom modules with error handling
try:
from services.analysis_service import HealthAnalysisService
from models.health import HealthAnalysis
except ImportError as e:
st.error(f"❌ Import Error: {e}")
st.info("Make sure the 'services' and 'models' folders exist with the required files.")
st.stop()
# Custom CSS
st.markdown("""
<style>
.main-header {
font-size: 3rem;
font-weight: bold;
color: #1f77b4;
text-align: center;
margin-bottom: 2rem;
}
.metric-card {
background-color: #f0f2f6;
padding: 1.5rem;
border-radius: 10px;
border-left: 5px solid #1f77b4;
}
.alert-critical {
background-color: #ffe5e5;
color: #7f0000;
border-left: 5px solid #b71c1c;
padding: 1rem;
border-radius: 5px;
margin: 0.5rem 0;
}
.alert-warning {
background-color: #fff0d1;
color: #7a4b00;
border-left: 5px solid #e69a00;
padding: 1rem;
border-radius: 5px;
margin: 0.5rem 0;
}
.alert-info {
background-color: #e6f3ff;
color: #0d47a1;
border-left: 5px solid #1e88e5;
padding: 1rem;
border-radius: 5px;
margin: 0.5rem 0;
}
.stress-low { color: #28a745; font-weight: bold; }
.stress-moderate { color: #ffc107; font-weight: bold; }
.stress-high { color: #dc3545; font-weight: bold; }
</style>
""", unsafe_allow_html=True)
# Initialize session state
if 'analysis' not in st.session_state:
st.session_state.analysis = None
if 'records_df' not in st.session_state:
st.session_state.records_df = None
# Helper functions
def summarize_heart_rate_zones(df: pd.DataFrame) -> pd.DataFrame:
"""Compute HR zone distribution for visualization."""
def hr_zone(hr: float) -> str:
if hr < 60:
return "Resting"
elif hr < 90:
return "Light"
elif hr < 120:
return "Moderate"
elif hr < 150:
return "Intense"
else:
return "Peak"
zones = df["Heart Rate (bpm)"].apply(hr_zone).value_counts().reset_index()
zones.columns = ["Zone", "Count"]
return zones
def recommend_music_bpm(steps: float, hr: float) -> dict:
"""Lightweight music pacing recommendation."""
if steps > 9000 or hr >= 140:
bpm = 150
mood = "High-energy run"
elif steps > 6000 or hr >= 120:
bpm = 140
mood = "Tempo / steady cardio"
elif steps > 4000 or hr >= 100:
bpm = 125
mood = "Brisk walk / light jog"
else:
bpm = 105
mood = "Recovery / focus"
return {"bpm": bpm, "mood": mood}
def stress_intervention_tip(stress_level: str) -> str:
"""Return an immediate intervention tip based on stress level."""
if stress_level == "High":
return "Start 4-7-8 breathing now. Pause movement and hydrate."
elif stress_level == "Moderate":
return "Take 3 slow belly breaths and do a 60-second body scan."
else:
return "Keep up the calm pace. Maintain hydration."
# Header
st.markdown('<h1 class="main-header">πŸ₯ Smart Health Monitoring Agent</h1>', unsafe_allow_html=True)
st.markdown("---")
# Sidebar
with st.sidebar:
st.header("πŸ“€ Upload Health Data")
st.markdown("Upload a CSV file containing your health metrics.")
uploaded_file = st.file_uploader(
"Choose a CSV file",
type=['csv'],
help="CSV should contain: Date, Steps, Heart Rate, Calories, Sleep Duration"
)
if uploaded_file is not None:
try:
with st.spinner("Analyzing your health data..."):
service = HealthAnalysisService()
uploaded_file.seek(0)
file_content = uploaded_file.read()
analysis = service.analyze_csv_file(file_content=file_content)
st.session_state.analysis = analysis
# Create DataFrame for display
records_data = []
for record in analysis.records:
stress = analysis.stress_levels[record.date]
sleep = analysis.sleep_qualities[record.date]
records_data.append({
'Date': record.date,
'Steps': record.steps,
'Heart Rate (bpm)': record.heart_rate,
'Calories': record.calories,
'Sleep (hours)': record.sleep_duration,
'Stress Level': stress.level,
'Stress Score': f"{stress.score:.2f}",
'Sleep Quality': sleep.rating,
'Sleep Score': sleep.score
})
st.session_state.records_df = pd.DataFrame(records_data)
st.success("βœ… Data analyzed successfully!")
st.balloons()
except Exception as e:
st.error(f"❌ Error: {str(e)}")
st.session_state.analysis = None
st.session_state.records_df = None
# Main content
if st.session_state.analysis is None:
# Landing page
st.markdown("""
### Welcome to Smart Health Monitoring Agent
This AI-powered tool analyzes your smartwatch/fitness data and provides:
- **Stress Level Predictions** - Based on heart rate, activity, and sleep patterns
- **Sleep Quality Analysis** - Comprehensive sleep assessment
- **Health Risk Alerts** - Intelligent alerts for potential health issues
- **Trend Analysis** - Visual insights into your health patterns
#### How to use:
1. Upload a CSV file with your health data using the sidebar
2. Required columns: Date, Steps, Heart Rate, Calories, Sleep Duration
3. View your personalized health dashboard
#### Sample CSV Format:
```csv
Date,Steps,Heart Rate,Calories,Sleep Duration
2024-12-01,8500,72,2100,7.5
2024-12-02,10200,68,2300,8.0
```
""")
# Show sample data preview
st.markdown("### πŸ“Š Sample Data Preview")
sample_data = pd.DataFrame({
'Date': ['2024-12-01', '2024-12-02', '2024-12-03'],
'Steps': [8500, 10200, 6800],
'Heart Rate': [72, 68, 78],
'Calories': [2100, 2300, 1950],
'Sleep Duration': [7.5, 8.0, 6.5]
})
st.dataframe(sample_data, width="stretch")
else:
analysis = st.session_state.analysis
records_df = st.session_state.records_df
latest_record = analysis.records[-1]
latest_stress = analysis.stress_levels[latest_record.date]
hr_zones_df = summarize_heart_rate_zones(records_df)
music_rec = recommend_music_bpm(latest_record.steps, latest_record.heart_rate)
# Key Metrics Cards
st.header("πŸ“Š Key Metrics")
col1, col2, col3, col4, col5 = st.columns(5)
with col1:
st.metric(
"Average Steps",
f"{analysis.metrics.average_steps:,.0f}",
delta=f"{analysis.metrics.average_steps - 10000:,.0f}" if analysis.metrics.average_steps < 10000 else None
)
with col2:
st.metric(
"Avg Heart Rate",
f"{analysis.metrics.average_heart_rate:.1f} bpm",
delta=f"{analysis.metrics.average_heart_rate - 70:.1f}" if analysis.metrics.average_heart_rate > 70 else None
)
with col3:
st.metric(
"Avg Calories",
f"{analysis.metrics.average_calories:,.0f}",
)
with col4:
st.metric(
"Avg Sleep",
f"{analysis.metrics.average_sleep:.1f} hrs",
delta=f"{analysis.metrics.average_sleep - 8:.1f}" if analysis.metrics.average_sleep < 8 else None
)
with col5:
# Health Score Gauge
health_score = analysis.health_score
color = "#28a745" if health_score >= 75 else "#ffc107" if health_score >= 50 else "#dc3545"
fig_gauge = go.Figure(go.Indicator(
mode="gauge+number",
value=health_score,
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': "Health Score"},
gauge={
'axis': {'range': [None, 100]},
'bar': {'color': color},
'steps': [
{'range': [0, 50], 'color': "lightgray"},
{'range': [50, 75], 'color': "gray"}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 90
}
}
))
fig_gauge.update_layout(height=200, margin=dict(l=0, r=0, t=0, b=0))
st.plotly_chart(fig_gauge, width="stretch")
st.markdown("---")
# Smart Features
st.header("πŸš€ Smart Boosters")
col1, col2, col3 = st.columns(3)
with col1:
st.subheader("🎢 Workout Music Optimizer")
st.write(f"Current vibe: **{music_rec['mood']}**")
st.write(f"Recommended BPM: **{music_rec['bpm']}**")
st.caption("Tip: Match your stride to the beat for smoother pacing.")
st.progress(min(music_rec["bpm"] / 180, 1.0))
with col2:
st.subheader("🧘 Real-time Stress Intervention")
st.write(f"Latest HR: **{latest_record.heart_rate:.0f} bpm**")
st.write(f"Stress: **{latest_stress.level} ({latest_stress.score:.2f})**")
st.info(stress_intervention_tip(latest_stress.level))
with col3:
st.subheader("🩺 Doctor Report Highlights")
st.write("Top talking points:")
bullets = [
f"- Avg HR: {analysis.metrics.average_heart_rate:.1f} bpm",
f"- Sleep: {analysis.metrics.average_sleep:.1f} hrs/night",
f"- Steps: {analysis.metrics.average_steps:,.0f} /day"
]
st.markdown("\n".join(bullets))
st.caption("Download full CSV for detailed review.")
st.markdown("---")
# Future Self Motivation
st.header("🌟 Design Your Future Self")
target = {
"steps": 10000,
"sleep": 8.0,
"hr": 68.0,
"stress": 0.25
}
col_fs1, col_fs2 = st.columns(2)
with col_fs1:
st.subheader("\"Future You\" (90 days)")
st.write("Identity-based goals set by your future self:")
st.markdown(
f"- Daily steps: **{target['steps']:,}**\n"
f"- Sleep: **{target['sleep']} hrs/night**\n"
f"- Resting HR target: **{target['hr']:.0f} bpm**\n"
f"- Stress score target: **{target['stress']:.2f}** (Low)\n"
)
encouragement = [
'Future You says: "Keep compounding the small wins."',
"Today's recovery is tomorrow's performance.",
"You're 1% closer every consistent day."
]
st.success(encouragement[len(analysis.records) % len(encouragement)])
with col_fs2:
st.subheader("Progress Toward Future You")
steps_progress = min(analysis.metrics.average_steps / target["steps"], 1.0)
st.markdown("**Steps**")
st.progress(steps_progress, text=f"{analysis.metrics.average_steps:,.0f} / {target['steps']:,}")
sleep_progress = min(analysis.metrics.average_sleep / target["sleep"], 1.0)
st.markdown("**Sleep**")
st.progress(sleep_progress, text=f"{analysis.metrics.average_sleep:.1f} / {target['sleep']:.1f} hrs")
hr_progress = min(target["hr"] / max(analysis.metrics.average_heart_rate, 1), 1.0)
st.markdown("**Resting Heart Rate**")
st.progress(hr_progress, text=f"{analysis.metrics.average_heart_rate:.1f} β†’ {target['hr']:.1f} bpm")
stress_avg = np.mean([s.score for s in analysis.stress_levels.values()])
stress_progress = min(target["stress"] / max(stress_avg, 0.01), 1.0)
st.markdown("**Stress Score**")
st.progress(stress_progress, text=f"{stress_avg:.2f} β†’ {target['stress']:.2f}")
st.markdown("---")
# Alerts Section
if analysis.alerts:
st.header("⚠️ Health Alerts")
for alert in analysis.alerts:
alert_class = f"alert-{alert.type}"
st.markdown(f"""
<div class="{alert_class}">
<strong>{alert.title}</strong><br>
{alert.message}<br>
<em>πŸ’‘ Recommendation: {alert.recommendation}</em>
</div>
""", unsafe_allow_html=True)
st.markdown("---")
# Charts Section
st.header("πŸ“ˆ Health Trends")
chart_data = records_df.copy()
chart_data['Date'] = pd.to_datetime(chart_data['Date'])
chart_data = chart_data.sort_values('Date')
col1, col2 = st.columns(2)
with col1:
fig_steps = px.line(
chart_data,
x='Date',
y='Steps',
title='Daily Steps Trend',
markers=True
)
fig_steps.update_layout(height=300)
st.plotly_chart(fig_steps, width="stretch")
with col2:
fig_hr = px.line(
chart_data,
x='Date',
y='Heart Rate (bpm)',
title='Heart Rate Trend',
markers=True,
color_discrete_sequence=['red']
)
fig_hr.update_layout(height=300)
st.plotly_chart(fig_hr, width="stretch")
col1, col2 = st.columns(2)
with col1:
fig_calories = px.bar(
chart_data,
x='Date',
y='Calories',
title='Daily Calorie Burn',
color='Calories',
color_continuous_scale='Blues'
)
fig_calories.update_layout(height=300)
st.plotly_chart(fig_calories, width="stretch")
with col2:
fig_sleep = px.bar(
chart_data,
x='Date',
y='Sleep (hours)',
title='Sleep Duration',
color='Sleep (hours)',
color_continuous_scale='Purples'
)
fig_sleep.update_layout(height=300)
st.plotly_chart(fig_sleep, width="stretch")
st.subheader("πŸ“Š Deeper Insights")
col_a, col_b = st.columns(2)
with col_a:
fig_zone = px.pie(
hr_zones_df,
values="Count",
names="Zone",
title="Heart Rate Zone Distribution",
color_discrete_sequence=px.colors.sequential.Blues
)
fig_zone.update_layout(height=340)
st.plotly_chart(fig_zone, width="stretch")
with col_b:
fig_corr = px.scatter(
chart_data,
x="Steps",
y="Calories",
color="Sleep (hours)",
size="Heart Rate (bpm)",
title="Steps vs Calories (bubble sized by HR, colored by Sleep)",
color_continuous_scale="Viridis"
)
fig_corr.update_layout(height=340)
st.plotly_chart(fig_corr, width="stretch")
st.markdown("---")
# AI Insights Panel
st.header("πŸ€– AI Insights")
col1, col2 = st.columns(2)
with col1:
st.subheader("Stress Level Predictions")
for record in analysis.records[-7:]:
stress = analysis.stress_levels[record.date]
stress_class = f"stress-{stress.level.lower()}"
st.markdown(f"**{record.date}**")
st.markdown(f'<span class="{stress_class}">{stress.level}</span> (Score: {stress.score:.2f}, Confidence: {stress.confidence*100:.0f}%)', unsafe_allow_html=True)
st.progress(stress.score, text=f"{stress.score*100:.0f}%")
with col2:
st.subheader("Sleep Quality Analysis")
for record in analysis.records[-7:]:
sleep = analysis.sleep_qualities[record.date]
sleep_color = "#28a745" if sleep.rating == "Excellent" else "#17a2b8" if sleep.rating == "Good" else "#ffc107" if sleep.rating == "Fair" else "#dc3545"
st.markdown(f"**{record.date}**")
st.markdown(f'<span style="color: {sleep_color}; font-weight: bold;">{sleep.rating}</span> (Score: {sleep.score:.0f}/100)', unsafe_allow_html=True)
st.progress(sleep.score / 100, text=f"{sleep.score:.0f}%")
st.markdown("---")
# Historical Trends
st.header("πŸ“… Historical Analysis")
service = HealthAnalysisService()
weekly_comparison = service.get_weekly_comparison(analysis.records)
best_worst = service.get_best_worst_days(analysis.records)
if weekly_comparison and weekly_comparison.get('changes'):
st.subheader("Week-over-Week Comparison")
changes = weekly_comparison['changes']
col1, col2, col3, col4 = st.columns(4)
with col1:
delta = changes.get('avg_steps', 0)
st.metric("Steps Change", f"{delta:+.1f}%")
with col2:
delta = changes.get('avg_hr', 0)
st.metric("Heart Rate Change", f"{delta:+.1f}%")
with col3:
delta = changes.get('avg_sleep', 0)
st.metric("Sleep Change", f"{delta:+.1f}%")
with col4:
delta = changes.get('avg_calories', 0)
st.metric("Calories Change", f"{delta:+.1f}%")
if best_worst:
col1, col2 = st.columns(2)
with col1:
if best_worst.get('best'):
st.subheader("πŸ† Best Day")
best = best_worst['best']
st.write(f"**Date:** {best['date']}")
st.write(f"**Health Score:** {best['score']:.1f}")
st.write(f"Steps: {best['record'].steps:,.0f} | HR: {best['record'].heart_rate:.0f} bpm | Sleep: {best['record'].sleep_duration:.1f} hrs")
with col2:
if best_worst.get('worst'):
st.subheader("⚠️ Needs Improvement")
worst = best_worst['worst']
st.write(f"**Date:** {worst['date']}")
st.write(f"**Health Score:** {worst['score']:.1f}")
st.write(f"Steps: {worst['record'].steps:,.0f} | HR: {worst['record'].heart_rate:.0f} bpm | Sleep: {worst['record'].sleep_duration:.1f} hrs")
st.markdown("---")
# Data Table
st.header("πŸ“‹ Detailed Data")
st.dataframe(records_df, width="stretch", height=400)
# Download button
csv = records_df.to_csv(index=False)
st.download_button(
label="πŸ“₯ Download Analysis as CSV",
data=csv,
file_name=f"health_analysis_{datetime.now().strftime('%Y%m%d')}.csv",
mime="text/csv"
)