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,32 @@
|
||||
"""normalize habit_type/status/difficulty column casing to lowercase
|
||||
|
||||
Revision ID: 0008_normalize_habit_enum_casing
|
||||
Revises: 0007_add_habit_difficulty
|
||||
Create Date: 2026-07-19
|
||||
|
||||
SQLAlchemy의 Enum 타입은 values_callable을 안 주면 기본적으로 enum 멤버의 .name(대문자, 예:
|
||||
"MEDIUM")을 DB에 저장하는데, 이 앱의 CHECK 제약조건과 difficulty 컬럼의 server_default는 전부
|
||||
소문자 .value(예: "medium")를 기준으로 작성되어 있었다. habit_type/status는 지금까지 ORM으로
|
||||
삽입된 대문자 값만 있어서 우연히 안 터졌고, difficulty는 마이그레이션이 직접 SQL로 넣은 소문자
|
||||
기본값과 섞여 LookupError가 발생했다. 모델 쪽은 values_callable을 추가해 앞으로는 항상 소문자로
|
||||
쓰도록 고쳤고, 이 마이그레이션은 기존에 대문자로 저장된 행을 소문자로 정규화한다.
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0008_normalize_habit_enum_casing"
|
||||
down_revision: Union[str, None] = "0007_add_habit_difficulty"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute("UPDATE habit SET habit_type = LOWER(habit_type)")
|
||||
op.execute("UPDATE habit SET status = LOWER(status)")
|
||||
op.execute("UPDATE habit SET difficulty = LOWER(difficulty)")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# 대소문자 정규화는 되돌릴 필요가 없는 데이터 정리이므로 downgrade는 아무 것도 하지 않는다.
|
||||
pass
|
||||
@@ -0,0 +1,32 @@
|
||||
"""add status (done/failed) to habit_log
|
||||
|
||||
Revision ID: 0009_add_habit_log_status
|
||||
Revises: 0008_normalize_habit_enum_casing
|
||||
Create Date: 2026-07-20
|
||||
|
||||
"어제 놓친 습관" 배너에서 사용자가 명시적으로 실패를 확정할 수 있게 하기 위해, "로그 행이 존재하면
|
||||
그 날 체크 완료"라는 기존 불변식을 유지하면서도 "완료"와 "실패"를 구분해야 한다. status 컬럼을
|
||||
추가하고 기존 행은 전부 완료였으므로 기본값 'done'으로 채운다.
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0009_add_habit_log_status"
|
||||
down_revision: Union[str, None] = "0008_normalize_habit_enum_casing"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"habit_log",
|
||||
sa.Column("status", sa.String(length=20), nullable=False, server_default="done"),
|
||||
)
|
||||
op.create_check_constraint("ck_habit_log_status", "habit_log", "status in ('done','failed')")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_constraint("ck_habit_log_status", "habit_log", type_="check")
|
||||
op.drop_column("habit_log", "status")
|
||||
Reference in New Issue
Block a user