Show daily streak count on leaderboard as well

This commit is contained in:
Peter Stockings
2026-02-24 21:12:45 +11:00
parent 9323082d37
commit f3abb4781b
4 changed files with 49 additions and 41 deletions

View File

@@ -1,51 +1,11 @@
from flask import Blueprint, render_template from flask import Blueprint, render_template
from app.auth import login_required, get_current_user from app.auth import login_required, get_current_user
from app.db import query, query_one from app.db import query, query_one
from app import SYDNEY_TZ from app.utils import calculate_streak
from datetime import datetime, timezone, timedelta
bp = Blueprint("dashboard", __name__) bp = Blueprint("dashboard", __name__)
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:
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
if days[0] == today or days[0] == today - timedelta(days=1):
expected = days[0]
for d in days:
if d == expected:
current += 1
expected -= timedelta(days=1)
else:
break
# Best streak
best = 1
run = 1
for i in range(1, len(days)):
if days[i] == days[i - 1] - timedelta(days=1):
run += 1
best = max(best, run)
else:
run = 1
best = max(best, current)
return {"current": current, "best": best}
@bp.route("/") @bp.route("/")
@login_required @login_required
def index(): def index():

View File

@@ -2,6 +2,7 @@ from flask import Blueprint, render_template
from app.auth import login_required from app.auth import login_required
from app.db import query, query_one from app.db import query, query_one
from app import SYDNEY_TZ from app import SYDNEY_TZ
from app.utils import calculate_streak
from datetime import timezone from datetime import timezone
bp = Blueprint("leaderboard", __name__) bp = Blueprint("leaderboard", __name__)
@@ -46,11 +47,13 @@ def index():
total_to_lose = start_w - goal total_to_lose = start_w - goal
goal_progress = min(100, round((weight_lost / total_to_lose) * 100, 1)) if total_to_lose > 0 else 0 goal_progress = min(100, round((weight_lost / total_to_lose) * 100, 1)) if total_to_lose > 0 else 0
streak = calculate_streak(u["id"])
ranked.append({ ranked.append({
**u, **u,
"weight_lost": round(weight_lost, 1), "weight_lost": round(weight_lost, 1),
"pct_lost": pct_lost, "pct_lost": pct_lost,
"goal_progress": goal_progress, "goal_progress": goal_progress,
"streak": streak["current"],
}) })
# Sort by % lost (descending) # Sort by % lost (descending)

View File

@@ -56,6 +56,7 @@
<th>Lost (%)</th> <th>Lost (%)</th>
<th>Goal Progress</th> <th>Goal Progress</th>
<th>Check-ins</th> <th>Check-ins</th>
<th>Streak</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -96,6 +97,8 @@
{% endif %} {% endif %}
</td> </td>
<td>{{ u.total_checkins }}</td> <td>{{ u.total_checkins }}</td>
<td>{% if u.streak > 0 %}<span style="color: var(--warning);">🔥 {{ u.streak }}d</span>{% else
%}<span style="color: var(--text-muted);"></span>{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

42
app/utils.py Normal file
View File

@@ -0,0 +1,42 @@
from app.db import query
from app import SYDNEY_TZ
from datetime import datetime, timedelta
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:
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
if days[0] == today or days[0] == today - timedelta(days=1):
expected = days[0]
for d in days:
if d == expected:
current += 1
expected -= timedelta(days=1)
else:
break
# Best streak
best = 1
run = 1
for i in range(1, len(days)):
if days[i] == days[i - 1] - timedelta(days=1):
run += 1
best = max(best, run)
else:
run = 1
best = max(best, current)
return {"current": current, "best": best}