"""APScheduler singleton — allows runtime reschedule of notification jobs.""" from apscheduler.schedulers.asyncio import AsyncIOScheduler _scheduler: AsyncIOScheduler | None = None def get_scheduler() -> AsyncIOScheduler: """Lazy-init and return the global scheduler.""" global _scheduler if _scheduler is None: _scheduler = AsyncIOScheduler() return _scheduler async def reschedule_daily_check(hour: int, minute: int): """Update the daily_check cron job to a new time.""" sched = get_scheduler() # Remove and re-add — APScheduler reschedule_job is inconsistent with cron triggers try: sched.remove_job("daily_check") except Exception: pass from app.services.scheduler import check_daily_reporting from app.database import async_session async def _wrapper(): async with async_session() as db: await check_daily_reporting(db) sched.add_job(_wrapper, "cron", hour=hour, minute=minute, id="daily_check") def start_scheduler(notification_hour: int = 17, notification_minute: int = 30): """Start the scheduler with configured notification times.""" sched = get_scheduler() from app.services.scheduler import check_daily_reporting, check_overdue_plans from app.database import async_session async def _daily(): async with async_session() as db: await check_daily_reporting(db) async def _overdue(): async with async_session() as db: await check_overdue_plans(db) sched.add_job(_daily, "cron", hour=notification_hour, minute=notification_minute, id="daily_check") sched.add_job(_overdue, "cron", hour=9, minute=0, id="overdue_check") sched.start() def shutdown_scheduler(): """Shutdown the scheduler gracefully.""" global _scheduler if _scheduler is not None: _scheduler.shutdown(wait=False) _scheduler = None