Apply same link/content fixes to the Python variant

Mirrors the fixes made to the PHP feeds: correct link format
(/actualites|agenda/<slug>?u=<UID> instead of the wrong ?news=/?event=),
and full rich-text body + attachment links instead of the short teaser
only. Keeps both implementations in sync even though only the PHP one
is currently deployed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
para
2026-09-25 23:47:19 +02:00
co-authored by Claude Sonnet 5
parent 7da5a7a3e6
commit 13ea82e347
+44 -13
View File
@@ -14,6 +14,7 @@ import json
import re import re
import subprocess import subprocess
import sys import sys
import unicodedata
from datetime import datetime, timezone from datetime import datetime, timezone
from email.utils import format_datetime from email.utils import format_datetime
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
@@ -36,9 +37,6 @@ MAX_PAGES = 2 # jusqu'a 40 items par flux
NEWS_API = f"{SITE}/actualites/@results" NEWS_API = f"{SITE}/actualites/@results"
EVENTS_API = f"{SITE}/agenda/@results" EVENTS_API = f"{SITE}/agenda/@results"
NEWS_LINK_TMPL = f"{SITE}/actualites?news={{id}}"
EVENT_LINK_TMPL = f"{SITE}/agenda?event={{id}}"
def fetch_json(url: str) -> dict: def fetch_json(url: str) -> dict:
result = subprocess.run( result = subprocess.run(
@@ -72,6 +70,42 @@ def strip_html(text: str | None) -> str:
return re.sub(r"\s+", " ", text).strip() return re.sub(r"\s+", " ", text).strip()
def slugify(text: str) -> str:
"""
Reproduit le slug d'URL genere par le site (cosmetique : le routeur JS
de silly.be lit en realite le parametre `u` pour savoir quel item
afficher, pas ce segment de chemin - mais autant coller a l'URL
"officielle" telle qu'on la voit dans la barre d'adresse).
"""
ascii_text = unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode("ascii")
ascii_text = ascii_text.lower()
ascii_text = re.sub(r"[^a-z0-9]+", "-", ascii_text)
return ascii_text.strip("-")
def attachments_html(item: dict) -> str:
"""HTML des liens vers les pieces jointes (PDF, etc.) d'un item."""
parts = []
for sub in item.get("items") or []:
file_info = sub.get("file") or {}
url = file_info.get("download") or sub.get("targetUrl")
if not url:
continue
label = sub.get("title") or url.rsplit("/", 1)[-1]
parts.append(f'<p><a href="{html.escape(url)}">{html.escape(label)}</a></p>')
return "".join(parts)
def full_body_html(item: dict) -> str:
"""Corps HTML complet (texte riche + pieces jointes), repli sur le resume court."""
text_fr = item.get("text_fr")
body = (text_fr.get("data") or "").strip() if isinstance(text_fr, dict) else ""
if not body:
summary = strip_html(item.get("description_fr") or item.get("description"))
body = f"<p>{html.escape(summary)}</p>" if summary else ""
return body + attachments_html(item)
def parse_dt(value: str | None) -> datetime | None: def parse_dt(value: str | None) -> datetime | None:
if not value: if not value:
return None return None
@@ -91,13 +125,11 @@ def build_news_items() -> list[str]:
entries = [] entries = []
for item in fetch_all_items(NEWS_API): for item in fetch_all_items(NEWS_API):
item_id = item.get("id", "") item_id = item.get("id", "")
link = NEWS_LINK_TMPL.format(id=item_id)
title = item.get("title_fr") or item.get("title") or "(sans titre)" title = item.get("title_fr") or item.get("title") or "(sans titre)"
summary = strip_html(item.get("description_fr") or item.get("description"))
body = strip_html(item.get("text_fr", {}).get("data") if isinstance(item.get("text_fr"), dict) else None)
description = summary or body
pub_dt = parse_dt(item.get("effective") or item.get("created"))
guid = item.get("UID") or item_id guid = item.get("UID") or item_id
link = f"{SITE}/actualites/{slugify(title)}?u={guid}"
description = full_body_html(item)
pub_dt = parse_dt(item.get("effective") or item.get("created"))
entries.append( entries.append(
" <item>\n" " <item>\n"
f" <title>{escape(title)}</title>\n" f" <title>{escape(title)}</title>\n"
@@ -132,13 +164,12 @@ def build_event_items() -> list[str]:
entries = [] entries = []
for item in fetch_all_items(EVENTS_API): for item in fetch_all_items(EVENTS_API):
event_id = item.get("@id", "").rstrip("/").split("/")[-1] event_id = item.get("@id", "").rstrip("/").split("/")[-1]
link = EVENT_LINK_TMPL.format(id=event_id)
title = item.get("title_fr") or item.get("title") or "(sans titre)" title = item.get("title_fr") or item.get("title") or "(sans titre)"
summary = strip_html(item.get("description_fr") or item.get("description"))
meta = format_event_meta(item)
description = " | ".join(p for p in (meta, summary) if p)
pub_dt = parse_dt(item.get("created") or item.get("start"))
guid = item.get("UID") or event_id guid = item.get("UID") or event_id
link = f"{SITE}/agenda/{slugify(title)}?u={guid}"
meta = format_event_meta(item)
description = (f"<p>{html.escape(meta)}</p>" if meta else "") + full_body_html(item)
pub_dt = parse_dt(item.get("created") or item.get("start"))
entries.append( entries.append(
" <item>\n" " <item>\n"
f" <title>{escape(title)}</title>\n" f" <title>{escape(title)}</title>\n"