from datetime import timezone from flask import Blueprint, render_template from app.auth import login_required, get_current_user from app.db import query, query_one from app.config import SYDNEY_TZ from app.utils import calculate_streak, calculate_weight_change bp = Blueprint("dashboard", __name__) @bp.route("/") @login_required def index(): user = get_current_user() uid = user["id"] # --- Single query: latest, first, count via window functions ---------- summary = query_one(""" SELECT total, first_weight, latest_weight, latest_bmi, latest_at FROM ( SELECT COUNT(*) OVER () AS total, FIRST_VALUE(weight_kg) OVER (ORDER BY checked_in_at ASC) AS first_weight, FIRST_VALUE(weight_kg) OVER (ORDER BY checked_in_at DESC) AS latest_weight, FIRST_VALUE(bmi) OVER (ORDER BY checked_in_at DESC) AS latest_bmi, FIRST_VALUE(checked_in_at) OVER (ORDER BY checked_in_at DESC) AS latest_at, ROW_NUMBER() OVER (ORDER BY checked_in_at DESC) AS rn FROM checkins WHERE user_id = %s ) sub WHERE rn = 1 """, (uid,)) # Build lightweight "latest" dict for the template latest = None weight_change = None weight_change_pct = None total_checkins = 0 if summary: total_checkins = summary["total"] latest = { "weight_kg": summary["latest_weight"], "bmi": summary["latest_bmi"], "checked_in_at": summary["latest_at"], } kg_lost, pct_lost = calculate_weight_change( summary["first_weight"], summary["latest_weight"] ) weight_change = round(-kg_lost, 1) weight_change_pct = round(-pct_lost, 1) # Recent check-ins (last 5) recent_checkins = query( "SELECT * FROM checkins WHERE user_id = %s ORDER BY checked_in_at DESC LIMIT 5", (uid,), ) # Activity feed (recent check-ins from all users) activity = query(""" SELECT c.*, u.display_name, u.username FROM checkins c JOIN users u ON c.user_id = u.id WHERE u.is_private = FALSE OR u.id = %s ORDER BY c.checked_in_at DESC LIMIT 10 """, (uid,)) # Milestones milestones = query( "SELECT * FROM milestones WHERE user_id = %s ORDER BY achieved_at DESC", (uid,), ) # Streak streak = calculate_streak(uid) # --- Pre-compute chart data (eliminates 2 client-side fetches) -------- chart_checkins = query( """SELECT weight_kg, bmi, checked_in_at FROM checkins WHERE user_id = %s ORDER BY checked_in_at ASC""", (uid,), ) chart_labels = [] chart_weights = [] weekly_labels = [] weekly_changes = [] for i, c in enumerate(chart_checkins): dt = c["checked_in_at"] if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) label = dt.astimezone(SYDNEY_TZ).strftime("%d %b") chart_labels.append(label) chart_weights.append(float(c["weight_kg"])) if i > 0: prev_w = float(chart_checkins[i - 1]["weight_kg"]) curr_w = float(c["weight_kg"]) weekly_labels.append(label) weekly_changes.append(round(curr_w - prev_w, 1)) return render_template( "dashboard.html", user=user, latest=latest, stats={"total_checkins": total_checkins}, weight_change=weight_change, weight_change_pct=weight_change_pct, recent_checkins=recent_checkins, activity=activity, milestones=milestones, streak=streak, chart_labels=chart_labels, chart_weights=chart_weights, weekly_labels=weekly_labels, weekly_changes=weekly_changes, )