Spaces:
Runtime error
Runtime error
| """ | |
| 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" | |
| ) | |