Implement local task 09 training workflows
This commit is contained in:
parent
e081fa410b
commit
cec84ffcd0
45 changed files with 2679 additions and 366 deletions
|
|
@ -10,12 +10,13 @@
|
|||
import logging
|
||||
from uuid import UUID
|
||||
|
||||
from app.domain.events import ScoreReady
|
||||
from app.domain.events import Exercise, ScoreReady
|
||||
from app.domain.timers import NORMATIVES, TimerCode
|
||||
from app.scenarios import store
|
||||
from app.scoring.competency import radar
|
||||
from app.scoring.dispatcher import evaluate_dispatcher
|
||||
from app.scoring.gost import evaluate
|
||||
from app.scoring.card import evaluate_card
|
||||
from app.scoring.dispatcher import dispatcher_metrics, evaluate_dispatcher
|
||||
from app.scoring.gost import GostResult, evaluate
|
||||
from app.session.hub import hub
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
@ -27,27 +28,36 @@ async def finish(session_id: UUID, state) -> None:
|
|||
if scenario is None:
|
||||
return
|
||||
|
||||
result = evaluate(
|
||||
scenario=scenario,
|
||||
kio=state.kio,
|
||||
timers=state.timers,
|
||||
revealed_facts=[fact.id for fact in state.slots.revealed_facts()] if state.slots else None,
|
||||
refined_facts=list(state.slots.refined) if state.slots else None,
|
||||
end_reason=state.end_reason,
|
||||
bounced_fields=state.bounced_fields,
|
||||
resolved_outcome=state.resolved_outcome,
|
||||
dispatched=state.dispatched_card is not None,
|
||||
)
|
||||
if state.exercise is Exercise.CARD:
|
||||
result = evaluate_card(scenario, state.dispatched_card or state.kio)
|
||||
elif state.exercise is Exercise.DDS:
|
||||
# В готовой карточке не было входящего звонка, опроса и действий
|
||||
# оператора 112. Ни одна E-метрика здесь не применима.
|
||||
result = GostResult()
|
||||
else:
|
||||
result = evaluate(
|
||||
scenario=scenario,
|
||||
kio=state.kio,
|
||||
timers=state.timers,
|
||||
revealed_facts=[fact.id for fact in state.slots.revealed_facts()] if state.slots else None,
|
||||
refined_facts=list(state.slots.refined) if state.slots else None,
|
||||
end_reason=state.end_reason,
|
||||
bounced_fields=state.bounced_fields,
|
||||
resolved_outcome=state.resolved_outcome,
|
||||
dispatched=state.dispatched_card is not None,
|
||||
)
|
||||
# Работа диспетчера — вторая роль и вторая таксономия. Отметки D1–D6 идут
|
||||
# рядом с E1–E6, а не вместо: в живой цепочке 112 → ДДС в одном занятии
|
||||
# участвуют оба (docs/spec/DATASET.md#статусы-реагирования).
|
||||
dispatcher_findings = evaluate_dispatcher(
|
||||
entries=state.status_log,
|
||||
services=state.notified_services(),
|
||||
deadline_ms=NORMATIVES[TimerCode.DDS_ACK].limit_ms,
|
||||
elapsed_ms=state.timers.measured_ms(TimerCode.DDS_ACK),
|
||||
)
|
||||
result.findings.extend(dispatcher_findings)
|
||||
if state.dispatched_card is not None and state.exercise is not Exercise.CARD:
|
||||
dispatcher_findings = evaluate_dispatcher(
|
||||
entries=state.status_log,
|
||||
services=state.notified_services(),
|
||||
deadline_ms=NORMATIVES[TimerCode.DDS_ACK].limit_ms,
|
||||
elapsed_ms=state.timers.measured_ms(TimerCode.DDS_ACK),
|
||||
)
|
||||
result.findings.extend(dispatcher_findings)
|
||||
result.metrics.extend(dispatcher_metrics(state, NORMATIVES[TimerCode.DDS_ACK].limit_ms))
|
||||
|
||||
# Сводка числами: по ней считается дельта между попытками в профиле.
|
||||
# Вытаскивать её разбором текста метрик («94 с») — путь к тихим ошибкам.
|
||||
|
|
@ -84,5 +94,5 @@ async def finish(session_id: UUID, state) -> None:
|
|||
|
||||
async def release_score(session_id: UUID, state) -> None:
|
||||
"""Отдать оценку курсанту, когда самооценка сдана."""
|
||||
if state.score is not None and state.self_assessed:
|
||||
if state.score is not None and (state.self_assessed or state.exercise is Exercise.CARD):
|
||||
hub.to_trainee(session_id, ScoreReady(session_id=session_id))
|
||||
|
|
|
|||
Loading…
Reference in a new issue