"""Rendu HTML du coût marginal inter-moteurs (Sprint 91, A.II.6).
Tableau récapitulatif des paires (A → B) avec le coût additionnel
par erreur évitée. Adaptive : retourne ``""`` si moins de 2 moteurs
ou si aucune paire n'a de données coût/erreur exploitables.
Permet à un archiviste de voir : *« passer de Tesseract à GPT-4o
coûte X € de plus par erreur évitée — est-ce justifié pour mon
budget ? »*
"""
from __future__ import annotations
from html import escape as _e
from typing import Optional
def build_marginal_cost_html(
matrix: Optional[list[dict]],
labels: Optional[dict[str, str]] = None,
) -> str:
"""Construit le tableau du coût marginal inter-moteurs.
Parameters
----------
matrix:
Sortie de
:func:`picarones.report.report_data.extra_metrics.compute_marginal_cost_section`.
Liste de dicts triée par coût marginal croissant. Si ``None``
ou vide, retourne ``""``.
labels:
Dict i18n optionnel.
"""
if not matrix:
return ""
labels = labels or {}
title = labels.get(
"marginal_cost_title",
"Coût marginal inter-moteurs (€ par erreur évitée)",
)
note = labels.get(
"marginal_cost_note",
"Pour chaque paire de moteurs (A → B), coût additionnel par "
"erreur évitée en passant de A à B. Valeur basse = changement "
"rentable. ‘Dominé’ = B est moins cher ET plus précis. Estimation "
"des erreurs basée sur ``cer × 1000`` (proxy par 1000 pages).",
)
h_from = labels.get("marginal_cost_from", "Depuis")
h_to = labels.get("marginal_cost_to", "Vers")
h_avoided = labels.get("marginal_cost_avoided", "Erreurs évitées")
h_delta = labels.get("marginal_cost_delta", "Coût Δ (€)")
h_per_err = labels.get("marginal_cost_per_err", "€ / erreur évitée")
h_dominated = labels.get("marginal_cost_dominated", "Dominé ?")
parts = [
'',
f'{_e(title)}
',
f''
f'{_e(note)}
',
'',
'',
]
for h in (h_from, h_to, h_avoided, h_delta, h_per_err, h_dominated):
parts.append(
f'| {_e(h)} | '
)
parts.append('
')
for row in matrix:
engine_a = row.get("engine_a") or row.get("from") or "?"
engine_b = row.get("engine_b") or row.get("to") or "?"
n_avoided = row.get("n_errors_avoided")
cost_delta = row.get("cost_delta")
cost_per_err = row.get("cost_per_avoided_error")
dominated = row.get("dominated", False)
n_avoided_cell = (
f"{int(n_avoided)}" if isinstance(n_avoided, (int, float)) else "—"
)
cost_delta_cell = (
f"{cost_delta:+.2f}" if isinstance(cost_delta, (int, float)) else "—"
)
if isinstance(cost_per_err, (int, float)):
cost_per_err_cell = f"{cost_per_err:.2f}"
else:
cost_per_err_cell = "—"
dominated_cell = (
'✓ B dominé par A'
if dominated else "—"
)
parts.append(
f''
f'| {_e(str(engine_a))} | '
f'{_e(str(engine_b))} | '
f'{n_avoided_cell} | '
f'{cost_delta_cell} | '
f'{cost_per_err_cell} | '
f'{dominated_cell} | '
f'
'
)
parts.append('
')
return "".join(parts)
__all__ = ["build_marginal_cost_html"]