fix: пауза исключена из реакции на доклад
This commit is contained in:
parent
0d58878cbe
commit
6e7c4f4596
8 changed files with 97 additions and 16 deletions
|
|
@ -163,6 +163,7 @@ def full_state() -> SessionState:
|
|||
paused=True,
|
||||
paused_at=AT,
|
||||
total_paused_ms=15_000,
|
||||
pause_intervals=[(AT - timedelta(seconds=20), AT - timedelta(seconds=5))],
|
||||
processed_station_commands=[str(uuid4())],
|
||||
text_revealed_facts={"f_address": "улица Ленина, 14"},
|
||||
consecutive_refusals=2,
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ def crew_report(phase: str, seconds: int) -> PhoneReportRecord:
|
|||
text="доклад", at=at_second(seconds))
|
||||
|
||||
|
||||
def reaction(status_log, reports, limit_ms=45_000):
|
||||
def reaction(status_log, reports, limit_ms=45_000, pause_intervals=None):
|
||||
card = live_card(status_log=status_log, crew_assignments={SERVICES[0]: "Бригада 12"},
|
||||
dispatched_at=T0)
|
||||
card.phone_reports = reports
|
||||
|
|
@ -324,9 +324,11 @@ def reaction(status_log, reports, limit_ms=45_000):
|
|||
entries=status_log, services=SERVICES, crew_assignments=card.crew_assignments,
|
||||
deadline_ms=30_000, elapsed_ms=5_000, phone_reports=reports,
|
||||
report_reaction_ms=limit_ms,
|
||||
pause_intervals=pause_intervals,
|
||||
)
|
||||
metric = next((m for m in dispatcher_metrics(card, 30_000, services=SERVICES,
|
||||
report_reaction_ms=limit_ms)
|
||||
report_reaction_ms=limit_ms,
|
||||
pause_intervals=pause_intervals)
|
||||
if m.key == "dds_report_reaction"), None)
|
||||
return [f for f in findings if f.metric_key == "dds_report_reaction"], metric
|
||||
|
||||
|
|
@ -364,6 +366,21 @@ def test_status_later_than_limit_after_crew_report_is_d6_with_fact_and_norm():
|
|||
assert metric.credit == time_credit(70_000, 45_000)
|
||||
|
||||
|
||||
def test_pause_between_report_and_status_does_not_count_as_reaction_time():
|
||||
marks = full_run(arrived_at=180)
|
||||
reports = [crew_report("arrived", 100)]
|
||||
pause = [(at_second(110), at_second(170))]
|
||||
|
||||
without_pause, late = reaction(marks, reports)
|
||||
with_pause, active = reaction(marks, reports, pause_intervals=pause)
|
||||
|
||||
assert [finding.code for finding in without_pause] == [ErrorCode.D6]
|
||||
assert not late.passed
|
||||
assert with_pause == []
|
||||
assert active.passed and active.credit == time_credit(20_000, 45_000)
|
||||
assert "20 с" in active.fact
|
||||
|
||||
|
||||
def test_violation_by_a_fraction_of_a_second_is_not_shown_as_the_limit():
|
||||
status_log = full_run(arrived_at=170)
|
||||
status_log[2] = status_log[2].model_copy(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import asyncio
|
||||
import time
|
||||
from datetime import timedelta
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
|
|
@ -41,14 +41,14 @@ from app.domain.events import (
|
|||
TtsEnd,
|
||||
)
|
||||
from app.domain.roles import Role
|
||||
from app.domain.statuses import ServiceStatus
|
||||
from app.domain.statuses import PhoneReportRecord, ServiceStatus
|
||||
from app.domain.timers import TimerCode
|
||||
from app.main import app
|
||||
from app.scenarios.loader import load_file
|
||||
from app.scoring.dispatcher import dispatcher_metrics
|
||||
from app.session import timers as timers_module
|
||||
from app.session.checkpoint import dump_state, load_state
|
||||
from app.session.finish import end_session
|
||||
from app.session.finish import end_session, score_dds_card
|
||||
from app.session.hub import hub
|
||||
from app.session.state import SessionState, now_utc
|
||||
from app.session.store import LessonPaused, LessonResumed, MemorySessionStore
|
||||
|
|
@ -621,9 +621,49 @@ def test_repeated_pause_and_resume_are_idempotent(clock, monkeypatch):
|
|||
assert sum(isinstance(record, LessonResumed) for record in records) == 1
|
||||
assert [event.paused for event in events if isinstance(event, SessionPaused)] == [True, False]
|
||||
assert state.total_paused_ms == 60_000
|
||||
assert state.pause_intervals == [(base, base + timedelta(seconds=60))]
|
||||
assert card.timers.timers[TimerCode.DDS_ACK].current_ms(clock.now) == 15_000
|
||||
|
||||
|
||||
def test_report_reaction_excludes_pause_after_control_and_checkpoint(clock, monkeypatch):
|
||||
base = datetime(2026, 9, 27, 10, tzinfo=UTC)
|
||||
state, card = _dds_state_with_active_card(clock)
|
||||
service = state.card_services(card)[0]
|
||||
monkeypatch.setattr("app.session.dds.now_utc", lambda: base)
|
||||
card.set_service_status(service, ServiceStatus.ACCEPTED, "Карточка принята")
|
||||
card.phone_reports.append(PhoneReportRecord(
|
||||
service=service, crew="Бригада 12", phase="dispatched",
|
||||
text="Бригада выехала", at=base,
|
||||
))
|
||||
store = MemorySessionStore()
|
||||
monkeypatch.setattr(hub, "store", store)
|
||||
monkeypatch.setitem(hub._sessions, state.session_id, state)
|
||||
|
||||
async def command(event):
|
||||
async with hub.operation(state.session_id):
|
||||
await control_ws._command(state.session_id, event, TEACHER)
|
||||
|
||||
monkeypatch.setattr("app.session.state.now_utc", lambda: base + timedelta(seconds=10))
|
||||
asyncio.run(command(SessionPause()))
|
||||
restored = load_state(dump_state(state), base + timedelta(seconds=70))
|
||||
assert restored.paused and restored.pause_intervals == []
|
||||
monkeypatch.setitem(hub._sessions, state.session_id, restored)
|
||||
monkeypatch.setattr("app.session.state.now_utc", lambda: base + timedelta(seconds=70))
|
||||
asyncio.run(command(SessionResume()))
|
||||
monkeypatch.setattr("app.session.dds.now_utc", lambda: base + timedelta(seconds=80))
|
||||
restored.desk.active.set_service_status(
|
||||
service, ServiceStatus.RESPONDING, "Основание: доклад. Сведения: бригада выехала.",
|
||||
)
|
||||
|
||||
result = score_dds_card(restored, restored.desk.active)
|
||||
reaction = next(item for item in result.metrics if item.key == "dds_report_reaction")
|
||||
assert restored.pause_intervals == [
|
||||
(base + timedelta(seconds=10), base + timedelta(seconds=70)),
|
||||
]
|
||||
assert reaction.passed and "20 с" in reaction.fact
|
||||
assert not any(item.metric_key == "dds_report_reaction" for item in result.findings)
|
||||
|
||||
|
||||
def test_pause_is_ignored_in_ended_session(clock, monkeypatch):
|
||||
state, _card = _dds_state_with_active_card(clock)
|
||||
store = MemorySessionStore()
|
||||
|
|
|
|||
Loading…
Reference in a new issue