[backend][B] Сервис мониторинга: снимок пациента → правила → алерты, статус, персистентность демо-часов #38

Closed
opened 2026-10-03 15:17:04 +03:00 by Eleboba · 1 comment
Owner

Описание

Реализация MonitoringService из app/services/contracts.py. Связывает БД и app.core.rules (ADR-007): правила детерминированы, сервис персистит результат.

Желаемый результат

  • app/services/snapshot.py: build_snapshot(token, as_of) -> PatientSnapshot из app.db.repo (маппинг имён: alcohol_bucket→alcohol, sleep_bucket→sleep_hours_bucket; возраст из birth_year; день терапии из профиля).
  • app/services/monitoring.py: after_data_change(token) → evaluate() → alert/crisis пишутся в alerts + Notifier; кешируемый status_color в профиле; возврат диалоговых действий движку. Кризис: аудит crisis_acute, дежурный врач.
  • Персистентность смещения app.core.clock в clock_state (через register_persistence).
  • Тесты: 13 дней Андрея через сервис на sqlite → алерты и цвета G G G Y Y G G G G Y Y Y R.

Зона и владение файлами

Lane B (backend) — владеет: app/api/** (кроме schemas.py), app/services/**, app/scheduler/**, app/integrations/** (кроме base.py), app/db/**, scripts/**, data/**, tests/test_api*.py, tests/test_services*.py, tests/test_scheduler*.py, tests/test_seed*.py, tests/test_integrations*.py.

Замороженные контракты (менять только отдельным мелким PR с пингом всех): app/core/types.py, app/dialog/contracts.py, app/api/schemas.py, app/integrations/base.py, app/services/contracts.py, app/main.py, app/config.py, config/modules.yaml, config/rules.yaml, config/scales/*, pyproject.toml/uv.lock, docker-compose.yml, Makefile.


Лог выполнения

2026-10-04

  • app/services/snapshot.py + app/services/monitoring.py (RuleMonitoring, регистрация через register_monitoring), аудит crisis_acute, уведомления врачу и дежурному, кеш status_color, флаг enhanced_monitoring.
  • Кризис не перезапускается при открытом acute-алерте; C-SSRS учитывается 24 ч.
  • Персистентность смещения часов в clock_state, восстановление в scheduler.start().
  • Тест: 13 дней Андрея через сервис → G G G Y Y G G G G Y Y Y R. PR в dev.
## Описание Реализация `MonitoringService` из `app/services/contracts.py`. Связывает БД и `app.core.rules` (ADR-007): правила детерминированы, сервис персистит результат. ## Желаемый результат - `app/services/snapshot.py`: `build_snapshot(token, as_of) -> PatientSnapshot` из `app.db.repo` (маппинг имён: `alcohol_bucket`→`alcohol`, `sleep_bucket`→`sleep_hours_bucket`; возраст из `birth_year`; день терапии из профиля). - `app/services/monitoring.py`: `after_data_change(token)` → `evaluate()` → `alert`/`crisis` пишутся в `alerts` + `Notifier`; кешируемый `status_color` в профиле; возврат диалоговых действий движку. Кризис: аудит `crisis_acute`, дежурный врач. - Персистентность смещения `app.core.clock` в `clock_state` (через `register_persistence`). - Тесты: 13 дней Андрея через сервис на sqlite → алерты и цвета G G G Y Y G G G G Y Y Y R. ## Зона и владение файлами **Lane B (backend)** — владеет: `app/api/**` (кроме `schemas.py`), `app/services/**`, `app/scheduler/**`, `app/integrations/**` (кроме `base.py`), `app/db/**`, `scripts/**`, `data/**`, `tests/test_api*.py`, `tests/test_services*.py`, `tests/test_scheduler*.py`, `tests/test_seed*.py`, `tests/test_integrations*.py`. **Замороженные контракты** (менять только отдельным мелким PR с пингом всех): `app/core/types.py`, `app/dialog/contracts.py`, `app/api/schemas.py`, `app/integrations/base.py`, `app/services/contracts.py`, `app/main.py`, `app/config.py`, `config/modules.yaml`, `config/rules.yaml`, `config/scales/*`, `pyproject.toml`/`uv.lock`, `docker-compose.yml`, `Makefile`. --- ## Лог выполнения ### 2026-10-04 - `app/services/snapshot.py` + `app/services/monitoring.py` (RuleMonitoring, регистрация через register_monitoring), аудит crisis_acute, уведомления врачу и дежурному, кеш status_color, флаг enhanced_monitoring. - Кризис не перезапускается при открытом acute-алерте; C-SSRS учитывается 24 ч. - Персистентность смещения часов в clock_state, восстановление в scheduler.start(). - Тест: 13 дней Андрея через сервис → G G G Y Y G G G G Y Y Y R. PR в dev.
Eleboba added this to the Этап 1 · Ветераны СВО milestone 2026-10-03 15:17:04 +03:00
Eleboba added the lane/Bsafetysize/1featurestage/veteransP0backend labels 2026-10-03 15:17:04 +03:00
Author
Owner

Контракт с lane A (движок #10, PR #50 смержен). Что сервис мониторинга должен читать из dialog_state.data (repo.get_dialog_state(s, token).data). Там только коды, текста пациента нет:

  • safety_events — список последних 50 событий: {"ts", "level": "acute"|"possible", "categories": [...], "rules": [...], "source": "lexicon"|"llm"|"both", "handled": "crisis_script"|"crisis_repeat"|"risk_screen"|"dispatched", "state"}. После каждой записи движок вызывает after_data_change(token). Если есть acute, который ещё не превращён в алерт, нужно создать алерт acute с кодом safety_acute, уведомить дежурного и записать в аудит crisis_acute.
  • crisis — {"open", "opened_at", "source": "safety:…"|"scale:phq9"|"rule:<id>", "variant", "closed_at"}.
  • contact_requests — [{"ts"}]: пациент нажал «Связаться с врачом». Нужен алерт уровня info/moderate на дашборд.
  • consent, revoked_at, delete_requested: true — пациент отозвал согласие. Пометить данные к удалению — задача lane B.

is_in_crisis(token) должен возвращать True, пока открыт acute-алерт без ack. Движок выходит из crisis по is_in_crisis()==False или по системному событию doctor_ack, поэтому при ack острого алерта в API отправьте Event(type="system", system="doctor_ack") в get_engine().handle(token, ...).

**Контракт с lane A (движок #10, PR #50 смержен).** Что сервис мониторинга должен читать из `dialog_state.data` (`repo.get_dialog_state(s, token).data`). Там только коды, текста пациента нет: - `safety_events` — список последних 50 событий: `{"ts", "level": "acute"|"possible", "categories": [...], "rules": [...], "source": "lexicon"|"llm"|"both", "handled": "crisis_script"|"crisis_repeat"|"risk_screen"|"dispatched", "state"}`. После каждой записи движок вызывает `after_data_change(token)`. Если есть `acute`, который ещё не превращён в алерт, нужно создать алерт `acute` с кодом `safety_acute`, уведомить дежурного и записать в аудит `crisis_acute`. - `crisis` — `{"open", "opened_at", "source": "safety:…"|"scale:phq9"|"rule:<id>", "variant", "closed_at"}`. - `contact_requests` — `[{"ts"}]`: пациент нажал «Связаться с врачом». Нужен алерт уровня info/moderate на дашборд. - `consent`, `revoked_at`, `delete_requested: true` — пациент отозвал согласие. Пометить данные к удалению — задача lane B. `is_in_crisis(token)` должен возвращать True, пока открыт `acute`-алерт без ack. Движок выходит из `crisis` по `is_in_crisis()==False` или по системному событию `doctor_ack`, поэтому при ack острого алерта в API отправьте `Event(type="system", system="doctor_ack")` в `get_engine().handle(token, ...)`.
Eleboba self-assigned this 2026-10-04 03:08:19 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: MedITron/PTSRaiAgent#38