fix(M7): corrections d'audit FIXME_M7 — sécurité, fenêtre, UID, timezone
Corrige les 5 constats de l'audit FIXME_M7 : #1 (Bloquant) — Protection des événements non marqués : - upsert_event() vérifie le marqueur X-PRONOTE-SYNC-MANAGED avant modification ; lève PronoteSyncError en cas de collision avec un événement non géré (aucune écriture) - delete_event() vérifie le marqueur ; no-op avec warning si non géré - Méthode privée _is_managed_event() factorisant le contrôle #2 (Bloquant) — Fenêtre de synchronisation : - Calcul en journées entières (minuit à minuit exclusif) - Filtrage des données locales (lessons, homeworks, school_events) avant passage au planner - Paramètre now injectable pour les tests #3 (Bloquant) — UID canonique vs brut : - list_managed_events() retourne (raw_uid, canonical_uid, vevent) - compute_plan() matche par UID canonique, route les raw UID vers *_to_remove, retourne le mapping remote_raw_by_canonical - executor.execute() utilise le raw UID pour les mises à jour (pas de doublon) - Pas de migration destructive des UID distants existants #4 (Correction) — Normalisation temporelle UTC : - normalize_datetime_to_utc() dans utils/uid.py : naïve → Europe/Paris → UTC ; consciente → UTC - Utilisée par generate_deterministic_uid() et component_to_signature() - Deux représentations du même instant → même UID et même signature #5 (Compatibilité) — date_search déprécié : - Remplacement par calendar.search(start, end, event=True, expand=True) Documentation : - GUIDE_DEV_PYTHON.md : suppression des références obsolètes à sync/state.py et état SQLite/JSON ; mise à jour de l'API CalDAV (search au lieu de date_search, upsert par UID) - TODO.md : M7 décoché (corrections en cours de validation) Tests : 390 passés, couverture 95.61% Co-authored-by: opencode/coder <coder@agents.invalid> Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid> Co-authored-by: opencode/tech-writer <tech-writer@agents.invalid>
This commit is contained in:
@@ -11,6 +11,7 @@ from __future__ import annotations
|
||||
import hashlib
|
||||
import re
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
_TEMPORAL_SUFFIX_PATTERN = re.compile(r"-\d{8}T\d{6}Z-Index-Education$")
|
||||
_EDUCATION_SUFFIX_PATTERN = re.compile(r"-Index-Education$")
|
||||
@@ -31,6 +32,21 @@ def normalize_pronote_uid(uid: str) -> str:
|
||||
return _EDUCATION_SUFFIX_PATTERN.sub("", normalized)
|
||||
|
||||
|
||||
def normalize_datetime_to_utc(dt: datetime) -> datetime:
|
||||
"""Normalise une datetime vers UTC pour les signatures et hachages.
|
||||
|
||||
Les datetimes naïves sont interprétées comme Europe/Paris puis converties
|
||||
vers UTC. Les datetimes conscientes sont converties vers UTC.
|
||||
|
||||
:param dt: Datetime à normaliser (naïve ou consciente).
|
||||
:return: Datetime en UTC.
|
||||
:rtype: datetime
|
||||
"""
|
||||
if dt.tzinfo is None:
|
||||
return dt.replace(tzinfo=ZoneInfo("Europe/Paris")).astimezone(ZoneInfo("UTC"))
|
||||
return dt.astimezone(ZoneInfo("UTC"))
|
||||
|
||||
|
||||
def generate_deterministic_uid(
|
||||
start: datetime,
|
||||
end: datetime,
|
||||
@@ -56,8 +72,8 @@ def generate_deterministic_uid(
|
||||
:rtype: str
|
||||
"""
|
||||
parts = [
|
||||
start.isoformat(),
|
||||
end.isoformat(),
|
||||
normalize_datetime_to_utc(start).isoformat(),
|
||||
normalize_datetime_to_utc(end).isoformat(),
|
||||
subject,
|
||||
",".join(sorted(teachers)),
|
||||
",".join(sorted(rooms)),
|
||||
|
||||
Reference in New Issue
Block a user