- Fetch 2026 F1 race calendar from jolpica API with all sessions (FP1-3, Qualifying, Sprint, Race) and UTC timestamps - Persist schedule to NFS as JSON, load on startup if fresh - APScheduler daily refresh at 03:00 UTC - GET /schedule endpoint with live/upcoming/past session status - POST /schedule/refresh for manual refresh trigger
80 lines
2 KiB
Python
80 lines
2 KiB
Python
"""F1 Streams - FastAPI backend with schedule service."""
|
|
|
|
import logging
|
|
from contextlib import asynccontextmanager
|
|
|
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
from apscheduler.triggers.cron import CronTrigger
|
|
from fastapi import FastAPI
|
|
|
|
from backend.schedule import ScheduleService
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
schedule_service = ScheduleService()
|
|
scheduler = AsyncIOScheduler()
|
|
|
|
|
|
async def _scheduled_refresh() -> None:
|
|
"""Callback for APScheduler daily refresh."""
|
|
logger.info("Running scheduled schedule refresh...")
|
|
await schedule_service.refresh()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""Startup and shutdown lifecycle handler."""
|
|
# Startup: load schedule and start background scheduler
|
|
await schedule_service.initialize()
|
|
|
|
scheduler.add_job(
|
|
_scheduled_refresh,
|
|
trigger=CronTrigger(hour=3, minute=0, timezone="UTC"),
|
|
id="daily_schedule_refresh",
|
|
name="Refresh F1 schedule daily at 03:00 UTC",
|
|
replace_existing=True,
|
|
)
|
|
scheduler.start()
|
|
logger.info("APScheduler started - daily refresh at 03:00 UTC")
|
|
|
|
yield
|
|
|
|
# Shutdown
|
|
scheduler.shutdown(wait=False)
|
|
logger.info("APScheduler shut down")
|
|
|
|
|
|
app = FastAPI(title="F1 Streams", lifespan=lifespan)
|
|
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
return {"status": "ok"}
|
|
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
return {"service": "f1-streams", "version": "2.0.1"}
|
|
|
|
|
|
@app.get("/schedule")
|
|
async def get_schedule():
|
|
"""Return the F1 race schedule for the current season with session statuses."""
|
|
return schedule_service.get_schedule()
|
|
|
|
|
|
@app.post("/schedule/refresh")
|
|
async def refresh_schedule():
|
|
"""Manually trigger a schedule refresh from the jolpica API."""
|
|
await schedule_service.refresh()
|
|
return {"status": "refreshed"}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|