perf: connection pooling, query consolidation, inline chart data, batch milestones

This commit is contained in:
Peter Stockings
2026-02-24 21:41:55 +11:00
parent 56168a182b
commit c76b4cd6fc
8 changed files with 219 additions and 88 deletions

View File

@@ -4,7 +4,7 @@ Shared business-logic helpers.
Keep route handlers thin — calculation logic lives here.
"""
from app.db import query, execute
from app.db import query, execute_many
from app.config import SYDNEY_TZ
from datetime import datetime, timedelta
@@ -66,19 +66,11 @@ def calculate_weight_change(start_w, current_w):
# Streaks
# ---------------------------------------------------------------------------
def calculate_streak(user_id):
"""Calculate current and best consecutive-day check-in streaks."""
rows = query(
"""SELECT DISTINCT (checked_in_at AT TIME ZONE 'UTC' AT TIME ZONE 'Australia/Sydney')::date AS d
FROM checkins WHERE user_id = %s ORDER BY d DESC""",
(user_id,),
)
if not rows:
def _compute_streak_from_dates(days, today):
"""Compute current and best streak from a sorted-desc list of dates."""
if not days:
return {"current": 0, "best": 0}
days = [r["d"] for r in rows]
today = datetime.now(SYDNEY_TZ).date()
# Current streak: must include today or yesterday to count
current = 0
expected = today
@@ -105,6 +97,50 @@ def calculate_streak(user_id):
return {"current": current, "best": best}
def calculate_streak(user_id):
"""Calculate current and best consecutive-day check-in streaks."""
rows = query(
"""SELECT DISTINCT (checked_in_at AT TIME ZONE 'UTC' AT TIME ZONE 'Australia/Sydney')::date AS d
FROM checkins WHERE user_id = %s ORDER BY d DESC""",
(user_id,),
)
days = [r["d"] for r in rows]
today = datetime.now(SYDNEY_TZ).date()
return _compute_streak_from_dates(days, today)
def calculate_streaks_bulk(user_ids):
"""Calculate streaks for multiple users in a single query.
Returns a dict: {user_id: {"current": int, "best": int}}.
"""
if not user_ids:
return {}
placeholders = ",".join(["%s"] * len(user_ids))
rows = query(
f"""SELECT user_id,
(checked_in_at AT TIME ZONE 'UTC' AT TIME ZONE 'Australia/Sydney')::date AS d
FROM checkins
WHERE user_id IN ({placeholders})
GROUP BY user_id, d
ORDER BY user_id, d DESC""",
tuple(user_ids),
)
# Group by user
from collections import defaultdict
user_days = defaultdict(list)
for r in rows:
user_days[r["user_id"]].append(r["d"])
today = datetime.now(SYDNEY_TZ).date()
result = {}
for uid in user_ids:
result[uid] = _compute_streak_from_dates(user_days.get(uid, []), today)
return result
# ---------------------------------------------------------------------------
# Milestone checker
# ---------------------------------------------------------------------------
@@ -136,15 +172,12 @@ def check_milestones(user_id, user):
("lost_20kg", total_lost >= 20),
]
for key, achieved in milestone_checks:
if achieved:
try:
execute(
"INSERT INTO milestones (user_id, milestone_key) VALUES (%s, %s) ON CONFLICT DO NOTHING",
(user_id, key),
)
except Exception:
pass
achieved = [(user_id, key) for key, ok in milestone_checks if ok]
if achieved:
execute_many(
"INSERT INTO milestones (user_id, milestone_key) VALUES (%s, %s) ON CONFLICT DO NOTHING",
achieved,
)
# ---------------------------------------------------------------------------