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:
2026-09-06 20:23:49 +02:00
parent 2c935ef648
commit 6d1a7a649f
11 changed files with 1895 additions and 161 deletions

47
tests/fixtures/blog_rss.xml vendored Normal file
View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>Blog du collège Les Mimosas</title>
<link>https://example.com/blog/</link>
<description>Actualités et informations du collège Les Mimosas</description>
<language>fr-FR</language>
<item>
<title>Information générale</title>
<link>https://example.com/blog/?p=1003</link>
<guid isPermaLink="false">https://example.com/blog/?p=1003</guid>
<pubDate>Wed, 12 Aug 2026 08:00:00 +0000</pubDate>
<description>Information générale à destination des familles.</description>
<content:encoded><![CDATA[
<p>La vie scolaire rappelle aux familles que les billets de cantine sont à commander avant le vendredi soir.</p>
<p>Pour toute question, consultez la page <a href="https://example.com/blog/cantine/">cantines et restauration</a> du site.</p>
]]></content:encoded>
</item>
<item>
<title>Réunion de rentrée</title>
<link>https://example.com/blog/?p=1001</link>
<guid isPermaLink="false">https://example.com/blog/?p=1001</guid>
<pubDate>Mon, 10 Aug 2026 09:00:11 +0000</pubDate>
<category>Administration</category>
<dc:creator>M. Dupont</dc:creator>
<description>Réunion de rentrée des parents d'élèves.</description>
<content:encoded><![CDATA[
<p>La réunion de rentrée des parents d'élèves se tiendra le mardi 15 septembre à 18 h 00 dans la salle polyvalente.</p>
<p>L'équipe pédagogique y présentera le projet d'établissement et le calendrier des conseils de classe. Un temps d'échange est prévu avec les professeurs principaux.</p>
<p>Merci de confirmer votre présence en remplissant le <a href="https://example.com/blog/reunion-rentree-inscription/">formulaire d'inscription</a> avant le 10 septembre.</p>
]]></content:encoded>
</item>
<item>
<title>Sortie pédagogique au musée</title>
<link>https://example.com/blog/?p=1002</link>
<guid isPermaLink="false">https://example.com/blog/?p=1002</guid>
<pubDate>Tue, 11 Aug 2026 14:30:00 +0000</pubDate>
<category>Pédagogie</category>
<description>Sortie pédagogique des élèves de 4e au musée d'art moderne.</description>
<content:encoded><![CDATA[
<p>Les élèves de 4e se rendront au musée d'art moderne le jeudi 8 octobre dans le cadre du cours d'arts plastiques.</p>
<p>La visite guidée portera sur la période impressionniste. Les élèves devront apporter un carnet de croquis et leur pique-nique.</p>
<p>Le détail de l'organisation figure dans la <a href="https://example.com/blog/sortie-musee-autorisation/">note d'autorisation</a> à retourner signée avant le 25 septembre.</p>
]]></content:encoded>
</item>
</channel>
</rss>