Merge remote-tracking branch 'origin/feat/study-memo-card-p1' into feat/email-pkm-folder

This commit is contained in:
hyungi
2026-06-07 02:45:15 +00:00
2 changed files with 19 additions and 10 deletions
+16 -9
View File
@@ -16,7 +16,7 @@ from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import func, or_, select, update
from sqlalchemy import and_, func, or_, select, update
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
@@ -261,24 +261,31 @@ async def due_cards(
session: Annotated[AsyncSession, Depends(get_session)],
limit: Annotated[int, Query(ge=1, le=200)] = 30,
):
"""오늘 복습할 카드 (due_at<=now, stage<4, 검수 통과만). due_at 오름차순."""
"""오늘 복습할 카드 (검수 통과만). 두 부류:
- 신규 승인 카드(progress 없음=첫 회상 전) — SR 큐 진입 경로(첫 회상). ''이면 due 안
박고 종료('큐 폭발 방지'), 애매/모름이면 평가 즉시 due(내일)로 입고.
- 예정 due 카드(due_at<=now, stage<4).
progress 는 user+card UNIQUE 라 outer join 으로 최대 1행. 예정 due 먼저, 신규(due NULL) 뒤로."""
now = datetime.now(timezone.utc)
P = StudyMemoCardProgress
rows = (
await session.execute(
select(StudyMemoCard, StudyMemoCardProgress.review_stage)
.join(StudyMemoCardProgress, StudyMemoCardProgress.card_id == StudyMemoCard.id)
select(StudyMemoCard, P.review_stage)
.outerjoin(P, and_(P.card_id == StudyMemoCard.id, P.user_id == user.id))
.where(
StudyMemoCard.user_id == user.id,
StudyMemoCard.deleted_at.is_(None),
StudyMemoCard.needs_review.is_(False),
StudyMemoCardProgress.due_at.is_not(None),
StudyMemoCardProgress.due_at <= now,
or_(
StudyMemoCardProgress.review_stage.is_(None),
StudyMemoCardProgress.review_stage < 4,
P.id.is_(None), # 신규(첫 회상 전) — progress 미생성
and_(
P.due_at.is_not(None),
P.due_at <= now,
or_(P.review_stage.is_(None), P.review_stage < 4),
),
),
)
.order_by(StudyMemoCardProgress.due_at.asc())
.order_by(P.due_at.asc().nulls_last(), StudyMemoCard.id.asc())
.limit(limit)
)
).all()
@@ -22,9 +22,11 @@
import EmptyState from '$lib/components/ui/EmptyState.svelte';
// sr_schedule.py 단일 source 미러 — '암'(correct) 다음 복습일 라벨 전용(실제 스케줄은 백엔드).
// stage===null = 신규 카드(progress 없음): '암'이면 백엔드가 due 안 박음(외움→큐 제외)이라 '안 나옴'.
const REVIEW_INTERVAL_DAYS = { 1: 3, 2: 7, 3: 14 };
function correctLabel(stage) {
const ns = (stage ?? 0) + 1;
if (stage === null || stage === undefined) return '안 나옴';
const ns = stage + 1;
if (ns >= 4) return '졸업';
return `+${REVIEW_INTERVAL_DAYS[ns]}일`;
}