fix(pipeline): choisir le prochain jour de cours

This commit is contained in:
2026-09-12 14:04:20 +02:00
parent d82bd8ffd8
commit 42bc7b8f4a
4 changed files with 131 additions and 20 deletions
+32 -15
View File
@@ -2,16 +2,19 @@
from __future__ import annotations
import logging
from dataclasses import dataclass
from datetime import date
from pronote_sync.errors import PipelineCriticalError, PipelineWarning, PronoteAuthRotationError
from pronote_sync.models.agenda import Lesson, SchoolEvent
from pronote_sync.models.agenda import Lesson, LessonStatus, SchoolEvent
from pronote_sync.models.homework import Homework
from pronote_sync.models.message import Message
from pronote_sync.sources.pronote.fallback import PronoteFetcherProtocol
from pronote_sync.utils.redaction import redact_exception
logger = logging.getLogger(__name__)
@dataclass(frozen=True)
class FetchedPronoteData:
@@ -36,27 +39,41 @@ def resolve_target_date(
) -> date:
"""Détermine la date cible du digest à partir de l'agenda disponible.
La règle privilégie J+1 lorsqu'il contient des cours. Si la journée en
cours contient des cours mais pas J+1, le prochain cours connu est choisi.
Sans cours correspondant, J+1 est conservé, y compris pendant les vacances.
La règle privilégie J+1 lorsqu'il contient un cours non annulé. Sinon,
elle choisit le prochain jour possédant un cours effectif dans la fenêtre.
Si aucune reprise n'est visible, la fin exclusive d'un événement scolaire
couvrant J+1 est utilisée ; à défaut, J+1 reste le meilleur choix connu.
:param today: Date de référence de l'exécution.
:param lessons: Cours récupérés pour la fenêtre de synchronisation.
:param school_events: Événements scolaires récupérés (réservés aux évolutions
du libellé de jour sans cours).
:param school_events: Événements scolaires récupérés, avec borne de fin exclusive.
:return: Date cible du digest.
:rtype: date
"""
del school_events
tomorrow = date.fromordinal(today.toordinal() + 1)
lesson_dates = {lesson.start.date() for lesson in lessons}
if tomorrow in lesson_dates:
return tomorrow
if today in lesson_dates:
future_dates = sorted(day for day in lesson_dates if day > today)
if future_dates:
return future_dates[0]
return tomorrow
effective_dates = {
lesson.start.date()
for lesson in lessons
if lesson.status != LessonStatus.CANCELLED and lesson.start.date() > today
}
if tomorrow in effective_dates:
reason = "cours effectif demain"
target = tomorrow
elif effective_dates:
reason = "prochain cours effectif connu"
target = min(effective_dates)
else:
event_ends = [
event.to_date for event in school_events if event.from_date <= tomorrow < event.to_date
]
if event_ends:
reason = "fin exclusive de l'événement couvrant demain"
target = max(event_ends)
else:
reason = "aucune reprise connue"
target = tomorrow
logger.info("Date cible %s : %s", target.isoformat(), reason)
return target
def _fetch_optional_messages(