File size: 4,492 Bytes
f5e83c3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | """
utils/streak_manager.py β Streak calculation and update logic for FitPlan Pro.
Streak increases when user completes a full day (all workout + diet checkboxes).
Streak resets if a day is missed.
"""
from datetime import date, timedelta
from utils.db import get_streak, save_streak
def update_streak(username, day_completed: bool) -> int:
"""
Update streak based on whether today's plan was fully completed.
Rules:
- day_completed=True β increment streak (if consecutive) or start new one
- day_completed=False β if today was previously completed, decrement streak
- Missed yesterday β streak resets to 0 on next completion
Returns: updated current_streak (int)
"""
today = str(date.today())
yesterday = str(date.today() - timedelta(days=1))
streak = get_streak(username)
history = streak.get("streak_history", [])
last = streak.get("last_completed_date")
if day_completed:
# Add today to history if not already there
if today not in history:
history.append(today)
history.sort()
if last == yesterday:
# Consecutive day β extend streak
streak["current_streak"] += 1
elif last == today:
# Already counted today β no change
pass
else:
# Missed one or more days β start fresh
streak["current_streak"] = 1
streak["last_completed_date"] = today
streak["longest_streak"] = max(
streak.get("longest_streak", 0),
streak["current_streak"]
)
else:
# User unchecked something β today is no longer complete
if last == today and today in history:
history.remove(today)
streak["current_streak"] = max(0, streak["current_streak"] - 1)
streak["last_completed_date"] = history[-1] if history else None
streak["streak_history"] = history
save_streak(streak)
return streak["current_streak"]
def get_streak_display(username):
"""
Get formatted streak info for display in the UI.
Returns dict ready to pass to the dashboard.
"""
streak = get_streak(username)
current = streak.get("current_streak", 0)
longest = streak.get("longest_streak", 0)
last = streak.get("last_completed_date")
history = streak.get("streak_history", [])
today = str(date.today())
yesterday = str(date.today() - timedelta(days=1))
# Determine if streak is at risk (yesterday not completed)
if current > 0 and last not in [today, yesterday]:
status = "broken"
current = 0 # reset display β streak was broken
# Actually update DB too
_reset_streak(username, streak)
elif last == today:
status = "active"
elif last == yesterday:
status = "at_risk" # complete today to keep streak alive
else:
status = "inactive"
return {
"current_streak": current,
"longest_streak": longest,
"last_completed": last,
"status": status,
"completed_days": len(history),
"emoji": _streak_emoji(current)
}
def _reset_streak(username, streak):
"""Internal: reset broken streak in DB."""
streak["current_streak"] = 0
save_streak(streak)
def _streak_emoji(streak_count):
"""Return an appropriate emoji based on streak length."""
if streak_count == 0:
return "π€"
elif streak_count < 3:
return "π₯"
elif streak_count < 7:
return "π₯π₯"
elif streak_count < 14:
return "π₯π₯π₯"
elif streak_count < 30:
return "β‘π₯"
else:
return "ππ₯"
def check_streak_milestone(streak_count):
"""
Return a milestone message if user hit a notable streak.
Returns: str message or None
"""
milestones = {
3: "3-day streak! You're building a habit! πͺ",
7: "One full week! Incredible consistency! π
",
14: "Two weeks strong! You're unstoppable! β‘",
21: "21 days β habits are officially formed! π§ ",
30: "30-day streak! Elite level dedication! π",
50: "50 days! You're a fitness legend! π",
100: "100 DAYS! Absolute monster! π"
}
return milestones.get(streak_count) |