Implement local task 09 training workflows

This commit is contained in:
andreysk0304 2026-09-21 17:40:54 +03:00
commit cec84ffcd0
45 changed files with 2679 additions and 366 deletions

View file

@ -0,0 +1,70 @@
"""Детерминированная оценка текстового упражнения оператора 112.
Нет входящего звонка и опроса: оцениваются только поля готовой КИО и
маршрутизация. Эталон берётся из утверждённого сценария, но не показывается
в `card.briefing` до завершения упражнения.
"""
from app.domain.events import Metric
from app.domain.kio import KIO, missing_fields
from app.domain.taxonomy import Finding, FindingSource
from app.scenarios.schema import Scenario
from app.scoring.gost import GostResult, _normalize_address
from app.scoring.taxonomy import METRIC_MAP, METRIC_WEIGHTS
def evaluate_card(scenario: Scenario, kio: KIO) -> GostResult:
result = GostResult()
truth = scenario.ground_truth
def add(key: str, title: str, fact: str, norm: str, passed: bool, ref: str) -> None:
result.metrics.append(Metric(
key=key, title=title, fact=fact, norm=norm, passed=passed,
ref=ref, weight=METRIC_WEIGHTS.get(key, 1.0),
))
if not passed:
code, competency = METRIC_MAP[key]
result.findings.append(Finding(
code=code,
source=FindingSource.KIO if code.value == "E5" else FindingSource.GROUND_TRUTH,
summary=f"{title}: {fact}; требуется {norm}",
fact=fact, norm=norm, ref=ref, competency=competency,
))
if truth.incident_code:
actual = kio.incident_code or "не определён"
add("incident_signs", "Признаки происшествия", actual,
truth.incident_code, actual == truth.incident_code, "классификатор ЕКП")
elif truth.incident_type:
actual = kio.incident_type.value if kio.incident_type else "не указан"
add("incident_type", "Тип происшествия", actual,
truth.incident_type.value, kio.incident_type == truth.incident_type,
"классификатор происшествий")
if truth.dds and truth.incident_code is None:
actual = kio.dds.value if kio.dds else "не выбрана"
add("dds_choice", "ДДС", actual, truth.dds.value,
kio.dds == truth.dds, "список оповещения по сценарию")
if truth.address:
written = kio.address or " ".join(filter(None, (kio.street, kio.building)))
add("address", "Адрес происшествия", written or "не заполнен", truth.address,
bool(_normalize_address(truth.address))
and _normalize_address(truth.address) <= _normalize_address(written),
"эталон сценария")
if truth.victims is not None:
actual = str(kio.victims_count) if kio.victims_count is not None else "не указано"
add("victims_count", "Число пострадавших", actual, str(truth.victims),
kio.victims_count == truth.victims, "эталон сценария")
required_fields = ([field for field in scenario.required_fields if field != "dds"]
if truth.incident_code else scenario.required_fields)
if required_fields:
missing = missing_fields(kio, required_fields)
add("required_fields", "Обязательные поля карточки",
"все заполнены" if not missing else "пусто: " + ", ".join(missing),
"все обязательные поля заполнены", not missing,
"поля КИО утверждённого сценария")
return result