From 59e6a1a451617391054475dfd182ac0ff9cd46b9 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 12 Sep 2026 14:40:14 +0200 Subject: [PATCH] =?UTF-8?q?fix(pronote):=20conserver=20les=20annulations?= =?UTF-8?q?=20ambigu=C3=ABs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pronote_sync/sources/pronote/lessons.py | 35 ++++++++++--------------- tests/unit/test_pronote_lessons.py | 14 ++++++++++ 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/pronote_sync/sources/pronote/lessons.py b/pronote_sync/sources/pronote/lessons.py index a86fedd..4e1b945 100644 --- a/pronote_sync/sources/pronote/lessons.py +++ b/pronote_sync/sources/pronote/lessons.py @@ -43,16 +43,16 @@ def collapse_replaced_lessons(lessons: list[Lesson]) -> list[Lesson]: """Supprime l'ancien exemplaire annulé lorsqu'un cours déplacé est présent. Deux cours sont considérés comme les exemplaires d'une même modification - lorsqu'ils partagent les dates, la matière, les enseignants et le groupe. - Une annulation isolée est toujours conservée ; seule l'annulation associée - à un exemplaire ``MOVED`` est retirée. + lorsqu'ils partagent les dates, la matière, les enseignants et le groupe, + et qu'une seule annulation et un seul déplacement se distinguent par leur + salle. Une annulation isolée ou ambiguë est toujours conservée. :param lessons: Cours issus d'une source Pronote. :return: Cours consolidés dans leur ordre d'origine. :rtype: list[Lesson] """ - grouped: dict[tuple[object, ...], list[Lesson]] = defaultdict(list) - for lesson in lessons: + grouped: dict[tuple[object, ...], list[tuple[int, Lesson]]] = defaultdict(list) + for index, lesson in enumerate(lessons): key = ( lesson.start, lesson.end, @@ -60,20 +60,13 @@ def collapse_replaced_lessons(lessons: list[Lesson]) -> list[Lesson]: tuple(teacher.casefold() for teacher in lesson.teachers), (lesson.group or "").casefold(), ) - grouped[key].append(lesson) + grouped[key].append((index, lesson)) - result: list[Lesson] = [] - for lesson in lessons: - key = ( - lesson.start, - lesson.end, - lesson.subject.casefold(), - tuple(teacher.casefold() for teacher in lesson.teachers), - (lesson.group or "").casefold(), - ) - siblings = grouped[key] - has_moved = any(sibling.status == LessonStatus.MOVED for sibling in siblings) - if has_moved and lesson.status == LessonStatus.CANCELLED: - continue - result.append(lesson) - return result + replaced_indexes: set[int] = set() + for siblings in grouped.values(): + cancelled = [item for item in siblings if item[1].status == LessonStatus.CANCELLED] + moved = [item for item in siblings if item[1].status == LessonStatus.MOVED] + if len(cancelled) == 1 and len(moved) == 1 and cancelled[0][1].rooms != moved[0][1].rooms: + replaced_indexes.add(cancelled[0][0]) + + return [lesson for index, lesson in enumerate(lessons) if index not in replaced_indexes] diff --git a/tests/unit/test_pronote_lessons.py b/tests/unit/test_pronote_lessons.py index 666dbb1..794d2a1 100644 --- a/tests/unit/test_pronote_lessons.py +++ b/tests/unit/test_pronote_lessons.py @@ -75,3 +75,17 @@ def test_collapse_replaced_lessons_keeps_real_cancellation() -> None: cancelled = _lesson(LessonStatus.CANCELLED, "101") assert collapse_replaced_lessons([cancelled]) == [cancelled] + + +def test_collapse_replaced_lessons_keeps_ambiguous_cancellations() -> None: + """Conserve les annulations lorsqu'une paire de remplacement est ambiguë. + + :return: None + """ + cancelled_old = _lesson(LessonStatus.CANCELLED, "101") + cancelled_real = _lesson(LessonStatus.CANCELLED, "305") + moved = _lesson(LessonStatus.MOVED, "204") + + result = collapse_replaced_lessons([cancelled_old, cancelled_real, moved]) + + assert result == [cancelled_old, cancelled_real, moved]