"""Follow-up after a crisis (clinical review 2026-10-04: «план безопасности и звонок после кризиса — обязательно»). Every crisis — from the safety lexicon, a rule (PHQ-9 item 9, C-SSRS high) or the questionnaire (red flags, high risk) — leaves a task for the doctor on duty: call the patient back, about 24 hours as a guide. The task is an alert on the dashboard; «позвонили» is its acknowledgement (`repo.ack_alert`, audited). One open task per patient: a repeated crisis signal does not pile up callbacks. On the next day the app itself asks how the person is, with the safety plan at hand (`app/scheduler/jobs.py`). """ from datetime import datetime, timedelta from sqlalchemy.ext.asyncio import AsyncSession from app.core.types import AlertLevel from app.db import repo from app.db.clinical import Alert POST_CRISIS_CALL = "post_crisis_call" ON_DUTY = "on-duty" CALL_WITHIN = timedelta(hours=24) # a guide, not a guarantee NEXT_DAY_CHECK_AFTER = timedelta(hours=20) async def schedule_post_crisis_call(session: AsyncSession, token: str, now: datetime, *, source: str) -> Alert | None: if POST_CRISIS_CALL in await repo.open_alert_codes(session, token): return None return await repo.add_alert( session, token=token, level=AlertLevel.HIGH, code=POST_CRISIS_CALL, reason="Перезвонить пациенту после кризиса — дежурный специалист, ориентир 24 ч. Отметка «позвонили» — " "подтверждение этого сигнала.", ts=now, params={"due_at": (now + CALL_WITHIN).isoformat(), "assignee": ON_DUTY, "source": source}, ) def next_day_check_due(crisis: dict | None, now: datetime) -> bool: """The day after a crisis the app asks how the person is — once per crisis episode.""" if not crisis or not crisis.get("opened_at") or crisis.get("followup_sent"): return False return now - datetime.fromisoformat(crisis["opened_at"]) >= NEXT_DAY_CHECK_AFTER