import logging from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.interval import IntervalTrigger from apscheduler.triggers.cron import CronTrigger logger = logging.getLogger(__name__) _scheduler = None _is_running = False def init_scheduler(): """Initialize the APScheduler instance.""" global _scheduler _scheduler = AsyncIOScheduler() logger.info("Scheduler initialized") def start_scheduler(interval_minutes: int = 10): """Start the scheduler with the given interval.""" global _is_running if _scheduler is None: init_scheduler() # Remove existing job if any if _scheduler.get_job("sync_job"): _scheduler.remove_job("sync_job") from . import sync_service _scheduler.add_job( sync_service.run_sync, trigger=IntervalTrigger(minutes=interval_minutes), id="sync_job", name="GoMag Sync", replace_existing=True ) if not _scheduler.running: _scheduler.start() _is_running = True logger.info(f"Scheduler started with interval {interval_minutes}min") def start_maintenance_job(hour: int = 3): """Schedule the daily DB/log maintenance job (prune history + cleanup logs). Runs independently of the sync job — starts the scheduler if it isn't already running so maintenance happens even when auto-sync is disabled. """ if _scheduler is None: init_scheduler() from . import maintenance_service _scheduler.add_job( maintenance_service.run_daily_maintenance, trigger=CronTrigger(hour=hour, minute=0), id="maintenance_job", name="Daily DB/Log Maintenance", replace_existing=True ) if not _scheduler.running: _scheduler.start() logger.info(f"Maintenance job scheduled daily at {hour:02d}:00") def stop_scheduler(): """Stop the scheduler.""" global _is_running if _scheduler and _scheduler.running: if _scheduler.get_job("sync_job"): _scheduler.remove_job("sync_job") _is_running = False logger.info("Scheduler stopped") def shutdown_scheduler(): """Shutdown the scheduler completely.""" global _scheduler, _is_running if _scheduler and _scheduler.running: _scheduler.shutdown(wait=False) _scheduler = None _is_running = False def get_scheduler_status(): """Get current scheduler status.""" job = _scheduler.get_job("sync_job") if _scheduler else None return { "enabled": _is_running, "next_run": job.next_run_time.isoformat() if job and job.next_run_time else None, "interval_minutes": int(job.trigger.interval.total_seconds() / 60) if job else None }