From 82cd35d4ae45a38da7f439c68bd37e5f46acea87 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 20 Aug 2026 21:11:50 +0000 Subject: [PATCH] Corriger l'import des vacances scolaires officielles --- app_new/lib_ext/vacances_scolaires.py | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/app_new/lib_ext/vacances_scolaires.py b/app_new/lib_ext/vacances_scolaires.py index fd19435..7b9fc2b 100644 --- a/app_new/lib_ext/vacances_scolaires.py +++ b/app_new/lib_ext/vacances_scolaires.py @@ -1,12 +1,13 @@ """Service d'import des vacances scolaires depuis l'API gouvernementale.""" import requests from datetime import datetime, timedelta +from zoneinfo import ZoneInfo from app_new.extensions import db from app_new.core.models import CollegeClosure # API du gouvernement pour les vacances scolaires -API_URL = "https://data.education.gouv.fr/api/v2/catalog/datasets/fr-en-calendrier-scolaire-scolaires/exports/json" +API_URL = "https://data.education.gouv.fr/api/explore/v2.1/catalog/datasets/fr-en-calendrier-scolaire/records" def fetch_vacances_scolaires(zone="A", annee=None): @@ -25,15 +26,15 @@ def fetch_vacances_scolaires(zone="A", annee=None): # L'API du gouvernement filtre par zone et année params = { - "where": f"zones='{zone}'", + "where": f"zones='Zone {zone}' AND annee_scolaire='{annee}-{annee + 1}'", "order_by": "start_date", - "limit": 50 + "limit": 100, } try: response = requests.get(API_URL, params=params, timeout=30) response.raise_for_status() - data = response.json() + data = response.json().get("results", []) vacances = [] for item in data: @@ -43,13 +44,16 @@ def fetch_vacances_scolaires(zone="A", annee=None): if start and end: # Filtrer pour l'année scolaire demandée - start_date = datetime.fromisoformat(start.replace("Z", "+00:00")) + start_date = datetime.fromisoformat(start.replace("Z", "+00:00")).astimezone(ZoneInfo("Europe/Paris")) if start_date.year >= annee - 1: - raw_end_date = datetime.fromisoformat(end.replace("Z", "+00:00")).date() - end_date = raw_end_date - timedelta(days=1) + raw_end_date = datetime.fromisoformat(end.replace("Z", "+00:00")).astimezone(ZoneInfo("Europe/Paris")).date() + # Les fermetures ponctuelles (ex. pont de l'Ascension) + # sont représentées par le même début et la même fin. + end_date = raw_end_date if raw_end_date == start_date.date() else raw_end_date - timedelta(days=1) # Pour l'agent, le 31 août reste une journée de vacances # (la rentrée des enseignants ne déclenche pas la reprise). - if "grandes vacances" in description.lower() and raw_end_date.month == 8: + is_summer = "grandes vacances" in description.lower() or "vacances d'été" in description.lower() + if is_summer and raw_end_date.month == 8: end_date = raw_end_date vacances.append({ "start_date": start_date.date(), @@ -63,7 +67,15 @@ def fetch_vacances_scolaires(zone="A", annee=None): # Lorsque l'API fournit plusieurs calendriers (enseignants, élèves), # retenir la rentrée la plus tardive : la permanence reste possible # jusqu'à la veille de la rentrée des élèves. - grandes = [item for item in vacances if "grandes vacances" in item["description"].lower()] + # Une ligne existe par académie et parfois par population. Dédupliquer + # en conservant la date de rentrée la plus tardive (élèves). + deduped = {} + for item in vacances: + key = (item["description"], item["start_date"]) + if key not in deduped or item["end_date"] > deduped[key]["end_date"]: + deduped[key] = item + vacances = list(deduped.values()) + grandes = [item for item in vacances if "grandes vacances" in item["description"].lower() or "vacances d'été" in item["description"].lower()] if grandes: max_end = max(item["end_date"] for item in grandes) for item in grandes: