feat(M5): source blog RSS — fetch, parsing, déduplication et état persistant
Implémentation complète de la source blog RSS du collège : - BlogRSSClient (sources/blog/rss.py) : client sans état récupérant et parsant le flux via feedparser, avec déduplication par ensemble de GUIDs connus, cache HTTP conditionnel (ETag/Last-Modified), conversion HTML→texte (BeautifulSoup), tri déterministe (date desc puis id asc), et mode dégradé (flux invalide/erreur → warning expurgé + liste vide). - BlogRSSFetchResult (sources/blog/result.py) : résultat immuable contenant articles, en-têtes de cache et indicateur not_modified. - BlogRSSState (sources/blog/state.py) : persistance JSON tolérante (GUIDs triés, version, ETag, Last-Modified) avec redaction des chemins dans les logs. - Fixture tests/fixtures/blog_rss.xml : flux RSS 2.0 anonymisé, 3 articles, dates fixes, ordre non chronologique. - 38 tests unitaires (22 client + 16 state) couvrant parsing nominal, déduplication intra-flux, 304, bozo, erreurs réseau, non-fuite de secrets, tri secondaire, persistance d'état et tolérance aux fichiers corrompus. - Documentation : TODO.md M5 coché, GUIDE_DEV_PYTHON.md §5 bis aligné avec l'API livrée (known_guids, BlogRSSFetchResult, BlogRSSState). - Configuration : feedparser ajouté aux additional_dependencies du hook mypy pre-commit pour aligner l'environnement isolé avec le .venv. Co-authored-by: opencode/coder <coder@agents.invalid> Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
This commit is contained in:
10
TODO.md
10
TODO.md
@@ -97,11 +97,11 @@ Récupérer et normaliser l'agenda, les devoirs et les messages Pronote, avec re
|
||||
|
||||
Récupérer le flux RSS du blog du collège, parser et dédupliquer les articles.
|
||||
|
||||
- [ ] Créer `sources/blog/rss.py` : `BlogRSSClient.fetch_and_parse(known_guids)` avec `feedparser` (§5 bis.7.1).
|
||||
- [ ] Parser les dates (RFC 822 / ISO 8601) et convertir le HTML en texte brut (`BeautifulSoup` + `html.unescape`).
|
||||
- [ ] Créer `sources/blog/state.py` (ou `sync/blog_state.py`) : `BlogRSSState` (JSON : `known_guids`, `etag`, `last_modified`).
|
||||
- [ ] Implémenter la déduplication par GUID et le cache HTTP (`If-Modified-Since` / `etag`).
|
||||
- [ ] Gérer un flux invalide (`bozo`) et les exceptions sans fuite de secret (retour `[]`/warning).
|
||||
- [x] Créer `sources/blog/rss.py` : `BlogRSSClient.fetch_and_parse(known_guids)` avec `feedparser` (§5 bis.7.1).
|
||||
- [x] Parser les dates (RFC 822 / ISO 8601) et convertir le HTML en texte brut (`BeautifulSoup` + `html.unescape`).
|
||||
- [x] Créer `sources/blog/state.py` (ou `sync/blog_state.py`) : `BlogRSSState` (JSON : `known_guids`, `etag`, `last_modified`).
|
||||
- [x] Implémenter la déduplication par GUID et le cache HTTP (`If-Modified-Since` / `etag`).
|
||||
- [x] Gérer un flux invalide (`bozo`) et les exceptions sans fuite de secret (retour `[]`/warning).
|
||||
|
||||
### Critères d'acceptation
|
||||
- `fetch_and_parse` renvoie les nouveaux articles triés par date décroissante, sans doublons.
|
||||
|
||||
Reference in New Issue
Block a user