feat(scheduler): APScheduler 3 jobs météo dans FastAPI lifespan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,13 @@ async def lifespan(app: FastAPI):
|
|||||||
create_db_and_tables()
|
create_db_and_tables()
|
||||||
from app.seed import run_seed
|
from app.seed import run_seed
|
||||||
run_seed()
|
run_seed()
|
||||||
|
# Démarrer le scheduler météo
|
||||||
|
from app.services.scheduler import setup_scheduler
|
||||||
|
setup_scheduler()
|
||||||
yield
|
yield
|
||||||
|
# Arrêter le scheduler
|
||||||
|
from app.services.scheduler import scheduler
|
||||||
|
scheduler.shutdown(wait=False)
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(title="Jardin API", lifespan=lifespan)
|
app = FastAPI(title="Jardin API", lifespan=lifespan)
|
||||||
|
|||||||
108
backend/app/services/scheduler.py
Normal file
108
backend/app/services/scheduler.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
"""Scheduler APScheduler — 3 jobs de collecte météo."""
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
scheduler = AsyncIOScheduler(timezone="Europe/Paris")
|
||||||
|
|
||||||
|
|
||||||
|
def _store_station_current() -> None:
|
||||||
|
"""Collecte et stocke les données actuelles de la station."""
|
||||||
|
from app.services.station import fetch_current
|
||||||
|
from app.models.meteo import MeteoStation
|
||||||
|
from app.database import engine
|
||||||
|
from sqlmodel import Session
|
||||||
|
|
||||||
|
data = fetch_current()
|
||||||
|
if not data:
|
||||||
|
logger.warning("Station current: aucune donnée collectée")
|
||||||
|
return
|
||||||
|
|
||||||
|
now_str = datetime.now().strftime("%Y-%m-%dT%H:00")
|
||||||
|
entry = MeteoStation(date_heure=now_str, type="current", **data)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
existing = session.get(MeteoStation, now_str)
|
||||||
|
if existing:
|
||||||
|
for k, v in data.items():
|
||||||
|
setattr(existing, k, v)
|
||||||
|
session.add(existing)
|
||||||
|
else:
|
||||||
|
session.add(entry)
|
||||||
|
session.commit()
|
||||||
|
logger.info(f"Station current stockée : {now_str}")
|
||||||
|
|
||||||
|
|
||||||
|
def _store_station_veille() -> None:
|
||||||
|
"""Collecte et stocke le résumé de la veille (NOAA)."""
|
||||||
|
from datetime import timedelta
|
||||||
|
from app.services.station import fetch_yesterday_summary
|
||||||
|
from app.models.meteo import MeteoStation
|
||||||
|
from app.database import engine
|
||||||
|
from sqlmodel import Session
|
||||||
|
|
||||||
|
data = fetch_yesterday_summary()
|
||||||
|
if not data:
|
||||||
|
logger.warning("Station veille: aucune donnée collectée")
|
||||||
|
return
|
||||||
|
|
||||||
|
yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%dT00:00")
|
||||||
|
entry = MeteoStation(date_heure=yesterday, type="veille", **data)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
existing = session.get(MeteoStation, yesterday)
|
||||||
|
if existing:
|
||||||
|
for k, v in data.items():
|
||||||
|
setattr(existing, k, v)
|
||||||
|
session.add(existing)
|
||||||
|
else:
|
||||||
|
session.add(entry)
|
||||||
|
session.commit()
|
||||||
|
logger.info(f"Station veille stockée : {yesterday}")
|
||||||
|
|
||||||
|
|
||||||
|
def _store_open_meteo() -> None:
|
||||||
|
"""Collecte et stocke les prévisions Open-Meteo."""
|
||||||
|
from app.services.meteo import fetch_and_store_forecast
|
||||||
|
from app.models.meteo import MeteoOpenMeteo
|
||||||
|
from app.database import engine
|
||||||
|
from sqlmodel import Session
|
||||||
|
|
||||||
|
rows = fetch_and_store_forecast()
|
||||||
|
if not rows:
|
||||||
|
logger.warning("Open-Meteo: aucune donnée collectée")
|
||||||
|
return
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
for row in rows:
|
||||||
|
existing = session.get(MeteoOpenMeteo, row["date"])
|
||||||
|
if existing:
|
||||||
|
for k, v in row.items():
|
||||||
|
if k != "date":
|
||||||
|
setattr(existing, k, v)
|
||||||
|
session.add(existing)
|
||||||
|
else:
|
||||||
|
session.add(MeteoOpenMeteo(**row))
|
||||||
|
session.commit()
|
||||||
|
logger.info(f"Open-Meteo stocké : {len(rows)} jours")
|
||||||
|
|
||||||
|
|
||||||
|
def setup_scheduler() -> None:
|
||||||
|
"""Configure et démarre le scheduler."""
|
||||||
|
scheduler.add_job(
|
||||||
|
_store_station_current, "interval", hours=1,
|
||||||
|
next_run_time=datetime.now(), id="station_current", replace_existing=True,
|
||||||
|
)
|
||||||
|
scheduler.add_job(
|
||||||
|
_store_station_veille, "cron", hour=6, minute=0,
|
||||||
|
next_run_time=datetime.now(), id="station_veille", replace_existing=True,
|
||||||
|
)
|
||||||
|
scheduler.add_job(
|
||||||
|
_store_open_meteo, "interval", hours=1,
|
||||||
|
next_run_time=datetime.now(), id="open_meteo", replace_existing=True,
|
||||||
|
)
|
||||||
|
scheduler.start()
|
||||||
|
logger.info("Scheduler météo démarré (3 jobs)")
|
||||||
Reference in New Issue
Block a user