diff --git a/artifacts/flask-api/app.py b/artifacts/flask-api/app.py index 9c2201c..b3da851 100644 --- a/artifacts/flask-api/app.py +++ b/artifacts/flask-api/app.py @@ -907,41 +907,63 @@ def export_consultation_print(slug: str): title_esc = html_module.escape(consultation["title"]) subject_esc = html_module.escape(consultation["subject"]) organizer_esc = html_module.escape(consultation.get("organizer_name") or "") - synthesis_esc = html_module.escape(synthesis_text).replace("\n", "
") now_str = dt.datetime.now(dt.timezone.utc).strftime("%d/%m/%Y %H:%M") + # Mise en forme du texte : "Sur X :" / "Concernant X :" en gras teal + import re as _re + _THEME_RE = _re.compile( + r'^((?:Sur|Concernant|À propos d[eu]?|En ce qui concerne|Quant à)[^:]{0,100}:\s?)([\s\S]*)$', + _re.IGNORECASE, + ) + paragraphs = [p.strip() for p in synthesis_text.split("\n\n") if p.strip()] + body_html = "" + for para in paragraphs: + m = _THEME_RE.match(para) + if m: + prefix = html_module.escape(m.group(1)) + rest = html_module.escape(m.group(2)) + body_html += f'

{prefix}{rest}

\n' + else: + body_html += f'

{html_module.escape(para)}

\n' + html_content = f""" {title_esc} -
-
- - - -
+
+
+

La Voix du Peuple · Consultation citoyenne

{title_esc}

- {f'

{organizer_esc}

' if organizer_esc else ""} -

{subject_esc}

-

Exporté le {now_str} — {idea_count} contribution(s) intégrée(s)

-
-
{synthesis_esc}
- diff --git a/artifacts/voix-du-peuple/src/pages/home.tsx b/artifacts/voix-du-peuple/src/pages/home.tsx index 1b1faf6..97e91c3 100644 --- a/artifacts/voix-du-peuple/src/pages/home.tsx +++ b/artifacts/voix-du-peuple/src/pages/home.tsx @@ -82,6 +82,34 @@ const VALEURS = [ // Clé hCaptcha — activée si la variable d'environnement est définie const HCAPTCHA_SITE_KEY = import.meta.env.VITE_HCAPTCHA_SITE_KEY as string | undefined; +// Détecte "Sur X :" / "Concernant Y :" en début de bloc et met le préfixe en valeur +function SynthesisText({ text }: { text: string }) { + const THEME_RE = /^((?:Sur|Concernant|À propos d[eu]?|En ce qui concerne|Quant à)[^:]{0,100}:\s?)([\s\S]*)$/i; + const blocks = text.split(/\n{2,}/); + return ( +
+ {blocks.map((block, i) => { + const trimmed = block.trim(); + if (!trimmed) return null; + const m = trimmed.match(THEME_RE); + if (m) { + return ( +

+ {m[1]} + {m[2]} +

+ ); + } + return ( +

+ {trimmed} +

+ ); + })} +
+ ); +} + const API_BASE = import.meta.env.VITE_API_URL ?? ""; export default function Home() { @@ -246,43 +274,60 @@ export default function Home() { if (!synthesis?.text) return; const date = format(new Date(), "d MMMM yyyy 'à' HH:mm", { locale: fr }); const nb = synthesis.ideaCount ?? 0; - const safe = (s: string) => s.replace(/&/g, "&").replace(//g, ">"); + const esc = (s: string) => s.replace(/&/g, "&").replace(//g, ">"); + const THEME_RE = /^((?:Sur|Concernant|À propos d[eu]?|En ce qui concerne|Quant à)[^:]{0,100}:\s?)([\s\S]*)$/i; + const formattedText = synthesis.text + .split(/\n{2,}/) + .filter(Boolean) + .map((block) => { + const t = block.trim(); + const m = t.match(THEME_RE); + if (m) return `

${esc(m[1])}${esc(m[2])}

`; + return `

${esc(t)}

`; + }) + .join("\n"); const html = ` La Voix du Peuple — Synthèse +
-

La Voix du Peuple — Synthèse citoyenne

-
Générée le ${safe(date)}  ·  ${nb} contribution${nb !== 1 ? "s" : ""} intégrée${nb !== 1 ? "s" : ""}
-
${safe(synthesis.text)}
+

La Voix du Peuple · Synthèse citoyenne

+

Synthèse des contributions

+
Générée le ${esc(date)}  ·  ${nb} contribution${nb !== 1 ? "s" : ""} intégrée${nb !== 1 ? "s" : ""}
+
${formattedText}
+
+ `; const w = window.open("", "_blank"); if (w) { w.document.write(html); w.document.close(); - w.onload = () => w.print(); } }; @@ -682,10 +727,10 @@ export default function Home() {
) : synthesis?.text ? (
- {synthesis.text} +
) : synthesis ? (

diff --git a/artifacts/voix-du-peuple/src/pages/transparence.tsx b/artifacts/voix-du-peuple/src/pages/transparence.tsx index 2a3e907..81a82ac 100644 --- a/artifacts/voix-du-peuple/src/pages/transparence.tsx +++ b/artifacts/voix-du-peuple/src/pages/transparence.tsx @@ -97,11 +97,37 @@ export default function Transparence() {

Articles surveillés

-

- Les articles de priorité critique et haute sont injectés dans chaque prompt. - Les articles moyenne sont suivis pour détecter les changements législatifs mais ne sont pas injectés - (le cadre légal statique les couvre déjà). -

+
+

+ La priorité détermine deux choses indépendantes : est-ce que le texte est injecté dans le prompt ? + et à quelle fréquence le surveille-t-on ? +

+ +

+ Dans tous les cas, si l'API Légifrance est indisponible au moment de l'analyse, le filtrage continue + sans interruption sur le cadre statique — sans dégradation visible pour l'utilisateur. +

+