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>
Corrige les 5 points de l'audit FIXME_M6 :
1. Normalisation des matières : fonction normalize_subject (NFKC +
unification des espaces + suppression ponctuation + minuscule)
partagée par la génération d'ID et le futur comparateur M8.
2. Expurgation du secret dans l'erreur de collision d'IDs :
redact_secrets enveloppe l'identifiant dans le message.
3. Test even/odd avec même matière pour isoler la parité comme seul
différenciateur d'ID ; tests de normalisation (casse, espaces,
Unicode) ; test de non-fuite de secret.
4. TODO.md M6 : 8 items cochés après validation.
5. GUIDE_DEV §8.4 : bloc de code corrigé (clôture, types Lesson/
TheoreticalLesson, comparaison des horaires en minutes, début ET
fin, référence à normalize_subject).
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
Implémentation complète de la source d'agenda théorique :
- model.py : modèles Pydantic de parsing JSON (TheoreticalLessonEntry,
TheoreticalAgendaFile) avec validation des formats d'heure et de l'ordre
début/fin.
- parity.py : WeekParityService déterministe calculant la parité d'une
semaine (paire/impaire) à partir d'une date de référence.
- holidays.py : SchoolHolidayCalendar lisant un fichier JSON de vacances
scolaires (zone A) avec bornes inclusives.
- provider.py : protocole TheoreticalAgendaProvider (get_lessons,
get_lessons_for_range).
- file.py : JsonTheoreticalAgendaProvider implémentant le protocole :
filtrage par parité et vacances, génération d'IDs déterministes
incluant le type de semaine, validation de l'unicité des IDs,
tri stable par identifiant.
- __init__.py : factory get_theoretical_provider câblant la
configuration (None si désactivé, erreur si config de parité partielle).
- Fixtures : theoretical.json (9 leçons all/even/odd) et
school_holidays.json (zone A, 4 périodes).
- 57 tests unitaires couvrant parsing, parité, vacances, provider,
factory, déduplication de range, collisions d'IDs.
- Guide : §8 et §12 alignés avec le format JSON.
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
Corrige les 5 points de l'audit FIXME_M5 :
1. (Bloquant) Sépare transport HTTP et parsing : utilise requests.get()
avec timeout explicite et en-têtes conditionnels, puis transmet le
contenu à feedparser.parse() — supprime le paramètre inexistant
request_timeout qui faisait échouer toute récupération réelle.
2. Rejette les statuts HTTP 4xx/5xx via raise_for_status() avant le
parsing.
3. Préserve les validateurs de cache (etag, last_modified) d'entrée sur
les chemins d'échec (exception, bozo) au lieu de les écraser à None.
4. Sauvegarde atomique de BlogRSSState : écrit dans un .tmp puis
Path.replace() pour éviter la corruption sur interruption.
5. Déduplication normale silencieuse : les GUID déjà connus sont
ignorés sans warning ; seuls les doublons intra-flux génèrent un
avertissement.
Tests : 49 tests (32 client + 17 state) dont 11 nouveaux couvrant
transport HTTP réel, statuts 401/404/500, préservation des validateurs,
en-têtes conditionnels, doublons intra-flux et sauvegarde atomique.
Guide : §5 bis.7.1 aligné avec le nouveau pattern transport/parsing.
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
Les modes explicites (ical, pronotepy) ne basculent plus silencieusement vers
l'autre source en cas d'échec. Seul le mode auto conserve le repli iCal →
pronotepy sur exception. Conforme au contrat de TODO.md M4 et AGENTS.md.
- fallback.py : _agenda_sources() et _homework_sources() retournent
(source, None) en mode explicite ; docstrings mises à jour.
- test_fallback.py : 4 tests remplacés, 4 tests ajoutés (non-repli +
empty-list-is-success), 4 tests ajustés ; :rtype: None ajouté.
Co-authored-by: coder/coder coder@agents.invalid
Co-authored-by: test-engineer/test-engineer test-engineer@agents.invalid
Configuration (D1+D3) :
- PronoteSettings : ajout pronote_url (str | None) et account_type
(Literal student/parent, défaut parent)
Parsing iCal (Fix 4+7) :
- parse_ical : détection STATUS:CANCELLED en plus de CATEGORIES
- parse_body : list[tuple[date, str]] au lieu de dict[date, str]
pour préserver les blocs multiples à la même date
- parse_homework_blocks : adapté aux listes
Co-authored-by: opencode/coder <coder@agents.invalid>
- test_ical.py : 20 tests (fetch file://, parsing VEVENT, détection
cours annulé, collect_homeworks avec déduplication, normalisation,
génération d'ID déterministe, redaction des erreurs)
- test_pronote_client.py : 9 tests (Protocol, messages mockés,
informations, agenda fallback, credentials manquants, sécurité
mot de passe)
- test_fallback.py : 16 tests (modes ICAL/PRONOTEPY/AUTO, repli,
PipelineCriticalError si les deux sources échouent, redaction
des erreurs)
- pre-commit : ajout de responses et pytest-mock au hook mypy
- Total : 42 nouveaux tests M4 (122 tests au total sur le projet)
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
- Guide de développement (GUIDE_DEV_PYTHON.md) : spécification complète
- TODO.md : 15 jalons de développement (M1-M15) avec étapes et critères d'acceptation
- AGENTS.md : guide de contribution pour agents et développeurs
- pyproject.toml : configuration projet (dépendances, ruff, mypy strict, pytest)
- .gitignore : exclusion venv, bytecode, .env, caches, state files
- .env.example : template des variables d'environnement (placeholders)
- Structure du package pronote_sync/ (14 sous-packages avec __init__.py)
- tests/ avec conftest.py et fixtures/
- Environnement virtuel .venv/ (Python 3.14)