"""Générateur du rapport HTML interactif auto-contenu.
Le rapport produit est un fichier HTML unique embarquant :
- Toutes les données (JSON inline)
- Chart.js et diff2html (depuis cdnjs)
- CSS et JavaScript de l'application
Vues disponibles
----------------
1. Classement — tableau triable par colonne (CER, WER, MER, WIL)
2. Galerie — grille d'images avec badge CER coloré
3. Document — image zoomable + diff coloré GT / OCR par moteur
4. Analyses — histogramme CER + graphique radar
Architecture
------------
Ce module est l'**orchestrateur**. Les responsabilités lourdes sont
découpées en sous-modules :
- :mod:`picarones.report.assets` — chargement vendor.js, encodage
base64 d'images, externalisation lazy.
- :mod:`picarones.report.report_data` — construction du dict JSON
passé au template (engines, documents, statistiques, Pareto, etc.).
- :mod:`picarones.report.render_helpers` — couleurs / SVG mutualisés.
Rétrocompat
-----------
Deux noms historiques sont **encore importés par des tests** sous
leur préfixe ``_`` et doivent être préservés :
- ``_build_report_data`` (importé par 14 fichiers de tests).
- ``_cer_color`` (importé par ``tests/report/test_report.py``).
Les autres noms ``_pct``, ``_safe``, ``_cer_bg``, ``_encode_image_b64``,
``_encode_images_b64_from_result``, ``_externalize_images_to_dir``,
``_load_vendor_js`` sont soit utilisés en interne (les 3 derniers,
voir :meth:`ReportGenerator.generate`), soit accessibles via leur
nom canonique dans :mod:`picarones.report.assets` ou
:mod:`picarones.report.render_helpers`.
"""
from __future__ import annotations
import json
import logging
from pathlib import Path
from typing import Any, Optional
from picarones.evaluation.benchmark_result import BenchmarkResult
from picarones.evaluation.statistics import build_critical_difference_svg
from picarones.reports._helpers.assets import (
encode_images_b64_from_result as _encode_images_b64_from_result,
externalize_images_to_dir as _externalize_images_to_dir,
load_vendor_js as _load_vendor_js,
)
# Ré-exports rétrocompat consommés par les tests externes (cf. docstring
# de module). La directive de fin de ligne documente l'intention de
# ré-export et empêche ruff de marquer l'import comme inutilisé.
from picarones.reports._helpers.render_helpers import cer_step_color as _cer_color # noqa: F401
from picarones.reports.html.data import build_report_data as _build_report_data # noqa: F401
logger = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
# Rendu Jinja2
# ---------------------------------------------------------------------------
# Depuis le Sprint 16, le template monolithique ~3100 lignes a été découpé en
# fichiers externes dans ``picarones/report/templates/`` (CSS, JS, vues HTML).
# ``base.html.j2`` assemble le tout via ``{% include %}``.
_TEMPLATES_DIR = Path(__file__).parent / "templates"
def _build_jinja_env():
"""Construit l'Environment Jinja2 pour le rapport.
Sprint S1 (Bandit B701, CWE-94) : autoescape activé via
``select_autoescape``. Les variables qui contiennent du HTML
pré-construit (renderers thématiques, SVG, JSON) sont marquées
avec ``| safe`` dans les templates ; les variables d'origine
utilisateur sont auto-échappées.
"""
from jinja2 import Environment, FileSystemLoader, select_autoescape
env = Environment(
loader=FileSystemLoader(str(_TEMPLATES_DIR)),
autoescape=select_autoescape(["html", "j2", "xml"]),
keep_trailing_newline=True,
)
return env
def _safe_json_for_script_tag(data: object) -> str:
"""Sérialise data en JSON safe pour injection dans `` dans une
chaîne JSON termine le tag