Spaces:
Sleeping
Sleeping
File size: 9,071 Bytes
fe6661c 979f3c3 fe6661c 979f3c3 fe6661c 979f3c3 fe6661c c0f7ba9 fe6661c 979f3c3 fe6661c 979f3c3 fe6661c 979f3c3 fe6661c 979f3c3 fe6661c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | """Vue taxonomique avancée — chantier 3 post-Sprint 97.
Regroupe les renderers orientés *édition critique* qui examinent la
structure des erreurs OCR au-delà du CER global :
- :func:`picarones.report.taxonomy_comparison_render.build_taxonomy_comparison_html`
— diagramme miroir A vs B des proportions d'erreurs par classe
+ tableau de récupérabilité éditoriale.
- :func:`picarones.report.taxonomy_cooccurrence_render.build_taxonomy_cooccurrence_html`
— heatmap Jaccard des co-occurrences de classes au niveau document
(opt-in : nécessite ``per_doc_classes``).
- :func:`picarones.report.taxonomy_intra_doc_render.build_taxonomy_intra_doc_html`
— heatmap classe × position intra-document (opt-in : nécessite des
paires gt+hyp non compactées).
- :func:`picarones.report.lexical_modernization_render.build_lexical_modernization_html`
— top-N des tokens GT modernisés par le moteur (opt-in :
nécessite la sortie de ``compute_lexical_modernization``).
Sources de données automatiques
-------------------------------
- *Comparaison* : utilise ``aggregated_taxonomy.class_distribution``
(ou ``counts``) du leader CER vs le runner-up. Disponible dès qu'au
moins 2 moteurs ont une taxonomie agrégée.
Sources de données opt-in (via ``opts``)
----------------------------------------
- ``opts["cooccurrence"]`` : sortie de
:func:`picarones.measurements.taxonomy_cooccurrence.compute_taxonomy_cooccurrence`.
- ``opts["intra_doc"]`` : sortie de
:func:`picarones.measurements.taxonomy_intra_doc.compute_taxonomy_position_heatmap`.
- ``opts["lexical_modernization"]`` : sortie de
:func:`picarones.measurements.lexical_modernization.compute_lexical_modernization`
agrégée corpus-wide.
Ces calculs ne sont pas faits automatiquement par le runner standard
(coût et données nécessaires non triviaux après ``compact()``) ;
l'utilisateur peut les pré-calculer dans son workflow et les
fournir via :func:`build_advanced_taxonomy_view_html`.
"""
from __future__ import annotations
import logging
from typing import Optional
logger = logging.getLogger(__name__)
def _select_two_engines_for_comparison(
engines_summary: list[dict],
) -> Optional[tuple[dict, dict]]:
"""Choisit deux moteurs à comparer dans le diagramme miroir.
Stratégie : leader CER (plus bas) vs runner-up (deuxième). Si
moins de 2 moteurs ont une ``aggregated_taxonomy`` non vide,
retourne ``None``.
"""
candidates = [
e for e in engines_summary
if isinstance(e.get("aggregated_taxonomy"), dict)
and (
e["aggregated_taxonomy"].get("class_distribution")
or e["aggregated_taxonomy"].get("counts")
)
]
if len(candidates) < 2:
return None
# Tri par CER croissant (leader = meilleur). Les moteurs sans CER
# vont en queue (clé None considérée comme inf).
candidates.sort(
key=lambda e: e.get("cer") if e.get("cer") is not None else float("inf"),
)
return candidates[0], candidates[1]
def _extract_class_counts(engine_entry: dict) -> dict[str, float]:
"""Extrait le dict ``{class_name: count}`` d'une entrée moteur.
Supporte les deux formats observés en production :
- Sprint 5 historique : ``aggregated_taxonomy["class_distribution"]``
- Variante : ``aggregated_taxonomy["counts"]``
"""
tax = engine_entry.get("aggregated_taxonomy") or {}
counts = tax.get("class_distribution") or tax.get("counts") or {}
if not isinstance(counts, dict):
return {}
out: dict[str, float] = {}
for k, v in counts.items():
if isinstance(v, (int, float)) and v >= 0:
out[str(k)] = float(v)
return out
def build_advanced_taxonomy_view_html(
report_data: dict,
labels: Optional[dict[str, str]] = None,
*,
cooccurrence: Optional[dict] = None,
intra_doc: Optional[dict] = None,
lexical_modernization: Optional[dict] = None,
) -> str:
"""Compose la vue taxonomique avancée du rapport.
Parameters
----------
report_data:
Dict produit par :func:`generator._build_report_data`.
labels:
Dict i18n complet.
cooccurrence:
Sortie pré-calculée de
:func:`picarones.measurements.taxonomy_cooccurrence.compute_taxonomy_cooccurrence`.
Optionnel — la sous-section est masquée si non fourni.
intra_doc:
Sortie pré-calculée de
:func:`picarones.measurements.taxonomy_intra_doc.compute_taxonomy_position_heatmap`.
Optionnel.
lexical_modernization:
Sortie pré-calculée de
:func:`picarones.measurements.lexical_modernization.aggregate_lexical_modernization`.
Optionnel.
Returns
-------
str
HTML de la vue (entête + sous-sections collapsibles) ou
``""`` si aucune sous-section n'a de contenu.
"""
labels = labels or {}
blocks: list[tuple[str, str]] = []
# Sous-section 1 : comparaison des deux leaders
try:
engines_summary = report_data.get("engines") or []
pair = _select_two_engines_for_comparison(engines_summary)
if pair is not None:
from picarones.measurements.taxonomy_comparison import compare_taxonomies
from picarones.report.taxonomy_comparison_render import (
build_taxonomy_comparison_html,
)
engine_a, engine_b = pair
data = compare_taxonomies(
engine_a.get("name", "engine_a"),
_extract_class_counts(engine_a),
engine_b.get("name", "engine_b"),
_extract_class_counts(engine_b),
)
html = build_taxonomy_comparison_html(data, labels=labels)
if html:
blocks.append((
labels.get(
"advtax_comparison_title",
"Comparaison taxonomique (leader vs runner-up)",
),
html,
))
except Exception as exc: # noqa: BLE001
logger.warning(
"[advanced_taxonomy_view.comparison] dégradé : %s", exc,
)
# Sous-section 2 : co-occurrence (opt-in)
if cooccurrence:
try:
from picarones.report.taxonomy_cooccurrence_render import (
build_taxonomy_cooccurrence_html,
)
html = build_taxonomy_cooccurrence_html(cooccurrence, labels=labels)
if html:
blocks.append((
labels.get(
"advtax_cooccurrence_title",
"Co-occurrence de classes d'erreurs",
),
html,
))
except Exception as exc: # noqa: BLE001
logger.warning(
"[advanced_taxonomy_view.cooccurrence] dégradé : %s", exc,
)
# Sous-section 3 : intra-document (opt-in)
if intra_doc:
try:
from picarones.report.taxonomy_intra_doc_render import (
build_taxonomy_intra_doc_html,
)
html = build_taxonomy_intra_doc_html(intra_doc, labels=labels)
if html:
blocks.append((
labels.get(
"advtax_intra_doc_title",
"Distribution intra-document des classes",
),
html,
))
except Exception as exc: # noqa: BLE001
logger.warning(
"[advanced_taxonomy_view.intra_doc] dégradé : %s", exc,
)
# Sous-section 4 : modernisation lexicale (opt-in)
if lexical_modernization:
try:
from picarones.report.lexical_modernization_render import (
build_lexical_modernization_html,
)
html = build_lexical_modernization_html(
lexical_modernization, labels=labels,
)
if html:
blocks.append((
labels.get(
"advtax_lexmod_title",
"Modernisation lexicale (top tokens)",
),
html,
))
except Exception as exc: # noqa: BLE001
logger.warning(
"[advanced_taxonomy_view.lexmod] dégradé : %s", exc,
)
if not blocks:
return ""
# Réutilise le shell partagé de la vue economics
from picarones.report.views.economics import _render_view_shell
return _render_view_shell(
view_title=labels.get(
"advtax_view_title", "Taxonomie avancée des erreurs",
),
view_note=labels.get(
"advtax_view_note",
"Vue centrée sur l'édition critique : composition des "
"erreurs au-delà du CER global, pour décider quel moteur "
"produit des erreurs récupérables vs irrécupérables.",
),
blocks=blocks,
)
__all__ = ["build_advanced_taxonomy_view_html"]
|