lct-hack/backend/app/scoring/competency.py
Ivan Gerasimov f01f2dab76 lct-12: детерминированная оценка по ГОСТ, таксономия, радар
Каждая метрика — факт против норматива со ссылкой: «94 с при ≤ 75 с,
ГОСТ Р 22.7.03-2021», а не балл. По недобытому факту — отдельная отметка
E1 с эталонным вопросом: в разборе нужен конкретный вопрос, не процент.
Радар — проекция тех же метрик без пересчёта весов, коммуникация без
судьи не рисуется нулём.

Таймер, не остановленный событием, — провал, а не зачёт: время
недоказуемо, операция не завершена. Метрика, которую нечем посчитать
(полнота опроса без модели эмбеддингов), видна как «не посчитано» —
молча выброшенная выглядела бы пройденной.

Найдено противоречие: таймеры опроса (75 с) и оповещения ДДС (60 с)
стартовали на ответе и останавливались передачей в ДДС — один отрезок,
два лимита. Опрос за законные 70 с давал E3, а на экране курсанта
краснел таймер посреди нормального разговора. События «опрос закончен»
в контракте нет, поэтому dds_notify снят с учёта, вопрос записан в
CONTRACT.md.

KIO проверяет присваивание: card.dds = "03" клало в карточку сырую
строку вместо кода ДДС, и падала уже оценка, далеко от места ошибки.
2026-09-17 14:06:16 +03:00

34 lines
1.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Радар шести компетенций.
Чистая проекция уже посчитанных метрик, без пересчёта весов: радар и оценка
всегда согласованы, и расхождение между ними невозможно по построению
(docs/product/METHODOLOGY.md#компетентная-модель).
"""
from app.domain.events import CompetencyScore, Metric
from app.domain.taxonomy import Competency
from app.scoring.taxonomy import METRIC_MAP
def radar(metrics: list[Metric]) -> list[CompetencyScore]:
"""Доля пройденного веса по каждой компетенции, 0–1.
Компетенция без метрик не рисуется нулём: «коммуникация» без судьи —
это «не оценивалось», а не «провалено».
"""
total: dict[Competency, float] = {}
passed: dict[Competency, float] = {}
for metric in metrics:
mapping = METRIC_MAP.get(metric.key)
if mapping is None:
continue
competency = mapping[1]
total[competency] = total.get(competency, 0.0) + metric.weight
if metric.passed:
passed[competency] = passed.get(competency, 0.0) + metric.weight
return [
CompetencyScore(competency=competency.value, value=round(passed.get(competency, 0.0) / weight, 3))
for competency in Competency
if (weight := total.get(competency))
]