126 lines
4.0 KiB
Python
126 lines
4.0 KiB
Python
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,
|
|
)
|