feat: статусы реагирования на АРМ ДДС и ошибки диспетчера D1–D6 (lct-33)
ТЗ называет обучаемого оператором ДДС, а его работа в системе-112 состоит из одного действия: получить карточку и правильно проставить статус реагирования с комментарием. Памятка заказчика посвящена этому целиком. Наш АРМ умел два статуса из девяти. - domain/statuses.py: девять статусов реагирования с автоматом переходов, семь статусов карточки, обязательные комментарии к отказам. Формулировки из памятки дословно — диспетчер должен узнать слова своего боевого АРМ. - Автомат на сервере: фронт рисует только пришедшее в available. Отвергнутая отметка не попадает в журнал и возвращается текстом для курсанта. - scoring/dispatcher.py: D1, D2, D3, D4, D6 детерминированно. D3 (отказ от профильного происшествия) проверяется по списку оповещения из ЕКП — без классификатора его было бы не на чем посчитать. D5 оставлен судье. - Отметки D идут в отчёт рядом с E: в живой цепочке 112 → ДДС участвуют обе роли. - Экран ДДС: таблица служб со статусами, поле комментария у отказов, журнал отметок, статус карточки красным в трёх случаях из семи. Сквозной тест повис на ожидании station.state и вскрыл продуктовый дефект: снимок уходил только в ответ на действие диспетчера, то есть список оповещения он видел лишь после того, как что-то нажмёт. Теперь station.state отправляется сразу за card.received. Упрощения записаны в карточке: статусы «Проверена» и «Не завершено» не считаются — первый требует главного специалиста, второй 48 часов. 148 тестов зелёных (23 новых), make typecheck чистый.
This commit is contained in:
parent
842a333d16
commit
d4f0d0a0f8
14 changed files with 953 additions and 9 deletions
|
|
@ -78,6 +78,9 @@ export interface CardReceived {
|
|||
at: string;
|
||||
}
|
||||
|
||||
/** Статус карточки целиком — считается из статусов служб, руками не ставится. */
|
||||
export type CardStatus = "registered" | "processed" | "checked" | "not_notified" | "refused" | "not_completed" | "completed";
|
||||
|
||||
/** Шесть осей радара. Проекция уже посчитанных метрик, без пересчёта весов. */
|
||||
export type Competency = "intake" | "interview" | "card" | "routing" | "norms" | "communication";
|
||||
|
||||
|
|
@ -130,7 +133,8 @@ export interface EraData {
|
|||
impact_force: string;
|
||||
}
|
||||
|
||||
export type ErrorCode = "E1" | "E2" | "E3" | "E4" | "E5" | "E6";
|
||||
/** E — ошибки приёма вызова, D — ошибки диспетчера ДДС. Две роли, две таксономии. D1–D6 взяты из памятки заказчика дословно: она перечисляет типовые нарушения с реальными примерами, и преподаватель узнаёт их формулировки (docs/spec/DATASET.md). */
|
||||
export type ErrorCode = "E1" | "E2" | "E3" | "E4" | "E5" | "E6" | "D1" | "D2" | "D3" | "D4" | "D5" | "D6";
|
||||
|
||||
export interface ErrorEvent {
|
||||
type: "error";
|
||||
|
|
@ -155,7 +159,7 @@ export interface Finding {
|
|||
}
|
||||
|
||||
/** Кто выставил отметку. `judge` — единственный недетерминированный источник. */
|
||||
export type FindingSource = "slots" | "ground_truth" | "timers" | "kio" | "chain" | "judge" | "instructor";
|
||||
export type FindingSource = "slots" | "dispatcher" | "ground_truth" | "timers" | "kio" | "chain" | "judge" | "instructor";
|
||||
|
||||
/** Уточняющие поля 01 — МЧС. */
|
||||
export interface FireDetails {
|
||||
|
|
@ -358,6 +362,17 @@ export interface SelfAssessmentSubmit {
|
|||
comment?: string;
|
||||
}
|
||||
|
||||
/** Статус реагирования одной службы из списка оповещения. */
|
||||
export type ServiceStatus = "added" | "received" | "accepted" | "declined" | "responding" | "arrived" | "working" | "completed" | "refused";
|
||||
|
||||
/** Диспетчер ставит статус реагирования своей службе. Последовательность жёсткая, комментарий к отказу обязателен — это не валидация формы, а то, чему учит второй режим занятия (docs/spec/DATASET.md#статусы-реагирования). */
|
||||
export interface ServiceStatusSet {
|
||||
type: "card.status";
|
||||
service: string;
|
||||
status: ServiceStatus;
|
||||
comment?: string;
|
||||
}
|
||||
|
||||
export interface SessionEnded {
|
||||
type: "session.ended";
|
||||
reason: CallEndReason;
|
||||
|
|
@ -412,6 +427,30 @@ export interface SessionStop {
|
|||
|
||||
export type Speaker = "caller" | "operator";
|
||||
|
||||
/** Что видит диспетчер: службы, их статусы и что доступно дальше. Все поля обязательные, хотя списки и могут быть пустыми: снимок всегда собирается целиком, а необязательное поле уезжает в TypeScript как `| undefined` и заставляет фронт проверять то, чего не бывает. */
|
||||
export interface StationSnapshot {
|
||||
services: Array<string>;
|
||||
statuses: Record<string, ServiceStatus>;
|
||||
available: Record<string, Array<ServiceStatus>>;
|
||||
card: CardStatus;
|
||||
log: Array<StatusEntry>;
|
||||
}
|
||||
|
||||
/** Состояние АРМ ДДС после каждой отметки: что стоит и что доступно дальше. */
|
||||
export interface StationState {
|
||||
type: "station.state";
|
||||
snapshot: StationSnapshot;
|
||||
}
|
||||
|
||||
/** Одна отметка службы. Комментарий — часть отметки, а не примечание к ней. */
|
||||
export interface StatusEntry {
|
||||
service: string;
|
||||
status: ServiceStatus;
|
||||
at: string;
|
||||
comment?: string;
|
||||
author?: string;
|
||||
}
|
||||
|
||||
export interface SttFinal {
|
||||
type: "stt.final";
|
||||
text: string;
|
||||
|
|
@ -500,9 +539,9 @@ export type ServerToObserver = SessionSnapshot | TranscriptAppend | KioState | T
|
|||
|
||||
export type InstructorToServer = ScenarioStart | DirectorInject | ReferencePlay | InstructorNoteAdd | ScoreOverride | ScenarioPublish | SessionStop;
|
||||
|
||||
export type ServerToStation = CardReceived | TimerTick | SessionEnded | ErrorEvent;
|
||||
export type ServerToStation = CardReceived | StationState | TimerTick | SessionEnded | ErrorEvent;
|
||||
|
||||
export type StationToServer = CardAck | CardBounce | ZoneDecision | CrewDispatched | CrewArrived;
|
||||
export type StationToServer = CardAck | CardBounce | ServiceStatusSet | ZoneDecision | CrewDispatched | CrewArrived;
|
||||
|
||||
export type EventName =
|
||||
"bg.start"
|
||||
|
|
@ -517,6 +556,7 @@ export type EventName =
|
|||
| "card.ack"
|
||||
| "card.bounce"
|
||||
| "card.received"
|
||||
| "card.status"
|
||||
| "crew.arrived"
|
||||
| "crew.dispatched"
|
||||
| "dds.dispatch"
|
||||
|
|
@ -540,6 +580,7 @@ export type EventName =
|
|||
| "session.ended"
|
||||
| "session.snapshot"
|
||||
| "session.stop"
|
||||
| "station.state"
|
||||
| "stt.final"
|
||||
| "stt.partial"
|
||||
| "timer.tick"
|
||||
|
|
|
|||
Loading…
Reference in a new issue