Intègre les corrections de la revue indépendante (FIXME_M5.md) :
- Transport HTTP séparé du parsing (requests.get + feedparser.parse)
- Rejet des statuts HTTP d'erreur (raise_for_status)
- Préservation des validateurs de cache sur les chemins d'échec
- Sauvegarde atomique de l'état (tmp + Path.replace)
- Déduplication normale silencieuse
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>
- AgendaSource : enum StrEnum (AUTO/ICAL/PRONOTEPY)
- PronoteFetcherProtocol : Protocol consommé par le pipeline
- PronoteFetcher : unifie iCal et pronotepy avec repli automatique
- AUTO : iCal d'abord, repli pronotepy, PipelineCriticalError si
les deux échouent
- ICAL/PRONOTEPY : source explicite, erreur redactée puis
PipelineCriticalError
- Messages et informations : toujours via pronotepy
- Avertissement quand le repli pronotepy retourne un résultat vide
après échec iCal (limite du mode dégradé)
Co-authored-by: opencode/coder <coder@agents.invalid>
- PronoteClientProtocol : Protocol (get_messages, get_informations,
get_agenda_fallback) pour la logique de repli
- PronoteClient : encapsule pronotepy.Client (connexion paresseuse)
avec API réelle 2.15.7 (lessons/homework/discussions/information_and_surveys)
- Erreurs PronoteAPIError et ValueError catchées, loggées avec
redact_exception, retournent des résultats vides
- Aucune fuite de secret (get_secret_value() uniquement à la connexion)
- pre-commit : ajout de pronotepy au hook mypy
Co-authored-by: opencode/coder <coder@agents.invalid>
- config/settings.py : modèles Pydantic Settings (Pronote, CalDAV, XMPP,
AI, Blog, App) avec SecretStr pour les mots de passe et clés API
- config/env.py : fonction load_settings() pour le chargement du .env
- utils/redaction.py : redact_url, redact_secrets, redact_exception
(masquage des tokens icalsecurise, mots de passe et URLs sensibles)
- utils/logging.py : setup_logging + RedactingFormatter
(masquage automatique des secrets dans les logs)
- utils/uid.py : normalize_pronote_uid (suppression suffixes temporels)
et generate_deterministic_uid (hash SHA-1 usedforsecurity=False)
- .env.example : aligné sur les modèles finaux (XMPP_TO, XMPP_HOST,
XMPP_ENABLED, BLOG_ENABLED, etc.)
- .pre-commit-config.yaml : ajout pydantic + pydantic-settings aux
additional_dependencies du hook mypy
- TODO.md : items M2 cochés
Décisions d'architecture (@architect) :
- XmppSettings : modèle complet §10.2.3, tous champs optionnels
- sync_past_days/future_days déplacés vers AppSettings (sans préfixe)
- AISettings.enabled = False par défaut
- BlogSettings inclus dès M2
- redact_exception comme fonction module (pas méthode)
- normalize_pronote_uid (nom du guide et des tests)
Validations :
- ruff check : PASS
- ruff format --check : PASS
- mypy strict : PASS (7 fichiers)
- bandit : PASS (0 issue)
- import settings : OK (toutes valeurs par défaut)
- redact_secrets/icalsecurise : masqué en REDACTED
- logging : secret masqué dans la sortie
- uid normalize : idempotent, deterministic OK
- SecretStr : pas de fuite dans repr
- pytest : 0 test (infrastructure OK)
Co-authored-by: OpenCode/orchestrator <opencode-orchestrator@agents.invalid>
Ajout dans la section « Conventions de code » d'AGENTS.md :
- Docstrings obligatoires pour toute fonction, méthode et classe publique
- Format Sphinx/reST (:param, :return:, :rtype:, :raises)
- Docstring de module obligatoire
- Objectif : documentation PDF via Sphinx/LaTeX
- Exemple concret inclus
- 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)