워크시트 검수 UI와 세션 다크톤 정리

This commit is contained in:
Yun Chan 2026-06-28 23:53:37 +09:00
parent f7aae885b2
commit 30441bd93b
7 changed files with 404 additions and 121 deletions

View file

@ -10,6 +10,7 @@ from pydantic import BaseModel, Field
from .. import session_persistence
from ..deps import Principal, Role, require_role
from ..runtime_policy import require_runtime_fallback_allowed
from ..session_read_model import StageLabel, stage_label
from ..services import session_metrics
from ..store import InProcSession, store
@ -26,7 +27,7 @@ class TeacherSessionSummary(BaseModel):
persona_name: str
session_no: int
status: str
stage: str
stage: StageLabel
turn_count: int
learner_turn_count: int
client_turn_count: int
@ -40,6 +41,8 @@ class TeacherSessionSummary(BaseModel):
class TeacherSessionReviewStatusRequest(BaseModel):
status: Literal["viewed", "closed"] = "closed"
note: str = Field(default="", max_length=2000)
worksheet_status: Literal["approved", "changes_requested", "rejected", "pending"] | None = None
worksheet_note: str | None = Field(default=None, max_length=2000)
class TeacherSessionReviewStatusResponse(BaseModel):
@ -49,13 +52,16 @@ class TeacherSessionReviewStatusResponse(BaseModel):
reviewer_id: str | None = None
reviewed_at: str | None = None
updated_at: str | None = None
worksheet_status: Literal["pending", "approved", "changes_requested", "rejected"] = "pending"
worksheet_note: str = ""
worksheet_reviewed_at: str | None = None
class TeacherGrowthPoint(BaseModel):
session_id: str
session_no: int
persona_code: str
stage: str
stage: StageLabel
started_at: str
ended_at: str | None = None
score: float | None = None
@ -118,7 +124,7 @@ def _growth_point(point: session_metrics.SessionGrowthPoint) -> TeacherGrowthPoi
session_id=point.session_id,
session_no=point.session_no,
persona_code=point.persona_code,
stage=point.stage,
stage=stage_label(point.stage),
started_at=point.started_at,
ended_at=point.ended_at,
score=point.score,
@ -161,6 +167,15 @@ def _review_status_value(record: dict[str, object] | None) -> Literal["pending",
return "pending"
def _worksheet_review_status_value(
record: dict[str, object] | None,
) -> Literal["pending", "approved", "changes_requested", "rejected"]:
value = str((record or {}).get("worksheet_status") or "pending")
if value in {"approved", "changes_requested", "rejected"}:
return value # type: ignore[return-value]
return "pending"
def _summary(
sess: InProcSession,
review_status: dict[str, object] | None = None,
@ -175,7 +190,7 @@ def _summary(
persona_name=sess.persona.display_name,
session_no=sess.session_no,
status="ended" if sess.ended else "active",
stage=sess.state.stage.value,
stage=stage_label(sess.state.stage),
turn_count=len(sess.turns),
learner_turn_count=learner_turns,
client_turn_count=client_turns,
@ -286,6 +301,8 @@ async def update_session_review_status(
status=request.status,
note=request.note,
principal=principal,
worksheet_status=request.worksheet_status,
worksheet_note=request.worksheet_note,
)
if saved is None:
raise HTTPException(
@ -299,4 +316,7 @@ async def update_session_review_status(
reviewer_id=str(saved.get("reviewer_id") or "") or None,
reviewed_at=str(saved.get("reviewed_at") or "") or None,
updated_at=str(saved.get("updated_at") or "") or None,
worksheet_status=_worksheet_review_status_value(saved),
worksheet_note=str(saved.get("worksheet_note") or ""),
worksheet_reviewed_at=str(saved.get("worksheet_reviewed_at") or "") or None,
)