feat(backend): demo scheduler on the virtual clock — doses, missed, check-ins, scales, silence
CI / ruff · pytest (pull_request) Successful in 1m3s
CI / ruff · pytest (pull_request) Successful in 1m3s
- app/scheduler/demo.py: run_until(now) replays due and fresh events in time order: dose rows + med_reminder, missed after 2 h without an answer → monitoring, daily_checkin, scale_due by the config/modules.yaml schedules, daily monitoring pass (silence, missed doses) → reach_out/scale_due - idempotent via scheduler_events (unique token+key); stale events are not sent; no contact without consent or during an open crisis - config/scheduler.yaml: times and windows; runs while the questionnaire release is off - POST /demo/clock/advance replays the scheduler up to the new time Refs #13 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ The only module (besides registration code) that touches both schemas — and on
|
||||
|
||||
from app.db import audit as _audit # noqa: F401 (registers audit_log in clinical metadata)
|
||||
from app.db import intake as _intake # noqa: F401
|
||||
from app.db import scheduler as _scheduler # noqa: F401
|
||||
from app.db.clinical import ClinicalBase
|
||||
from app.db.identity import IdentityBase
|
||||
from app.db.session import clinical_engine, identity_engine
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Delivery log of the demo scheduler (#13): one row per delivered event, the unique key makes runs idempotent.
|
||||
|
||||
Clinical DB, keyed by token only (guardrail #5). Created by `init_db` (`app.db.bootstrap`).
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.db.clinical import TOKEN_LEN, BigId, ClinicalBase
|
||||
from app.db.types import TZDateTime
|
||||
|
||||
|
||||
class SchedulerEvent(ClinicalBase):
|
||||
__tablename__ = "scheduler_events"
|
||||
__table_args__ = (UniqueConstraint("token", "key", name="uq_scheduler_events_token_key"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(BigId, primary_key=True, autoincrement=True)
|
||||
token: Mapped[str] = mapped_column(String(TOKEN_LEN))
|
||||
key: Mapped[str] = mapped_column(String(96)) # e.g. "dose:3:2026-10-06T08:00", "checkin:2026-10-06"
|
||||
kind: Mapped[str] = mapped_column(String(32)) # med_reminder | missed | daily_checkin | scale_due | evaluation
|
||||
due_at: Mapped[datetime] = mapped_column(TZDateTime)
|
||||
done_at: Mapped[datetime] = mapped_column(TZDateTime)
|
||||
Reference in New Issue
Block a user