""" FocusTrack - Page 1: Live Dashboard Real-time activity overview with charts and controls. """ import streamlit as st import plotly.express as px import plotly.graph_objects as go import pandas as pd from datetime import datetime, timedelta, date from ui_utils import ( fmt_duration, fmt_duration_long, get_focus_score, privacy_badge, page_header, status_dot, category_pill, CATEGORY_COLORS, get_db ) def render(): db = get_db() privacy_badge() page_header("Live Dashboard", "Real-time activity overview") # ── Current Session Card ────────────────────────────────────────────────── latest = db.get_latest_activity() tracker_status = db.get_setting("tracker_running", "true") status_label = { "true": ("running", "#10b981"), "paused": ("paused", "#f59e0b"), "false": ("stopped", "#f43f5e"), }.get(tracker_status, ("unknown", "#6366f1")) app_name = latest.get("app_name", "—") if latest else "—" window = latest.get("window_title", "—") if latest else "—" category = latest.get("category", "—") if latest else "—" cat_color = CATEGORY_COLORS.get(category, "#6366f1") st.markdown(f"""
NOW TRACKING ● {status_label[0].upper()}
{app_name[:60]}
{window[:80]}
{category}
{datetime.now().strftime("%H:%M:%S")}
""", unsafe_allow_html=True) # ── Summary Metrics ──────────────────────────────────────────────────────── today_start = datetime.combine(date.today(), datetime.min.time()) today_end = datetime.combine(date.today(), datetime.max.time()) summary = db.get_summary(today_start, today_end) focus_sec = summary.get("focus_seconds") or 0 idle_sec = summary.get("idle_seconds") or 0 total_sec = summary.get("total_seconds") or 0 focus_score = get_focus_score(focus_sec, total_sec) top_apps = db.get_by_app(today_start, today_end, limit=1) top_app = top_apps[0]["app_name"] if top_apps else "—" c1, c2, c3, c4 = st.columns(4) with c1: st.metric("🎯 Focus Today", fmt_duration_long(focus_sec), delta=f"+{fmt_duration(focus_sec)}" if focus_sec > 0 else None) with c2: st.metric("💤 Idle Time", fmt_duration_long(idle_sec)) with c3: pct_color = "normal" if focus_score >= 60 else "inverse" st.metric("📊 Focus Score", f"{focus_score}%", delta="Good" if focus_score >= 60 else "Low", delta_color=pct_color) with c4: st.metric("🏆 Top App", top_app[:20] if top_app != "—" else "—") st.markdown("
", unsafe_allow_html=True) # ── Controls ────────────────────────────────────────────────────────────── st.markdown("**Tracker Controls**") col1, col2, col3, col4 = st.columns([1, 1, 1, 4]) with col1: if st.button("▶ Start", use_container_width=True): db.set_setting("tracker_running", "true") st.toast("✅ Tracker started!", icon="▶") st.rerun() with col2: if st.button("⏸ Pause", use_container_width=True): db.set_setting("tracker_running", "paused") st.toast("⏸ Tracker paused", icon="⏸") st.rerun() with col3: if st.button("⏹ Stop", use_container_width=True): db.set_setting("tracker_running", "false") st.toast("⏹ Tracker stopped", icon="⏹") st.rerun() st.markdown("
", unsafe_allow_html=True) # ── Charts Row ──────────────────────────────────────────────────────────── col_left, col_right = st.columns([1.1, 1.9]) with col_left: st.markdown("#### Time by Category") cat_data = db.get_by_category(today_start, today_end) if cat_data: df_cat = pd.DataFrame(cat_data) df_cat["minutes"] = df_cat["total_seconds"] / 60 df_cat["label"] = df_cat.apply( lambda r: f"{r['category']} ({fmt_duration(r['total_seconds'])})", axis=1 ) colors = [CATEGORY_COLORS.get(c, "#6366f1") for c in df_cat["category"]] fig = go.Figure(go.Pie( labels=df_cat["category"], values=df_cat["minutes"], hole=0.55, marker=dict(colors=colors, line=dict(width=2, color="#0a0a0f")), textinfo="label+percent", textfont=dict(size=11, family="Inter"), hovertemplate="%{label}
%{value:.0f} min
%{percent}", )) fig.update_layout( showlegend=False, margin=dict(t=20, b=20, l=20, r=20), height=300, paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font=dict(color="#9090b0", family="Inter"), annotations=[dict( text=f"{fmt_duration(focus_sec)}", x=0.5, y=0.5, font_size=16, font_family="Syne", font_color="#f0f0ff", showarrow=False )] ) st.plotly_chart(fig, use_container_width=True, config={"displayModeBar": False}) else: st.markdown("""
No data yet today. Start the tracker to begin.
""", unsafe_allow_html=True) with col_right: st.markdown("#### Hourly Activity Timeline") hourly = db.get_hourly_timeline(date.today()) if hourly: df_h = pd.DataFrame(hourly) df_h["hour_label"] = df_h["hour"].apply(lambda h: f"{int(h):02d}:00") df_h["minutes"] = df_h["total_seconds"] / 60 df_h["color"] = df_h["category"].map(lambda c: CATEGORY_COLORS.get(c, "#6366f1")) fig2 = px.bar( df_h, x="hour_label", y="minutes", color="category", color_discrete_map=CATEGORY_COLORS, labels={"hour_label": "Hour", "minutes": "Minutes"}, barmode="stack", ) fig2.update_layout( margin=dict(t=20, b=30, l=0, r=0), height=300, paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font=dict(color="#9090b0", family="Inter"), xaxis=dict(showgrid=False, tickfont=dict(size=10)), yaxis=dict(showgrid=True, gridcolor="#2a2a3a", tickfont=dict(size=10)), legend=dict( orientation="h", yanchor="bottom", y=1.02, xanchor="left", x=0, font=dict(size=10) ), bargap=0.15, ) st.plotly_chart(fig2, use_container_width=True, config={"displayModeBar": False}) else: st.markdown("""
No hourly data yet for today.
""", unsafe_allow_html=True) # ── Top Apps Table ──────────────────────────────────────────────────────── st.markdown("#### Top Apps Today") top_apps_data = db.get_by_app(today_start, today_end, limit=8) if top_apps_data: df_apps = pd.DataFrame(top_apps_data) df_apps["time"] = df_apps["total_seconds"].apply(fmt_duration_long) df_apps["pct"] = df_apps["total_seconds"].apply( lambda s: f"{(s / focus_sec * 100):.0f}%" if focus_sec > 0 else "—" ) # Render as styled rows html_rows = "" for _, row in df_apps.iterrows(): cat = row.get("category", "uncategorized") color = CATEGORY_COLORS.get(cat, "#6366f1") bar_w = int((row["total_seconds"] / (df_apps["total_seconds"].max() or 1)) * 100) html_rows += f""" {row['app_name'][:40]} {cat} {row['time']}
""" st.markdown(f""" {html_rows}
App Category Time Share
""", unsafe_allow_html=True) else: st.markdown("

No app data yet. Start tracking!

", unsafe_allow_html=True) # Auto-refresh every 10 seconds if st.session_state.get("auto_refresh", True): import time st.markdown(""" """, unsafe_allow_html=True)