lct-15: самооценка курсанта до показа автооценки
Правило сервера, а не интерфейса: score.ready курсанту уходит только после self_assessment.submit, обойти его через DevTools нельзя. Преподаватель и монитор получают оценку сразу — им ждать нечего. Чек-лист для самооценки отдаётся отдельной точкой и только после конца звонка: во время разговора это содержимое подсказок, и открыть его значило бы выдать в контрольном режиме то, чего там быть не должно. Попутно подключён расчёт оценки при завершении звонка — детерминированный слой был написан в lct-12, но его никто не вызывал. Тест на отсутствие события смотрит очередь курсанта, а не ждёт из сокета: ожидание того, чего не будет, вешает прогон навсегда.
This commit is contained in:
parent
78f66cf3b5
commit
25143645d3
10 changed files with 260 additions and 2 deletions
|
|
@ -14,6 +14,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||
from app.db import repo
|
||||
from app.db.base import get_session
|
||||
from app.domain.events import SessionMode
|
||||
from app.scenarios import store
|
||||
from app.session.hub import hub
|
||||
|
||||
router = APIRouter(prefix="/api/sessions", tags=["sessions"])
|
||||
|
||||
|
|
@ -73,6 +75,34 @@ async def read(session_id: UUID, db: AsyncSession = Depends(get_session)) -> Ses
|
|||
return _out(session)
|
||||
|
||||
|
||||
class ChecklistItemOut(BaseModel):
|
||||
id: str
|
||||
question: str
|
||||
|
||||
|
||||
@router.get("/{session_id}/checklist", response_model=list[ChecklistItemOut])
|
||||
async def checklist(session_id: UUID) -> list[ChecklistItemOut]:
|
||||
"""Чек-лист для самооценки — **только после конца звонка**.
|
||||
|
||||
Во время звонка это содержимое подсказок: отдать его значит выдать
|
||||
в контрольном режиме то, чего там быть не должно. После звонка курсант
|
||||
по нему отмечает, что, по его мнению, пропустил.
|
||||
"""
|
||||
state = hub.get(session_id)
|
||||
if state is None:
|
||||
raise HTTPException(status_code=404, detail="session_not_found")
|
||||
if not state.ended:
|
||||
raise HTTPException(status_code=409, detail="call_not_ended")
|
||||
scenario = store.get(state.scenario_id)
|
||||
if scenario is None:
|
||||
raise HTTPException(status_code=404, detail="scenario_not_found")
|
||||
return [
|
||||
ChecklistItemOut(id=item.id, question=item.question)
|
||||
for item in scenario.checklist
|
||||
if item.question
|
||||
]
|
||||
|
||||
|
||||
@router.get("", response_model=list[SessionOut])
|
||||
async def listing(
|
||||
trainee: UUID | None = None,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ from app.domain.events import (
|
|||
)
|
||||
from app.domain.events import BgStart
|
||||
from app.scenarios import store
|
||||
from app.session.finish import finish, release_score
|
||||
from app.session.hub import hub
|
||||
from app.session.state import now_utc
|
||||
from app.voice.models import get_voice_models
|
||||
|
|
@ -125,10 +126,14 @@ async def _handle(session_id: UUID, state, event) -> None:
|
|||
state.on_event("callback.dial")
|
||||
|
||||
case "self_assessment.submit":
|
||||
state.self_assessed = True
|
||||
state.self_assessment = {"missed": event.missed, "comment": event.comment}
|
||||
if hub.journal:
|
||||
await hub.journal.self_assessment(
|
||||
session_id, event.missed, event.comment, now_utc()
|
||||
)
|
||||
# Оценка могла быть готова раньше самооценки — теперь её можно отдать.
|
||||
await release_score(session_id, state)
|
||||
|
||||
case "call.hangup":
|
||||
if state.voice is not None:
|
||||
|
|
@ -142,6 +147,7 @@ async def _handle(session_id: UUID, state, event) -> None:
|
|||
await hub.journal.session_ended(
|
||||
session_id, state.ended_at, CallEndReason.HANGUP.value
|
||||
)
|
||||
await finish(session_id, state)
|
||||
|
||||
|
||||
def _start_voice(session_id: UUID, state) -> None:
|
||||
|
|
|
|||
Loading…
Reference in a new issue