Эталонный диалог собирается кодом из фактов и чек-листа: написанный руками, он разошёлся бы с фактами при первой же правке сценария, и курсанта оштрафовали бы за правильный ответ. Отчёт: метрики фактом против норматива со ссылкой, отметка E1 на каждый недобытый факт с эталонным вопросом, расхождение самооценки — что курсант заметил сам, чего не заметил, что отметил зря. Не заметил — самое ценное для разбора. Внешний монитор — не отдельное приложение, а другой режим отрисовки тех же событий: крупный таймер опроса, ход разговора, карточка, после оценки разбор на весь экран. Коррекция преподавателем сохраняет автооценку рядом: видно, что скорректировано и кем. Найдено: все метрики весили одинаково, и курсант, не задавший ни одного вопроса, но заполнивший карточку руками, получал 87 из 100. Предварительные веса (полнота опроса — 4) дают 74; окончательные утверждает методист, вопрос записан в DEBRIEF.md.
48 lines
1.9 KiB
Python
48 lines
1.9 KiB
Python
"""Эталонный диалог собирается из фактов, а не пишется руками."""
|
||
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from app.scenarios.loader import load_file
|
||
from app.scoring.reference import build, missed_steps
|
||
|
||
LIBRARY = Path(__file__).resolve().parents[2] / "scenarios"
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def scenario():
|
||
return load_file(LIBRARY / "fire-apartment-l2.yaml", LIBRARY)
|
||
|
||
|
||
def test_every_checklist_question_becomes_a_step(scenario):
|
||
reference = build(scenario)
|
||
assert [step.checklist_id for step in reference.steps] == [
|
||
item.id for item in scenario.checklist if item.question
|
||
]
|
||
assert reference.first_line == scenario.first_line
|
||
|
||
|
||
def test_answers_come_from_facts_not_from_prose(scenario):
|
||
"""Эталон не может разойтись с фактами: ответы взяты из них же."""
|
||
reference = build(scenario)
|
||
address = next(step for step in reference.steps if step.fact_id == "f_address")
|
||
fact = next(fact for fact in scenario.facts if fact.id == "f_address")
|
||
assert fact.value in address.answer
|
||
|
||
|
||
def test_step_without_a_fact_has_no_answer(scenario):
|
||
"""«Представьтесь» факта не добывает — и ответа в эталоне у него нет."""
|
||
caller = next(step for step in build(scenario).steps if step.checklist_id == "q_caller")
|
||
assert caller.fact_id is None and caller.answer is None
|
||
assert not caller.required
|
||
|
||
|
||
def test_missed_steps_are_only_the_required_ones(scenario):
|
||
missed = missed_steps(scenario, revealed=["f_address"])
|
||
assert [step.fact_id for step in missed] == ["f_what_burns", "f_people", "f_smoke", "f_gas"]
|
||
assert all(step.question for step in missed), "в разборе нужен текст эталонного вопроса"
|
||
|
||
|
||
def test_without_slot_machine_nothing_is_declared_missed(scenario):
|
||
assert missed_steps(scenario, revealed=None) == []
|