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:
@@ -1,4 +1,4 @@
|
||||
"""Questionnaire jobs on the demo clock; clinical release disabled by default.
|
||||
"""Scheduler on the demo clock: questionnaire jobs when that release is on, else the demo scheduler (#13).
|
||||
|
||||
One scheduler per database. PostgreSQL's advisory transaction lock serializes
|
||||
workers; an asyncio lock does the same within a SQLite demonstration process.
|
||||
@@ -22,6 +22,7 @@ from app.db.intake import QuestionnaireTask
|
||||
from app.db.session import get_clinical_session
|
||||
from app.dialog.contracts import Event, Reply, get_engine
|
||||
from app.integrations.base import integrations
|
||||
from app.scheduler import demo
|
||||
from app.services import monitoring
|
||||
from app.services import questionnaire as service
|
||||
|
||||
@@ -148,6 +149,9 @@ async def run_until_now():
|
||||
return
|
||||
await service.dispatch_notices()
|
||||
if not intake_enabled():
|
||||
# Questionnaire release off → the demo scheduler (doses, check-ins, scales, silence) runs instead.
|
||||
if demo.load_config().enabled:
|
||||
await demo.run_until(clock.now())
|
||||
return
|
||||
await plan()
|
||||
await deliver()
|
||||
@@ -160,7 +164,7 @@ async def _run():
|
||||
await run_until_now()
|
||||
except Exception as exc:
|
||||
log.error("questionnaire scheduler error=%s", type(exc).__name__)
|
||||
await asyncio.sleep(30)
|
||||
await asyncio.sleep(demo.load_config().tick_seconds)
|
||||
|
||||
|
||||
async def start():
|
||||
@@ -169,7 +173,7 @@ async def start():
|
||||
await monitoring.init_clock_persistence()
|
||||
except (OSError, SQLAlchemyError) as exc:
|
||||
log.error("clock persistence unavailable error=%s", type(exc).__name__)
|
||||
if _worker is None and intake_enabled():
|
||||
if _worker is None and (intake_enabled() or demo.load_config().enabled):
|
||||
_worker = asyncio.create_task(_run())
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user