add habit leveling/XP system, explicit fail marking for missed habits, and scrollable habit tabs
- Habits and the account now accumulate XP and levels from check streaks, with streak/level-up celebration banners and push notifications. - The "yesterday missed" banner on /today now lets a habit be explicitly marked failed (not just checked done), backed by a new HabitLog.status column so completion-rate/streak calculations never count a failed day as done. - The habit management tabs scroll horizontally on narrow screens instead of wrapping to two lines, with a pure-CSS edge shadow indicating more content.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
from app.schemas.level import LevelInfo
|
||||
|
||||
# 체크 1회당 얻는 기본 경험치.
|
||||
XP_PER_CHECK = 10
|
||||
|
||||
# 예정일을 놓쳤을 때의 페널티. 연속으로 놓칠수록 (miss_streak * MISS_PENALTY_BASE)로 가속되며,
|
||||
# MISS_PENALTY_CAP에서 더 이상 커지지 않는다(끝없이 방치된 습관이 비정상적인 값으로 폭주하지 않도록).
|
||||
MISS_PENALTY_BASE = 8
|
||||
MISS_PENALTY_CAP = 80
|
||||
|
||||
# 연속 달성일 마일스톤(log_service.MILESTONE_STREAKS와 동일한 값) 도달 시 추가로 얹어주는 보너스 경험치.
|
||||
STREAK_MILESTONE_BONUS_XP: dict[int, int] = {
|
||||
7: 20,
|
||||
30: 80,
|
||||
66: 150,
|
||||
100: 250,
|
||||
200: 500,
|
||||
365: 1000,
|
||||
}
|
||||
|
||||
# 레벨업에 필요한 경험치는 LEVEL_BASE_XP에서 시작해 레벨마다 LEVEL_STEP_XP씩 늘어난다
|
||||
# (초반엔 빠르게, 갈수록 완만하게 오르는 곡선).
|
||||
LEVEL_BASE_XP = 20
|
||||
LEVEL_STEP_XP = 10
|
||||
|
||||
|
||||
def _xp_required_for_level(level: int) -> int:
|
||||
"""`level`에서 `level + 1`로 올라가는 데 필요한 경험치."""
|
||||
return LEVEL_BASE_XP + LEVEL_STEP_XP * (level - 1)
|
||||
|
||||
|
||||
def level_from_xp(xp: int) -> LevelInfo:
|
||||
xp = max(xp, 0)
|
||||
level = 1
|
||||
remaining = xp
|
||||
while True:
|
||||
needed = _xp_required_for_level(level)
|
||||
if remaining < needed:
|
||||
progress_pct = round(remaining / needed * 100, 1)
|
||||
return LevelInfo(
|
||||
xp=xp,
|
||||
level=level,
|
||||
xp_into_level=remaining,
|
||||
xp_for_next_level=needed,
|
||||
progress_pct=progress_pct,
|
||||
)
|
||||
remaining -= needed
|
||||
level += 1
|
||||
Reference in New Issue
Block a user