Files
openrouter-watcher/src/openrouter_monitor/tasks/scheduler.py
Luca Sacchi Ricciardi 3ae5d736ce feat(tasks): T55-T58 implement background tasks for OpenRouter sync
- T55: Setup APScheduler with AsyncIOScheduler and @scheduled_job decorator
- T56: Implement hourly usage stats sync from OpenRouter API
- T57: Implement daily API key validation job
- T58: Implement weekly cleanup job for old usage stats
- Add usage_stats_retention_days config option
- Integrate scheduler with FastAPI lifespan events
- Add 26 unit tests for scheduler, sync, and cleanup tasks
- Add apscheduler to requirements.txt

The background tasks now automatically:
- Sync usage stats every hour from OpenRouter
- Validate API keys daily at 2 AM UTC
- Clean up old data weekly on Sunday at 3 AM UTC
2026-04-07 17:41:24 +02:00

77 lines
2.0 KiB
Python

"""APScheduler task scheduler.
T55: Background task scheduler using APScheduler with AsyncIOScheduler.
"""
from apscheduler.schedulers.asyncio import AsyncIOScheduler
# Singleton scheduler instance
_scheduler = None
def get_scheduler():
"""Get or create the singleton scheduler instance.
Returns:
AsyncIOScheduler: The scheduler instance (singleton)
Example:
>>> scheduler = get_scheduler()
>>> scheduler.start()
"""
global _scheduler
if _scheduler is None:
_scheduler = AsyncIOScheduler(timezone='UTC')
return _scheduler
def scheduled_job(trigger, **trigger_args):
"""Decorator to register a scheduled job.
Args:
trigger: APScheduler trigger (IntervalTrigger, CronTrigger, etc.)
**trigger_args: Additional arguments for add_job (id, name, etc.)
Returns:
Decorator function that registers the job and returns original function
Example:
>>> from apscheduler.triggers.interval import IntervalTrigger
>>>
>>> @scheduled_job(IntervalTrigger(hours=1), id='sync_task')
... async def sync_data():
... pass
"""
def decorator(func):
get_scheduler().add_job(func, trigger=trigger, **trigger_args)
return func
return decorator
def init_scheduler():
"""Initialize and start the scheduler.
Should be called during application startup.
Registers all decorated jobs and starts the scheduler.
Example:
>>> init_scheduler()
>>> # Scheduler is now running
"""
scheduler = get_scheduler()
scheduler.start()
def shutdown_scheduler():
"""Shutdown the scheduler gracefully.
Should be called during application shutdown.
Waits for running jobs to complete before stopping.
Example:
>>> shutdown_scheduler()
>>> # Scheduler is stopped
"""
scheduler = get_scheduler()
scheduler.shutdown(wait=True)