From 93333afffa243864177094aef77b6c44dfc7ed62 Mon Sep 17 00:00:00 2001 From: laurent Date: Wed, 25 Feb 2026 18:27:58 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20=5Feml=5Fto=5Fhtml=20retourne=20le=20pay?= =?UTF-8?q?load=20QP=20brut=20(accents=20non=20cass=C3=A9s)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problème : email.policy.default + get_content() décode déjà les accents (=C3=A9 → é), puis picnic._decode_and_parse() les re-encode en ASCII avec errors="replace" → les accents devenaient "?" → date introuvable. Solution : utiliser l'ancienne API email.message_from_bytes() sans policy et get_payload(decode=False) pour récupérer le corps brut encore QP-encodé, exactement comme un fichier .html sauvegardé depuis le mail. Co-Authored-By: Claude Sonnet 4.6 --- tickettracker/pipeline.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tickettracker/pipeline.py b/tickettracker/pipeline.py index 36e2a96..500e11b 100644 --- a/tickettracker/pipeline.py +++ b/tickettracker/pipeline.py @@ -114,24 +114,34 @@ def _parse(file_path: Path, source: str): def _eml_to_html(file_path: Path) -> str: """Extrait la partie HTML d'un fichier .eml (email de confirmation Picnic). - Lit le .eml avec le module email stdlib, parcourt les parties MIME - et retourne le contenu de la première partie text/html trouvée. + Retourne le corps HTML brut, encore encodé en Quoted-Printable (QP), + exactement comme si on lisait un fichier .html sauvegardé depuis le mail. + Le parser Picnic (picnic._decode_and_parse) se charge lui-même du décodage QP. + + Pourquoi ne pas utiliser policy.default / get_content() ? + Parce que cette API décode déjà les accents (=C3=A9 → é), ce qui empêche + picnic.py de les retrouver via sa propre pipeline QP → UTF-8. Args: file_path: Chemin vers le fichier .eml. Returns: - Contenu HTML sous forme de chaîne. + Corps HTML brut (QP-encodé) sous forme de chaîne ASCII. Raises: ValueError: Si aucune partie HTML n'est trouvée dans le .eml. """ raw = file_path.read_bytes() - msg = email.message_from_bytes(raw, policy=policy.default) + # On utilise l'ancienne API (sans policy.default) pour garder le payload brut + msg = email.message_from_bytes(raw) for part in msg.walk(): if part.get_content_type() == "text/html": - return part.get_content() + # decode=False → payload brut, encore QP-encodé, en str ASCII + payload = part.get_payload(decode=False) + if isinstance(payload, bytes): + return payload.decode("ascii", errors="replace") + return payload # déjà une str raise ValueError( f"Aucune partie HTML trouvée dans le fichier .eml : {file_path.name}"