diff --git a/backend/app/main.py b/backend/app/main.py index 37a831c..9233a39 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -21,7 +21,13 @@ async def lifespan(app: FastAPI): create_db_and_tables() from app.seed import run_seed run_seed() + # Démarrer le scheduler météo + from app.services.scheduler import setup_scheduler + setup_scheduler() yield + # Arrêter le scheduler + from app.services.scheduler import scheduler + scheduler.shutdown(wait=False) app = FastAPI(title="Jardin API", lifespan=lifespan) diff --git a/backend/app/services/scheduler.py b/backend/app/services/scheduler.py new file mode 100644 index 0000000..4186400 --- /dev/null +++ b/backend/app/services/scheduler.py @@ -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)")