"""Planification de la synchronisation CalDAV. Ce module compare les données Pronote normalisées aux événements distants marqués comme gérés par ``pronote-sync`` et produit un plan de synchronisation CalDAV (ajouts, mises à jour, suppressions) pour chaque catégorie d'événement : cours, devoirs et événements scolaires. Le plan est calculé de manière pure et déterministe : deux entrées identiques produisent un plan identique, et un événement dont la signature sémantique n'a pas changé n'apparaît dans aucune liste du plan (idempotence). """ from __future__ import annotations from typing import Any from pronote_sync.models.agenda import Lesson, SchoolEvent from pronote_sync.models.homework import Homework from pronote_sync.models.pronote import PronoteData from pronote_sync.models.sync import CalDAVSyncPlan from pronote_sync.sync.serialization import ( component_to_signature, homework_to_vevent, lesson_to_vevent, school_event_to_vevent, ) def compute_plan( pronote_data: PronoteData, remote_managed: list[tuple[str, Any]], ) -> CalDAVSyncPlan: """Calcule le plan de synchronisation CalDAV. :param pronote_data: Données Pronote normalisées (cours, devoirs, événements). :param remote_managed: Liste de couples (uid, vevent) pour les événements distants marqués comme gérés par pronote-sync. :return: Plan de synchronisation avec les listes d'ajouts, mises à jour et suppressions pour chaque type d'événement. :rtype: CalDAVSyncPlan """ remote_signatures: dict[str, str] = {} for uid, vevent in remote_managed: remote_signatures[uid] = component_to_signature(vevent) lessons_to_add: list[Lesson] = [] lessons_to_update: list[Lesson] = [] lessons_to_remove: list[str] = [] local_lessons_by_uid: dict[str, Lesson] = {lesson.id: lesson for lesson in pronote_data.lessons} for lesson in pronote_data.lessons: local_sig = component_to_signature(lesson_to_vevent(lesson)) if lesson.id not in remote_signatures: lessons_to_add.append(lesson) elif remote_signatures[lesson.id] != local_sig: lessons_to_update.append(lesson) for uid in remote_signatures: if ( uid not in local_lessons_by_uid and not uid.startswith("homework-") and not uid.startswith("school-event-") ): lessons_to_remove.append(uid) homeworks_to_add: list[Homework] = [] homeworks_to_update: list[Homework] = [] homeworks_to_remove: list[str] = [] local_homeworks_by_uid: dict[str, Homework] = { f"homework-{homework.id}": homework for homework in pronote_data.homeworks } for homework in pronote_data.homeworks: uid = f"homework-{homework.id}" local_sig = component_to_signature(homework_to_vevent(homework)) if uid not in remote_signatures: homeworks_to_add.append(homework) elif remote_signatures[uid] != local_sig: homeworks_to_update.append(homework) for uid in remote_signatures: if uid.startswith("homework-") and uid not in local_homeworks_by_uid: homeworks_to_remove.append(uid) school_events_to_add: list[SchoolEvent] = [] school_events_to_update: list[SchoolEvent] = [] school_events_to_remove: list[str] = [] local_school_events_by_uid: dict[str, SchoolEvent] = { f"school-event-{event.label}-{event.from_date.isoformat()}": event for event in pronote_data.school_events } for school_event in pronote_data.school_events: uid = f"school-event-{school_event.label}-{school_event.from_date.isoformat()}" local_sig = component_to_signature(school_event_to_vevent(school_event)) if uid not in remote_signatures: school_events_to_add.append(school_event) elif remote_signatures[uid] != local_sig: school_events_to_update.append(school_event) for uid in remote_signatures: if uid.startswith("school-event-") and uid not in local_school_events_by_uid: school_events_to_remove.append(uid) return CalDAVSyncPlan( lessons_to_add=lessons_to_add, lessons_to_update=lessons_to_update, lessons_to_remove=lessons_to_remove, homeworks_to_add=homeworks_to_add, homeworks_to_update=homeworks_to_update, homeworks_to_remove=homeworks_to_remove, school_events_to_add=school_events_to_add, school_events_to_update=school_events_to_update, school_events_to_remove=school_events_to_remove, )