lct-12: детерминированная оценка по ГОСТ, таксономия, радар
Каждая метрика — факт против норматива со ссылкой: «94 с при ≤ 75 с, ГОСТ Р 22.7.03-2021», а не балл. По недобытому факту — отдельная отметка E1 с эталонным вопросом: в разборе нужен конкретный вопрос, не процент. Радар — проекция тех же метрик без пересчёта весов, коммуникация без судьи не рисуется нулём. Таймер, не остановленный событием, — провал, а не зачёт: время недоказуемо, операция не завершена. Метрика, которую нечем посчитать (полнота опроса без модели эмбеддингов), видна как «не посчитано» — молча выброшенная выглядела бы пройденной. Найдено противоречие: таймеры опроса (75 с) и оповещения ДДС (60 с) стартовали на ответе и останавливались передачей в ДДС — один отрезок, два лимита. Опрос за законные 70 с давал E3, а на экране курсанта краснел таймер посреди нормального разговора. События «опрос закончен» в контракте нет, поэтому dds_notify снят с учёта, вопрос записан в CONTRACT.md. KIO проверяет присваивание: card.dds = "03" клало в карточку сырую строку вместо кода ДДС, и падала уже оценка, далеко от места ошибки.
This commit is contained in:
parent
7c97310938
commit
f01f2dab76
9 changed files with 489 additions and 7 deletions
34
backend/app/scoring/competency.py
Normal file
34
backend/app/scoring/competency.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
"""Радар шести компетенций.
|
||||
|
||||
Чистая проекция уже посчитанных метрик, без пересчёта весов: радар и оценка
|
||||
всегда согласованы, и расхождение между ними невозможно по построению
|
||||
(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))
|
||||
]
|
||||
Loading…
Reference in a new issue