diff --git a/docs/user/writing-a-pipeline-module.md b/docs/user/writing-a-pipeline-module.md index c3c29066de37c2568c00e316fbef9c16760edd84..cf069824aa879c82c81a66a17028088fdb2d2d9f 100644 --- a/docs/user/writing-a-pipeline-module.md +++ b/docs/user/writing-a-pipeline-module.md @@ -18,7 +18,7 @@ ```python from picarones.core.modules import BaseModule, ArtifactType -from picarones.core.pipeline_runner import ( +from picarones.core.pipeline import ( PipelineRunner, PipelineSpec, PipelineStep, ) @@ -150,7 +150,7 @@ class NERExtractor(BaseModule): ### 3.a Mono-document (Sprint 63) ```python -from picarones.core.pipeline_runner import ( +from picarones.core.pipeline import ( PipelineRunner, PipelineSpec, PipelineStep, ) @@ -178,7 +178,7 @@ que `Document.ground_truths` porte une `TextGT` (ou `AltoGT`, ### 3.b Corpus complet (Sprint 64) ```python -from picarones.core.pipeline_benchmark import run_pipeline_benchmark +from picarones.measurements.pipeline_benchmark import run_pipeline_benchmark bench = run_pipeline_benchmark(spec, my_corpus) print(bench.n_pipelines_succeeded, "/", bench.n_docs) @@ -203,7 +203,7 @@ bench = run_pipeline_benchmark(spec, corpus, initial_inputs_factory=my_factory) ### 3.c Comparer N pipelines (Sprint 65) ```python -from picarones.core.pipeline_comparison import compare_pipelines +from picarones.measurements.pipeline_comparison import compare_pipelines comparison = compare_pipelines( [spec_baseline, spec_with_correcteur_a, spec_with_correcteur_b], diff --git a/picarones/cli/__init__.py b/picarones/cli/__init__.py index 39d99f44910db162ea5824f85758710f0649b468..e474f91f5eddaf979fe3c42576d95aa78c1047e2 100644 --- a/picarones/cli/__init__.py +++ b/picarones/cli/__init__.py @@ -103,7 +103,7 @@ def cli() -> None: @click.option("--json-output", is_flag=True, default=False, help="Sortie en JSON") def metrics_cmd(reference: str, hypothesis: str, json_output: bool) -> None: """Calcule CER et WER entre deux fichiers texte.""" - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics ref_text = Path(reference).read_text(encoding="utf-8").strip() hyp_text = Path(hypothesis).read_text(encoding="utf-8").strip() @@ -309,7 +309,7 @@ def demo_cmd( # Suivi longitudinal if with_history: click.echo("\n── Démonstration suivi longitudinal ──────────────") - from picarones.core.history import BenchmarkHistory, generate_demo_history + from picarones.measurements.history import BenchmarkHistory, generate_demo_history history = BenchmarkHistory(":memory:") generate_demo_history(history, n_runs=8) entries = history.query(engine="tesseract") @@ -333,7 +333,7 @@ def demo_cmd( # Analyse de robustesse if with_robustness: click.echo("\n── Démonstration analyse de robustesse ───────────") - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report report = generate_demo_robustness_report( engine_names=["tesseract", "pero_ocr"] ) diff --git a/picarones/cli/_history.py b/picarones/cli/_history.py index debe68201c4aff56fa0d142446a597a1efd7ce83..d053a668218b6335784e9141633be41ce5c27505 100644 --- a/picarones/cli/_history.py +++ b/picarones/cli/_history.py @@ -103,7 +103,7 @@ def history_cmd( """ _setup_logging(verbose) - from picarones.core.history import BenchmarkHistory, generate_demo_history + from picarones.measurements.history import BenchmarkHistory, generate_demo_history history = BenchmarkHistory(db) diff --git a/picarones/cli/_imports.py b/picarones/cli/_imports.py index 8a825f79a37f298531bc468d89d6052e88a8792d..407a3e16b353c862059c6fbcdd6b9c5364ac3447 100644 --- a/picarones/cli/_imports.py +++ b/picarones/cli/_imports.py @@ -76,7 +76,7 @@ def import_iiif_cmd( """ _setup_logging(verbose) - from picarones.importers.iiif import IIIFImporter + from picarones.extras.importers.iiif import IIIFImporter click.echo(f"Manifeste IIIF : {manifest_url}") diff --git a/picarones/cli/_pipeline.py b/picarones/cli/_pipeline.py index 02d3fe07b0d1c6dfc547bc0b33be0f59fbeb580c..556b3f07b0165ae13bcacce7254c0272a1023dcd 100644 --- a/picarones/cli/_pipeline.py +++ b/picarones/cli/_pipeline.py @@ -66,8 +66,8 @@ def pipeline_run_cmd( import json as _json from picarones.core.corpus import load_corpus_from_directory - from picarones.core.pipeline_benchmark import run_pipeline_benchmark - from picarones.core.pipeline_spec_loader import load_pipeline_spec_from_yaml + from picarones.measurements.pipeline_benchmark import run_pipeline_benchmark + from picarones.measurements.pipeline_spec_loader import load_pipeline_spec_from_yaml spec = load_pipeline_spec_from_yaml(spec_path) corpus = load_corpus_from_directory(str(corpus_dir)) @@ -163,8 +163,8 @@ def pipeline_compare_cmd( """Compare N pipelines décrites dans SPECS_PATH sur le même corpus.""" from picarones.core.corpus import load_corpus_from_directory from picarones.core.modules import ArtifactType - from picarones.core.pipeline_comparison import compare_pipelines - from picarones.core.pipeline_spec_loader import ( + from picarones.measurements.pipeline_comparison import compare_pipelines + from picarones.measurements.pipeline_spec_loader import ( load_comparison_specs_from_yaml, ) diff --git a/picarones/cli/_robustness.py b/picarones/cli/_robustness.py index fea5dbf0d15e7f2a19bffb5a04ff6cd9316f738f..af7739d9fe7c32ccc41346bc84ec55b34201d6ea 100644 --- a/picarones/cli/_robustness.py +++ b/picarones/cli/_robustness.py @@ -99,7 +99,7 @@ def robustness_cmd( deg_types = [d.strip() for d in degradations.split(",") if d.strip()] - from picarones.core.robustness import ( + from picarones.measurements.robustness import ( RobustnessAnalyzer, ALL_DEGRADATION_TYPES, generate_demo_robustness_report ) @@ -139,7 +139,7 @@ def robustness_cmd( click.echo(f"Erreur moteur : {exc}", err=True) sys.exit(1) - from picarones.core.robustness import RobustnessAnalyzer + from picarones.measurements.robustness import RobustnessAnalyzer analyzer = RobustnessAnalyzer( engines=[ocr_engine], degradation_types=deg_types, diff --git a/picarones/cli/_workflows.py b/picarones/cli/_workflows.py index 0041360efe00c6877c7c49381193e30b1ef2c6f1..60057b0844db73592dbe07b4e87d80fc9dcdc9ba 100644 --- a/picarones/cli/_workflows.py +++ b/picarones/cli/_workflows.py @@ -90,7 +90,7 @@ def run_cmd( _setup_logging(verbose) from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark # Chargement du corpus try: @@ -183,7 +183,7 @@ def _run_workflow( _setup_logging(verbose) from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark try: corp = load_corpus_from_directory(corpus) diff --git a/picarones/core/abbreviations.py b/picarones/core/abbreviations.py deleted file mode 100644 index 22c7c0ba74f796db976327ed0f1069bfd0217ca2..0000000000000000000000000000000000000000 --- a/picarones/core/abbreviations.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.historical.abbreviations`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.historical.abbreviations import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.historical.abbreviations as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/baseline_comparison.py b/picarones/core/baseline_comparison.py deleted file mode 100644 index 96db6d2480877c36ac466559fcc345faed14aa58..0000000000000000000000000000000000000000 --- a/picarones/core/baseline_comparison.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.baseline_comparison`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.baseline_comparison import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.baseline_comparison as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/builtin_hooks.py b/picarones/core/builtin_hooks.py deleted file mode 100644 index 9d7b012892736db0281d5e64fe7781521116eee7..0000000000000000000000000000000000000000 --- a/picarones/core/builtin_hooks.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.builtin_hooks`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.builtin_hooks import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.builtin_hooks as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/calibration.py b/picarones/core/calibration.py deleted file mode 100644 index 7f70c0471244025c064bd0cd4620bce994f6ac14..0000000000000000000000000000000000000000 --- a/picarones/core/calibration.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.calibration`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.calibration import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.calibration as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/char_scores.py b/picarones/core/char_scores.py deleted file mode 100644 index 18597ae29732e42f46d5ba68fd7f85ab68eac9a0..0000000000000000000000000000000000000000 --- a/picarones/core/char_scores.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.char_scores`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.char_scores import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.char_scores as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/confusion.py b/picarones/core/confusion.py deleted file mode 100644 index ad64e881ddb8c148bd3ecb6172d38b474e2b1380..0000000000000000000000000000000000000000 --- a/picarones/core/confusion.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.confusion`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.confusion import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.confusion as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/cost_projection.py b/picarones/core/cost_projection.py deleted file mode 100644 index 709571da9d1a4a0374b1b8beea5c6074a762c8fc..0000000000000000000000000000000000000000 --- a/picarones/core/cost_projection.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.cost_projection`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.cost_projection import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.cost_projection as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/difficulty.py b/picarones/core/difficulty.py deleted file mode 100644 index 8926945c05f1cd898466071322ace5f72af205db..0000000000000000000000000000000000000000 --- a/picarones/core/difficulty.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.difficulty`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.difficulty import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.difficulty as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/early_modern_typography.py b/picarones/core/early_modern_typography.py deleted file mode 100644 index c7b04840e4fff6825ab955123e0f8e1919e266cd..0000000000000000000000000000000000000000 --- a/picarones/core/early_modern_typography.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.historical.early_modern_typography`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.historical.early_modern_typography import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.historical.early_modern_typography as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/equivalence_profile.py b/picarones/core/equivalence_profile.py deleted file mode 100644 index 6101ae37f28db04bb0eb178477aa3eec51f7de6c..0000000000000000000000000000000000000000 --- a/picarones/core/equivalence_profile.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.equivalence_profile`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.equivalence_profile import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.equivalence_profile as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/error_absorption.py b/picarones/core/error_absorption.py deleted file mode 100644 index 5f7874b9a70f652f8fb4d2041d3b83303ff5b67c..0000000000000000000000000000000000000000 --- a/picarones/core/error_absorption.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.error_absorption`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.error_absorption import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.error_absorption as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/measurements/narrative/facts.py b/picarones/core/facts.py similarity index 100% rename from picarones/measurements/narrative/facts.py rename to picarones/core/facts.py diff --git a/picarones/core/hallucination.py b/picarones/core/hallucination.py deleted file mode 100644 index 389092e6d153abb3bba5a7925c37861b097a0730..0000000000000000000000000000000000000000 --- a/picarones/core/hallucination.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.hallucination`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.hallucination import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.hallucination as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/history.py b/picarones/core/history.py deleted file mode 100644 index 5a6a77460ec44fa356eb3292d2e0e2cbec37b45f..0000000000000000000000000000000000000000 --- a/picarones/core/history.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.history`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.history import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.history as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/image_predictive.py b/picarones/core/image_predictive.py deleted file mode 100644 index 7c99e9990057682f8a3cb0dd470fcdd3ffccf5cf..0000000000000000000000000000000000000000 --- a/picarones/core/image_predictive.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.academic.image_predictive`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.academic.image_predictive import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.academic.image_predictive as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/image_quality.py b/picarones/core/image_quality.py deleted file mode 100644 index f265735b0044a73273d918a03794cc50eb17b887..0000000000000000000000000000000000000000 --- a/picarones/core/image_quality.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.image_quality`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.image_quality import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.image_quality as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/incremental_comparison.py b/picarones/core/incremental_comparison.py deleted file mode 100644 index 355cfacae59a5a0c5d5444ed5996b997e93e55d4..0000000000000000000000000000000000000000 --- a/picarones/core/incremental_comparison.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.incremental_comparison`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.incremental_comparison import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.incremental_comparison as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/inter_engine.py b/picarones/core/inter_engine.py deleted file mode 100644 index 7c9011670191defbb979878181761a448d955c75..0000000000000000000000000000000000000000 --- a/picarones/core/inter_engine.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.inter_engine`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.inter_engine import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.inter_engine as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/layout.py b/picarones/core/layout.py deleted file mode 100644 index 5a2eb6a965e1c6958510201e32efdaad3005a420..0000000000000000000000000000000000000000 --- a/picarones/core/layout.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.layout`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.layout import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.layout as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/levers.py b/picarones/core/levers.py deleted file mode 100644 index cce14722ab9e9ab126bb1619612f3841958ec446..0000000000000000000000000000000000000000 --- a/picarones/core/levers.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.levers`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.levers import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.levers as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/lexical_modernization.py b/picarones/core/lexical_modernization.py deleted file mode 100644 index e03585f890b46593598255f7c0269b2c5786aa2a..0000000000000000000000000000000000000000 --- a/picarones/core/lexical_modernization.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.historical.lexical_modernization`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.historical.lexical_modernization import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.historical.lexical_modernization as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/line_metrics.py b/picarones/core/line_metrics.py deleted file mode 100644 index d38ff0cb623e7473a8d914aa6f2c9d42f48ac526..0000000000000000000000000000000000000000 --- a/picarones/core/line_metrics.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.line_metrics`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.line_metrics import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.line_metrics as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/longitudinal.py b/picarones/core/longitudinal.py deleted file mode 100644 index 4e5732fe852320a8ebfc369421d100ab8504ccf8..0000000000000000000000000000000000000000 --- a/picarones/core/longitudinal.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.longitudinal`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.longitudinal import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.longitudinal as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/marginal_cost.py b/picarones/core/marginal_cost.py deleted file mode 100644 index 5d2fd947f6ffdf26efb1068e4d1f153a9283153c..0000000000000000000000000000000000000000 --- a/picarones/core/marginal_cost.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.marginal_cost`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.marginal_cost import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.marginal_cost as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/metric_hooks.py b/picarones/core/metric_hooks.py index 3f185f9d7eba032a2f2d70748da810231ab82069..05fbb7807527d5a419fc8bc0435470f0ddf05ac9 100644 --- a/picarones/core/metric_hooks.py +++ b/picarones/core/metric_hooks.py @@ -4,9 +4,9 @@ Chantier 2 du plan d'évolution post-Sprint 97. Pourquoi ce module ------------------ -Avant ce chantier, ``picarones.core.runner._compute_document_result`` +Avant ce chantier, ``picarones.measurements.runner._compute_document_result`` contenait **11 imports tardifs codés en dur** vers -``picarones.core.confusion``, ``char_scores``, ``taxonomy``, ``structure``, +``picarones.measurements.confusion``, ``char_scores``, ``taxonomy``, ``structure``, ``image_quality``, ``line_metrics``, ``hallucination``, ``philological_runner``, ``searchability_runner``, ``numerical_sequences_runner``, ``readability_runner`` — chacun enrobé diff --git a/picarones/core/modern_archives.py b/picarones/core/modern_archives.py deleted file mode 100644 index 775a50883a3a9e8c754aee4844c8385364bf7828..0000000000000000000000000000000000000000 --- a/picarones/core/modern_archives.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.historical.modern_archives`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.historical.modern_archives import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.historical.modern_archives as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/module_policy.py b/picarones/core/module_policy.py deleted file mode 100644 index 9f7814b9e67232037a2e4488cb35728aa3e9f58b..0000000000000000000000000000000000000000 --- a/picarones/core/module_policy.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.governance.module_policy`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.governance.module_policy import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.governance.module_policy as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/mufi.py b/picarones/core/mufi.py deleted file mode 100644 index 00ec90afc70ed1a522bf739e38a16e8a464c3669..0000000000000000000000000000000000000000 --- a/picarones/core/mufi.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.historical.mufi`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.historical.mufi import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.historical.mufi as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/__init__.py b/picarones/core/narrative/__init__.py deleted file mode 100644 index 72f3dcce154bdef95ce5bfa1baac36fb4edf5afc..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Alias rétrocompat — package déplacé dans :mod:`picarones.measurements.narrative`. - -Phase E du chantier de refonte en 3 cercles. Le moteur narratif -(Cercle 2) vit désormais dans ``picarones.measurements.narrative``. -Cet alias maintient la rétrocompat des imports historiques : -``from picarones.core.narrative import build_synthesis``, -``from picarones.core.narrative.facts import Fact``, etc. -""" - -from picarones.measurements.narrative import * # noqa: F401, F403 - -import picarones.measurements.narrative as _module -# Réexport explicite des noms privés (préfixe ``_``) que ``import *`` -# ne propage pas — rétrocompat des tests Sprints qui importent -# directement ``_DEFAULT_REGISTRY`` (test_sprint19_narrative_engine). -for _shim_name in dir(_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_module, _shim_name) -del _shim_name -__all__ = getattr(_module, "__all__", [ - nm for nm in dir(_module) if not nm.startswith("_") -]) diff --git a/picarones/core/narrative/arbiter.py b/picarones/core/narrative/arbiter.py deleted file mode 100644 index 537b8ca4d86b9c72607e04d2a2f2c0ea86ee540b..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/arbiter.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.arbiter`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.arbiter import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.arbiter as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/detectors/__init__.py b/picarones/core/narrative/detectors/__init__.py deleted file mode 100644 index 9d677e61151ff05be9f2d9e5bd0942a2fc7d883e..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/detectors/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Alias rétrocompat — package déplacé dans :mod:`picarones.measurements.narrative.detectors`. - -Phase E du chantier de refonte. Les 18 détecteurs en 6 familles -(ranking, pareto, stratum, quality, history, ensemble) vivent -désormais dans ``picarones.measurements.narrative.detectors/``. -""" - -from picarones.measurements.narrative.detectors import * # noqa: F401, F403 - -import picarones.measurements.narrative.detectors as _module -__all__ = getattr(_module, "__all__", [ - nm for nm in dir(_module) if not nm.startswith("_") -]) diff --git a/picarones/core/narrative/detectors/_helpers.py b/picarones/core/narrative/detectors/_helpers.py deleted file mode 100644 index 66ab9bc648a744714e81ad9caf7023dcb3aeb4fd..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/detectors/_helpers.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.detectors._helpers`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.detectors._helpers import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.detectors._helpers as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/detectors/ensemble.py b/picarones/core/narrative/detectors/ensemble.py deleted file mode 100644 index 866a1fcf29ce002dbba8d789e45623bec8de9553..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/detectors/ensemble.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.detectors.ensemble`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.detectors.ensemble import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.detectors.ensemble as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/detectors/history.py b/picarones/core/narrative/detectors/history.py deleted file mode 100644 index 11da8aeb8066eb448043e39a167a66ae713f6b5c..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/detectors/history.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.detectors.history`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.detectors.history import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.detectors.history as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/detectors/pareto.py b/picarones/core/narrative/detectors/pareto.py deleted file mode 100644 index 766efc7a5014013cf27aa86031e8f0ae5f0dc694..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/detectors/pareto.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.detectors.pareto`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.detectors.pareto import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.detectors.pareto as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/detectors/quality.py b/picarones/core/narrative/detectors/quality.py deleted file mode 100644 index d53a8adfab3e0f7167cd1964e32b83ac845ca429..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/detectors/quality.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.detectors.quality`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.detectors.quality import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.detectors.quality as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/detectors/ranking.py b/picarones/core/narrative/detectors/ranking.py deleted file mode 100644 index af9ba4275488bfb58baa518a0e19ade6c34a397e..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/detectors/ranking.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.detectors.ranking`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.detectors.ranking import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.detectors.ranking as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/detectors/stratum.py b/picarones/core/narrative/detectors/stratum.py deleted file mode 100644 index b24f8f2789fb266292b0ca2f4bd8f150760f1e33..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/detectors/stratum.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.detectors.stratum`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.detectors.stratum import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.detectors.stratum as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/facts.py b/picarones/core/narrative/facts.py deleted file mode 100644 index dbe7af96580e009f5f0e483e571b165f222d9b35..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/facts.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.facts`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.facts import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.facts as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/registry.py b/picarones/core/narrative/registry.py deleted file mode 100644 index b51183dc7a145bdb269d3920e78e9df15bee8264..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/registry.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.registry`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.registry import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.registry as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/renderer.py b/picarones/core/narrative/renderer.py deleted file mode 100644 index 5f3ccef7fad62388a86ee55f7fcbc7fa7fa34f45..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/renderer.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.narrative.renderer`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.narrative.renderer import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.narrative.renderer as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/narrative/templates/en.yaml b/picarones/core/narrative/templates/en.yaml deleted file mode 100644 index 40e2af6c48583a04243034dd742cc2568f2133e5..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/templates/en.yaml +++ /dev/null @@ -1,96 +0,0 @@ -# Narrative rendering templates — English. -# Anti-hallucination rule: never introduce a number or entity name that is not -# already in the Fact ``payload``. Tests verify traceability of every number -# appearing in the rendered synthesis. - -global_leader_cer: >- - On this corpus of {n_docs} documents, {engine} achieves the lowest mean CER - ({cer_pct} %). - -statistical_tie: >- - Engines {engines_list} are not statistically distinguishable - (Friedman-Nemenyi, α = {alpha}, n = {n_blocks} documents, CD = {critical_distance}). - -significant_gap: >- - The gap between {leader} and {runner_up} is statistically significant - (Wilcoxon, p = {p_value:.4f}, Δ CER = {delta_cer_pct} points over {n_pairs} pairs). - -stratum_winner: >- - On stratum "{stratum}" ({n_docs_stratum} documents), {engine} achieves - the lowest CER ({cer_pct} % vs. {second_cer_pct} % for {second_engine}). - -stratum_collapse: >- - {engine} is globally competitive ({global_cer_pct} %) but collapses on - stratum "{stratum}" ({local_cer_pct} % over {n_docs_stratum} documents, - i.e. {delta_cer_pct} points above its own average). - -error_profile_outlier: >- - {engine} has an atypical error profile: {proportion_pct} % of errors fall - into class "{error_class}", vs. a median of {median_proportion_pct} % across - other engines (×{ratio_to_median} the median). - -llm_hallucination_flag: >- - Hallucination signal on {engine} ({reasons_list}) — - {hallucinating_rate_pct} % of documents above alert thresholds. - -robustness_fragile: >- - {engine} is fragile under "{degradation}" degradation: its CER rises from - {cer_baseline_pct} % to {cer_degraded_pct} % at maximum level (×{ratio}). - -speed_winner: >- - {engine} is the fastest ({mean_duration} s/doc, ×{speedup} faster than the - median) for comparable quality (CER {cer_pct} %). - -confidence_warning: >- - High statistical uncertainty: the {confidence_level} % confidence interval of - {engine} spans {ci_width_pct} CER points, compared with a gap of - {gap_to_runner_up_pct} points to the runner-up. - -pareto_alternative: >- - At much lower cost, {engine} offers an interesting trade-off ({cer_pct} % - CER for {cost} €/{cost_unit_pages} pages, vs {leader_cer_pct} % / {leader_cost} € for - {leader}, i.e. ×{cost_saving_ratio} cheaper). - -cost_outlier: >- - Disproportionate cost for {engine} ({cost} €/{cost_unit_pages} pages, ×{ratio_to_median} - the median) without a compensating quality advantage (CER {cer_pct} %). - -ensemble_opportunity: >- - Engines {pair_a} and {pair_b} have divergent error profiles - ({divergence_metric}={divergence}). On this corpus of {doc_count} documents, - {best_engine} preserves {best_recall_pct} % of tokens; a majority vote - among the engines would preserve {oracle_recall_pct} % — i.e. - {absolute_gap_pct} points recoverable ({relative_gap_pct} % of the best - engine's errors). - -median_mean_gap_warning: >- - Asymmetric distribution for {engine}: median CER {median_cer_pct} % - vs mean {mean_cer_pct} % across {n_docs} documents (relative gap - {relative_gap_pct} %). The mean is pulled by a few catastrophic - documents — the median (now used for default ranking) is more - representative. - -stratification_recommended: >- - Heterogeneous corpus ({n_strata} strata): {leader} performs very - differently depending on document type — median CER - {min_stratum_cer_pct} % on "{min_stratum}" vs - {max_stratum_cer_pct} % on "{max_stratum}", a gap of {gap_pct} - points. The global ranking hides this disparity; consult the - stratified view. - -engine_off_baseline: >- - {engine} achieved {cer_current_pct} % CER here, vs {cer_historical_mean_pct} % - on average over the last {n_runs} runs of your institution on this - same corpus (relative delta {relative_delta_pct} %). This corpus is - harder for it than usual. - -engine_unstable: >- - Over {n_runs} successive runs, {engine} produces variable outputs - (CER CV {cer_cv_pct} %, identical-run pair rate {identical_run_rate_pct} %). - Reproducibility is limited — interpret the average CER with caution. - -regression_in_history: >- - Over the {n_runs} historical runs for {engine}, the average CER - moved from {first_cer_pct} % to {last_cer_pct} % - (cumulative change {absolute_delta_pct} points). Investigate what - changed in the pipeline or the models. diff --git a/picarones/core/narrative/templates/fr.yaml b/picarones/core/narrative/templates/fr.yaml deleted file mode 100644 index d3a858abfd5dcb5462007859458a8a59d43fafce..0000000000000000000000000000000000000000 --- a/picarones/core/narrative/templates/fr.yaml +++ /dev/null @@ -1,101 +0,0 @@ -# Templates de rendu narratif — français. -# -# Chaque clé correspond à une valeur de ``FactType``. La valeur est un template -# Python ``.format()`` qui consomme les champs du ``Fact.payload``. -# -# Règle anti-hallucination : n'introduire aucune valeur numérique ou nom -# d'entité qui ne soit pas dans le ``payload``. Les tests parsent la synthèse -# rendue et vérifient la traçabilité. - -global_leader_cer: >- - Sur ce corpus de {n_docs} documents, {engine} obtient le CER moyen le plus - bas ({cer_pct} %). - -statistical_tie: >- - Les moteurs {engines_list} ne sont pas statistiquement distinguables - (Friedman-Nemenyi, α = {alpha}, n = {n_blocks} documents, CD = {critical_distance}). - -significant_gap: >- - L'écart entre {leader} et {runner_up} est statistiquement significatif - (Wilcoxon, p = {p_value:.4f}, Δ CER = {delta_cer_pct} points sur {n_pairs} paires). - -stratum_winner: >- - Sur la strate « {stratum} » ({n_docs_stratum} documents), {engine} - obtient le CER le plus bas ({cer_pct} % contre {second_cer_pct} % - pour {second_engine}). - -stratum_collapse: >- - {engine} est globalement compétitif ({global_cer_pct} %) mais s'effondre sur - la strate « {stratum} » ({local_cer_pct} % sur {n_docs_stratum} documents, - soit {delta_cer_pct} points au-dessus de sa moyenne). - -error_profile_outlier: >- - Le profil d'erreurs de {engine} est atypique : {proportion_pct} % de la - classe « {error_class} », contre une médiane de {median_proportion_pct} % - sur les autres moteurs (ratio ×{ratio_to_median}). - -llm_hallucination_flag: >- - Signal d'hallucination sur {engine} ({reasons_list}) — - {hallucinating_rate_pct} % de documents au-dessus des seuils d'alerte. - -robustness_fragile: >- - {engine} est fragile à la dégradation « {degradation} » : son CER passe de - {cer_baseline_pct} % à {cer_degraded_pct} % au niveau maximal (ratio ×{ratio}). - -speed_winner: >- - {engine} est le plus rapide ({mean_duration} s / doc, ×{speedup} plus vite - que la médiane) pour un CER comparable ({cer_pct} %). - -confidence_warning: >- - Incertitude statistique élevée : l'intervalle de confiance à {confidence_level} % - de {engine} s'étend sur {ci_width_pct} points de CER, à comparer à l'écart de - {gap_to_runner_up_pct} points avec le second. - -pareto_alternative: >- - À coût sensiblement inférieur, {engine} offre un compromis intéressant - ({cer_pct} % de CER pour {cost} €/{cost_unit_pages} pages, contre {leader_cer_pct} % / - {leader_cost} € pour {leader}, soit ×{cost_saving_ratio} moins cher). - -cost_outlier: >- - Coût disproportionné pour {engine} ({cost} €/{cost_unit_pages} pages, ×{ratio_to_median} - la médiane) sans avantage de qualité compensatoire (CER {cer_pct} %). - -ensemble_opportunity: >- - Les moteurs {pair_a} et {pair_b} ont des profils d'erreurs divergents - ({divergence_metric}={divergence}). Sur ce corpus de {doc_count} documents, - {best_engine} préserve {best_recall_pct} % des tokens ; un voting majoritaire - entre les moteurs en préserverait {oracle_recall_pct} %, soit - {absolute_gap_pct} points récupérables ({relative_gap_pct} % des erreurs - du meilleur moteur). - -median_mean_gap_warning: >- - Distribution asymétrique pour {engine} : médiane CER {median_cer_pct} % - vs moyenne {mean_cer_pct} % sur {n_docs} documents (écart relatif - {relative_gap_pct} %). La moyenne est tirée par quelques documents - catastrophiques — la médiane (utilisée pour le tri par défaut) est - plus représentative. - -stratification_recommended: >- - Corpus hétérogène ({n_strata} strates) : {leader} performe très - différemment selon le type de document — médiane CER - {min_stratum_cer_pct} % sur « {min_stratum} » contre - {max_stratum_cer_pct} % sur « {max_stratum} », soit {gap_pct} points - d'écart. Le classement global masque cette disparité ; consulter la - vue stratifiée. - -engine_off_baseline: >- - {engine} a obtenu {cer_current_pct} % CER ici, vs {cer_historical_mean_pct} % - en moyenne sur les {n_runs} runs précédents de votre institution sur - ce même corpus (écart relatif {relative_delta_pct} %). Ce corpus lui - est plus difficile que d'habitude. - -engine_unstable: >- - Sur {n_runs} runs successifs, {engine} produit des sorties variables - (CV CER {cer_cv_pct} %, paires de runs identiques {identical_run_rate_pct} %). - La reproductibilité est limitée — interpréter le CER moyen avec prudence. - -regression_in_history: >- - Sur les {n_runs} runs historiques pour {engine}, le CER moyen - est passé de {first_cer_pct} % à {last_cer_pct} % - (variation cumulée {absolute_delta_pct} points). Vérifier ce qui - a changé dans le pipeline ou les modèles. diff --git a/picarones/core/ner.py b/picarones/core/ner.py deleted file mode 100644 index fbe1636fd6f17410174d301cd4c5a5c5f0869977..0000000000000000000000000000000000000000 --- a/picarones/core/ner.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.ner`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.ner import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.ner as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/ner_backends.py b/picarones/core/ner_backends.py deleted file mode 100644 index 44f96cd6b909d1796f0a51caecf0fed5b412a462..0000000000000000000000000000000000000000 --- a/picarones/core/ner_backends.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.ner_backends`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.ner_backends import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.ner_backends as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/normalization.py b/picarones/core/normalization.py deleted file mode 100644 index 3c918f13020ad8929570b09b3fdc0f3d92bba2c3..0000000000000000000000000000000000000000 --- a/picarones/core/normalization.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.normalization`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.normalization import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.normalization as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/numerical_sequences.py b/picarones/core/numerical_sequences.py deleted file mode 100644 index 8e25033fbfa6cf75cb83ce3e95226ce6f760172f..0000000000000000000000000000000000000000 --- a/picarones/core/numerical_sequences.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.numerical_sequences`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.numerical_sequences import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.numerical_sequences as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/numerical_sequences_runner.py b/picarones/core/numerical_sequences_runner.py deleted file mode 100644 index 0cf26886e75ae57f78571087c86a9fa6129fb50e..0000000000000000000000000000000000000000 --- a/picarones/core/numerical_sequences_runner.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.numerical_sequences_runner`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.numerical_sequences_runner import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.numerical_sequences_runner as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/philological_runner.py b/picarones/core/philological_runner.py deleted file mode 100644 index 4483f7df1ae1c8b2fb1639eed30ca52168fb66d1..0000000000000000000000000000000000000000 --- a/picarones/core/philological_runner.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.historical.philological_runner`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.historical.philological_runner import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.historical.philological_runner as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/pipeline_runner.py b/picarones/core/pipeline.py similarity index 97% rename from picarones/core/pipeline_runner.py rename to picarones/core/pipeline.py index b3b29bea2de2095a9977e13b2bf1cc7c50e8f65c..3c58191ebfd774463a24c05f4ca6ec1bfb003708 100644 --- a/picarones/core/pipeline_runner.py +++ b/picarones/core/pipeline.py @@ -61,21 +61,21 @@ from picarones.core.modules import ArtifactType, BaseModule # registre typé (Sprint 34) — sans ces imports, ``compute_at_junction`` # trouverait un registre vide et ne calculerait rien aux jonctions. # Sprint 34 : cer / wer / mer / wil + stub TEXT→ALTO -import picarones.core.builtin_metrics # noqa: F401 +import picarones.measurements.builtin_metrics # noqa: F401 # Sprints 55-60 : métriques philologiques. -import picarones.core.unicode_blocks # noqa: F401 -import picarones.core.abbreviations # noqa: F401 -import picarones.core.mufi # noqa: F401 -import picarones.core.early_modern_typography # noqa: F401 -import picarones.core.modern_archives # noqa: F401 -import picarones.core.roman_numerals # noqa: F401 +import picarones.measurements.unicode_blocks # noqa: F401 +import picarones.measurements.abbreviations # noqa: F401 +import picarones.measurements.mufi # noqa: F401 +import picarones.measurements.early_modern_typography # noqa: F401 +import picarones.measurements.modern_archives # noqa: F401 +import picarones.measurements.roman_numerals # noqa: F401 # Sprint 53 : reading order F1. Sprints 38, 52 : NER, readability. -import picarones.core.reading_order # noqa: F401 -import picarones.core.readability # noqa: F401 -import picarones.core.ner # noqa: F401 +import picarones.measurements.reading_order # noqa: F401 +import picarones.measurements.readability # noqa: F401 +import picarones.measurements.ner # noqa: F401 # Chantier 1 (post-Sprint 97) : métriques (ALTO, ALTO) pour évaluer # les reconstructeurs ALTO contre une GT ALTO du document. -import picarones.core.alto_metrics # noqa: F401 +import picarones.measurements.alto_metrics # noqa: F401 logger = logging.getLogger(__name__) diff --git a/picarones/core/pricing.py b/picarones/core/pricing.py deleted file mode 100644 index b9431ed6431ffe713c56252a03d9d7fd105eadff..0000000000000000000000000000000000000000 --- a/picarones/core/pricing.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.pricing`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.pricing import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.pricing as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/rare_tokens.py b/picarones/core/rare_tokens.py deleted file mode 100644 index 17a9285a41f42100915b669b6a0912fd97bbdbb6..0000000000000000000000000000000000000000 --- a/picarones/core/rare_tokens.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.rare_tokens`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.rare_tokens import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.rare_tokens as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/readability.py b/picarones/core/readability.py deleted file mode 100644 index 9c0a96ef15c99e4bc1767b1459cce52de421d4e8..0000000000000000000000000000000000000000 --- a/picarones/core/readability.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.readability`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.readability import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.readability as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/readability_runner.py b/picarones/core/readability_runner.py deleted file mode 100644 index ab5d818bda6a3b7b32d08c9b26b092431ae1bf28..0000000000000000000000000000000000000000 --- a/picarones/core/readability_runner.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.readability_runner`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.readability_runner import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.readability_runner as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/reading_order.py b/picarones/core/reading_order.py deleted file mode 100644 index 59d5c5129d510390368f714f8d8d88812cb57dcb..0000000000000000000000000000000000000000 --- a/picarones/core/reading_order.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.reading_order`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.reading_order import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.reading_order as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/reliability.py b/picarones/core/reliability.py deleted file mode 100644 index 4d5a5115614536b70a21b2b3e93d23bdf5fc0aae..0000000000000000000000000000000000000000 --- a/picarones/core/reliability.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.reliability`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.reliability import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.reliability as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/results.py b/picarones/core/results.py index 20e4d01a7479f62a5cc7dcd7f93ec0519e57c01a..83a60694ad04381e921c36b57daf12b7641daa0d 100644 --- a/picarones/core/results.py +++ b/picarones/core/results.py @@ -16,7 +16,7 @@ from pathlib import Path from typing import Optional from picarones import __version__ -from picarones.core.metrics import MetricsResult, aggregate_metrics +from picarones.measurements.metrics import MetricsResult, aggregate_metrics @dataclass diff --git a/picarones/core/robustness.py b/picarones/core/robustness.py deleted file mode 100644 index 5d1d852de91ed93ab00345bd36cb1941c1a2de50..0000000000000000000000000000000000000000 --- a/picarones/core/robustness.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.robustness`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.robustness import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.robustness as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/robustness_projection.py b/picarones/core/robustness_projection.py deleted file mode 100644 index 2452a6742bff74c9df429536838ae9bc7dcf0cf6..0000000000000000000000000000000000000000 --- a/picarones/core/robustness_projection.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.robustness_projection`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.robustness_projection import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.robustness_projection as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/roman_numerals.py b/picarones/core/roman_numerals.py deleted file mode 100644 index b269775f2b32b5c5289911b21b2da0e749393ddd..0000000000000000000000000000000000000000 --- a/picarones/core/roman_numerals.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.historical.roman_numerals`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.historical.roman_numerals import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.historical.roman_numerals as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/searchability.py b/picarones/core/searchability.py deleted file mode 100644 index 7b5566bf4a1bcc768e7719d23cbaf73a84a0397b..0000000000000000000000000000000000000000 --- a/picarones/core/searchability.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.searchability`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.searchability import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.searchability as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/searchability_runner.py b/picarones/core/searchability_runner.py deleted file mode 100644 index 7dcbd0cbd815fb42289ab1e40226e26d2afdcb96..0000000000000000000000000000000000000000 --- a/picarones/core/searchability_runner.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.searchability_runner`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.searchability_runner import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.searchability_runner as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/specialization.py b/picarones/core/specialization.py deleted file mode 100644 index f67cdab273dc61116e505c3dc11d021060cb741a..0000000000000000000000000000000000000000 --- a/picarones/core/specialization.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.specialization`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.specialization import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.specialization as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/statistics.py b/picarones/core/statistics.py deleted file mode 100644 index 689cac5cc9e48a1993728558dde901e8c47418d9..0000000000000000000000000000000000000000 --- a/picarones/core/statistics.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.statistics`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.statistics import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.statistics as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/structure.py b/picarones/core/structure.py deleted file mode 100644 index 7b703e560670ba26e0f5f4fff230348b264b5d64..0000000000000000000000000000000000000000 --- a/picarones/core/structure.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.structure`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.structure import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.structure as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/taxonomy.py b/picarones/core/taxonomy.py deleted file mode 100644 index c2dec846c4362e6ec7dcc9b3917b64c52da940c8..0000000000000000000000000000000000000000 --- a/picarones/core/taxonomy.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.taxonomy`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.taxonomy import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.taxonomy as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/taxonomy_comparison.py b/picarones/core/taxonomy_comparison.py deleted file mode 100644 index 62d5e031af58c724aa1f0d57431a12abb9050d59..0000000000000000000000000000000000000000 --- a/picarones/core/taxonomy_comparison.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.taxonomy_comparison`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.taxonomy_comparison import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.taxonomy_comparison as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/taxonomy_cooccurrence.py b/picarones/core/taxonomy_cooccurrence.py deleted file mode 100644 index f1d499873b24b2d418c36477e1fa7ee9ad19931f..0000000000000000000000000000000000000000 --- a/picarones/core/taxonomy_cooccurrence.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.academic.taxonomy_cooccurrence`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.academic.taxonomy_cooccurrence import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.academic.taxonomy_cooccurrence as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/taxonomy_intra_doc.py b/picarones/core/taxonomy_intra_doc.py deleted file mode 100644 index 68e4f9c74dc62b8629e120284399922760269a81..0000000000000000000000000000000000000000 --- a/picarones/core/taxonomy_intra_doc.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.academic.taxonomy_intra_doc`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.academic.taxonomy_intra_doc import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.academic.taxonomy_intra_doc as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/throughput.py b/picarones/core/throughput.py deleted file mode 100644 index e84ffeaeb65e0d13ece2a8683d8646d6d606a716..0000000000000000000000000000000000000000 --- a/picarones/core/throughput.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.throughput`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.throughput import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.throughput as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/unicode_blocks.py b/picarones/core/unicode_blocks.py deleted file mode 100644 index 6c5906a4bff39377b3092bfc768303a6f8567bd4..0000000000000000000000000000000000000000 --- a/picarones/core/unicode_blocks.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.extras.historical.unicode_blocks`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.extras.historical.unicode_blocks import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.extras.historical.unicode_blocks as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/core/worst_lines.py b/picarones/core/worst_lines.py deleted file mode 100644 index b8d4ec6dbd31ba302e71f9e71c62b962538b4ed2..0000000000000000000000000000000000000000 --- a/picarones/core/worst_lines.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Alias rétrocompat — module déplacé dans :mod:`picarones.measurements.worst_lines`. - -Le contenu vit désormais dans son cercle d'origine. Cet alias permet -aux imports historiques (y compris les noms privés ``_*``) de -continuer à fonctionner sans modification. - -Voir :doc:`docs/architecture-cercles.md` pour la cartographie. -""" - -from picarones.measurements.worst_lines import * # noqa: F401, F403 - -# Réexport explicite de TOUS les noms (privés inclus) pour la -# rétrocompatibilité des tests Sprints qui importent ``_helper``, -# ``_compute_X``, ``_SCIPY_AVAILABLE``, etc. Sans cette boucle, ``import *`` -# ne propage que les noms publics et casse les imports historiques. -import picarones.measurements.worst_lines as _shim_module -for _shim_name in dir(_shim_module): - if _shim_name == "__builtins__": - continue - if _shim_name not in globals(): - globals()[_shim_name] = getattr(_shim_module, _shim_name) -del _shim_module, _shim_name - -__all__ = [ - _n for _n in dir() if not _n.startswith("__") -] diff --git a/picarones/fixtures.py b/picarones/fixtures.py index a3d4b957a17bcfe67f317e342cd3136f680744d4..288a75e1a762c74662612ccce43ce73d77aebc99 100644 --- a/picarones/fixtures.py +++ b/picarones/fixtures.py @@ -13,19 +13,19 @@ import random import struct import zlib -from picarones.core.metrics import MetricsResult +from picarones.measurements.metrics import MetricsResult from picarones.core.results import BenchmarkResult, DocumentResult, EngineReport from picarones.pipelines.over_normalization import detect_over_normalization # Sprint 5 — métriques avancées -from picarones.core.confusion import build_confusion_matrix -from picarones.core.char_scores import compute_ligature_score, compute_diacritic_score -from picarones.core.taxonomy import classify_errors, aggregate_taxonomy -from picarones.core.structure import analyze_structure, aggregate_structure -from picarones.core.image_quality import generate_mock_quality_scores, aggregate_image_quality -from picarones.core.char_scores import aggregate_ligature_scores, aggregate_diacritic_scores +from picarones.measurements.confusion import build_confusion_matrix +from picarones.measurements.char_scores import compute_ligature_score, compute_diacritic_score +from picarones.measurements.taxonomy import classify_errors, aggregate_taxonomy +from picarones.measurements.structure import analyze_structure, aggregate_structure +from picarones.measurements.image_quality import generate_mock_quality_scores, aggregate_image_quality +from picarones.measurements.char_scores import aggregate_ligature_scores, aggregate_diacritic_scores # Sprint 10 — distribution des erreurs + hallucinations VLM -from picarones.core.line_metrics import compute_line_metrics, aggregate_line_metrics, LineMetrics -from picarones.core.hallucination import compute_hallucination_metrics, aggregate_hallucination_metrics +from picarones.measurements.line_metrics import compute_line_metrics, aggregate_line_metrics, LineMetrics +from picarones.measurements.hallucination import compute_hallucination_metrics, aggregate_hallucination_metrics # --------------------------------------------------------------------------- # Textes GT réalistes (documents patrimoniaux) @@ -240,7 +240,7 @@ def _png_to_data_uri(png_bytes: bytes) -> str: # --------------------------------------------------------------------------- def _make_metrics(reference: str, hypothesis: str) -> MetricsResult: - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics return compute_metrics(reference, hypothesis) @@ -427,11 +427,11 @@ def generate_sample_benchmark( } # Agrégation Sprint 5 - from picarones.core.confusion import aggregate_confusion_matrices, ConfusionMatrix - from picarones.core.char_scores import LigatureScore, DiacriticScore - from picarones.core.taxonomy import TaxonomyResult - from picarones.core.structure import StructureResult - from picarones.core.image_quality import ImageQualityResult + from picarones.measurements.confusion import aggregate_confusion_matrices, ConfusionMatrix + from picarones.measurements.char_scores import LigatureScore, DiacriticScore + from picarones.measurements.taxonomy import TaxonomyResult + from picarones.measurements.structure import StructureResult + from picarones.measurements.image_quality import ImageQualityResult agg_confusion = aggregate_confusion_matrices([ ConfusionMatrix(**dr.confusion_matrix) @@ -468,7 +468,7 @@ def generate_sample_benchmark( LineMetrics.from_dict(dr.line_metrics) for dr in doc_results if dr.line_metrics ]) - from picarones.core.hallucination import HallucinationMetrics as _HM + from picarones.measurements.hallucination import HallucinationMetrics as _HM agg_hallucination = aggregate_hallucination_metrics([ _HM.from_dict(dr.hallucination_metrics) for dr in doc_results if dr.hallucination_metrics diff --git a/picarones/core/alto_metrics.py b/picarones/measurements/alto_metrics.py similarity index 99% rename from picarones/core/alto_metrics.py rename to picarones/measurements/alto_metrics.py index a72f8b42614dcacaab09ceb62f43fafee0ed05dd..cf6420a6902a192fd723211d1d9600b2bbe40a89 100644 --- a/picarones/core/alto_metrics.py +++ b/picarones/measurements/alto_metrics.py @@ -17,7 +17,7 @@ enregistre quatre métriques natives (``alto_text_cer``, les opérateurs jiwer historiques sur le texte extrait des deux côtés. L'approche est strictement additive vis-à-vis de -:mod:`picarones.core.metrics` : ce module ne touche pas le chemin de +:mod:`picarones.measurements.metrics` : ce module ne touche pas le chemin de calcul historique (``compute_metrics``), il enrichit uniquement le registre typé pour les pipelines composées. diff --git a/picarones/measurements/builtin_hooks.py b/picarones/measurements/builtin_hooks.py index 0b3888966a8890f9cf4aeaeea80ee74dbc9631a4..db3b42d0ff725bc9a966260f4bf10940cc2cc1a2 100644 --- a/picarones/measurements/builtin_hooks.py +++ b/picarones/measurements/builtin_hooks.py @@ -4,7 +4,7 @@ Chantier 2 du plan d'évolution post-Sprint 97. Ce module **migre** les 12 hooks document-level et 12 agrégateurs corpus-level qui étaient codés en dur dans -``picarones.core.runner._compute_document_result`` et autour de la +``picarones.measurements.runner._compute_document_result`` et autour de la boucle d'agrégation (lignes 794-827 du runner pré-chantier-2). Approche additive — rétrocompat stricte @@ -97,7 +97,7 @@ def calibration_from_engine_result( normalisées à ``[0, 1]``. Les confidences négatives (Tesseract met -1 pour les non-mots) sont ignorées. """ - from picarones.core.calibration import compute_calibration_metrics + from picarones.measurements.calibration import compute_calibration_metrics if not token_confidences: return None @@ -146,7 +146,7 @@ def calibration_from_engine_result( requires_success=True, ) def _confusion_hook(*, ground_truth, hypothesis, **_): - from picarones.core.confusion import build_confusion_matrix + from picarones.measurements.confusion import build_confusion_matrix return build_confusion_matrix(ground_truth, hypothesis).as_dict() @@ -157,7 +157,7 @@ def _confusion_hook(*, ground_truth, hypothesis, **_): requires_success=True, ) def _char_scores_hook(*, ground_truth, hypothesis, **_): - from picarones.core.char_scores import ( + from picarones.measurements.char_scores import ( compute_diacritic_score, compute_ligature_score, ) @@ -173,7 +173,7 @@ def _char_scores_hook(*, ground_truth, hypothesis, **_): requires_success=True, ) def _taxonomy_hook(*, ground_truth, hypothesis, **_): - from picarones.core.taxonomy import classify_errors + from picarones.measurements.taxonomy import classify_errors return classify_errors(ground_truth, hypothesis).as_dict() @@ -184,7 +184,7 @@ def _taxonomy_hook(*, ground_truth, hypothesis, **_): requires_success=True, ) def _structure_hook(*, ground_truth, hypothesis, **_): - from picarones.core.structure import analyze_structure + from picarones.measurements.structure import analyze_structure return analyze_structure(ground_truth, hypothesis).as_dict() @@ -195,7 +195,7 @@ def _structure_hook(*, ground_truth, hypothesis, **_): requires_success=True, ) def _line_metrics_hook(*, ground_truth, hypothesis, **_): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics return compute_line_metrics(ground_truth, hypothesis).as_dict() @@ -206,7 +206,7 @@ def _line_metrics_hook(*, ground_truth, hypothesis, **_): requires_success=True, ) def _hallucination_hook(*, ground_truth, hypothesis, **_): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics return compute_hallucination_metrics(ground_truth, hypothesis).as_dict() @@ -230,7 +230,7 @@ def _calibration_hook(*, ground_truth, ocr_result, **_): # résultat OCR (pour comparer un échec OCR à la qualité image). ) def _image_quality_hook(*, image_path, **_): - from picarones.core.image_quality import analyze_image_quality + from picarones.measurements.image_quality import analyze_image_quality iq = analyze_image_quality(image_path) if iq.error is not None: return None @@ -247,7 +247,7 @@ def _image_quality_hook(*, image_path, **_): # — comportement adaptive intact. ) def _philological_hook(*, ground_truth, hypothesis, **_): - from picarones.core.philological_runner import compute_philological_metrics + from picarones.measurements.philological_runner import compute_philological_metrics return compute_philological_metrics(ground_truth, hypothesis) @@ -257,7 +257,7 @@ def _philological_hook(*, ground_truth, hypothesis, **_): profiles=_STANDARD_PROFILES, ) def _searchability_hook(*, ground_truth, hypothesis, **_): - from picarones.core.searchability_runner import compute_searchability_metrics + from picarones.measurements.searchability_runner import compute_searchability_metrics return compute_searchability_metrics(ground_truth, hypothesis) @@ -267,7 +267,7 @@ def _searchability_hook(*, ground_truth, hypothesis, **_): profiles=_STANDARD_PROFILES, ) def _numerical_sequences_hook(*, ground_truth, hypothesis, **_): - from picarones.core.numerical_sequences_runner import ( + from picarones.measurements.numerical_sequences_runner import ( compute_numerical_sequence_metrics_adaptive, ) return compute_numerical_sequence_metrics_adaptive(ground_truth, hypothesis) @@ -279,7 +279,7 @@ def _numerical_sequences_hook(*, ground_truth, hypothesis, **_): profiles=_STANDARD_PROFILES, ) def _readability_hook(*, ground_truth, hypothesis, corpus_lang, **_): - from picarones.core.readability_runner import compute_readability_metrics + from picarones.measurements.readability_runner import compute_readability_metrics return compute_readability_metrics(ground_truth, hypothesis, lang=corpus_lang) @@ -294,7 +294,7 @@ def _readability_hook(*, ground_truth, hypothesis, corpus_lang, **_): profiles=_STANDARD_PROFILES, ) def _aggregate_confusion(doc_results: list) -> Optional[dict]: - from picarones.core.confusion import ( + from picarones.measurements.confusion import ( ConfusionMatrix, aggregate_confusion_matrices, ) try: @@ -321,7 +321,7 @@ def _aggregate_confusion(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_char_scores(doc_results: list) -> Optional[dict]: - from picarones.core.char_scores import ( + from picarones.measurements.char_scores import ( DiacriticScore, LigatureScore, aggregate_diacritic_scores, @@ -351,7 +351,7 @@ def _aggregate_char_scores(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_taxonomy(doc_results: list) -> Optional[dict]: - from picarones.core.taxonomy import TaxonomyResult, aggregate_taxonomy + from picarones.measurements.taxonomy import TaxonomyResult, aggregate_taxonomy results = [ TaxonomyResult.from_dict(dr.taxonomy) for dr in doc_results @@ -368,7 +368,7 @@ def _aggregate_taxonomy(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_structure(doc_results: list) -> Optional[dict]: - from picarones.core.structure import StructureResult, aggregate_structure + from picarones.measurements.structure import StructureResult, aggregate_structure results = [ StructureResult.from_dict(dr.structure) for dr in doc_results @@ -385,7 +385,7 @@ def _aggregate_structure(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_image_quality(doc_results: list) -> Optional[dict]: - from picarones.core.image_quality import ( + from picarones.measurements.image_quality import ( ImageQualityResult, aggregate_image_quality, ) results = [ @@ -404,7 +404,7 @@ def _aggregate_image_quality(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_line_metrics(doc_results: list) -> Optional[dict]: - from picarones.core.line_metrics import ( + from picarones.measurements.line_metrics import ( LineMetrics, aggregate_line_metrics, ) results = [ @@ -423,7 +423,7 @@ def _aggregate_line_metrics(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_hallucination(doc_results: list) -> Optional[dict]: - from picarones.core.hallucination import ( + from picarones.measurements.hallucination import ( HallucinationMetrics, aggregate_hallucination_metrics, ) results = [ @@ -543,7 +543,7 @@ def _aggregate_calibration(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_philological(doc_results: list) -> Optional[dict]: - from picarones.core.philological_runner import aggregate_philological_metrics + from picarones.measurements.philological_runner import aggregate_philological_metrics return aggregate_philological_metrics( [dr.philological_metrics for dr in doc_results], ) @@ -555,7 +555,7 @@ def _aggregate_philological(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_searchability(doc_results: list) -> Optional[dict]: - from picarones.core.searchability_runner import aggregate_searchability_metrics + from picarones.measurements.searchability_runner import aggregate_searchability_metrics return aggregate_searchability_metrics( [dr.searchability_metrics for dr in doc_results], ) @@ -567,7 +567,7 @@ def _aggregate_searchability(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_numerical_sequences(doc_results: list) -> Optional[dict]: - from picarones.core.numerical_sequences_runner import ( + from picarones.measurements.numerical_sequences_runner import ( aggregate_numerical_sequence_metrics, ) return aggregate_numerical_sequence_metrics( @@ -581,7 +581,7 @@ def _aggregate_numerical_sequences(doc_results: list) -> Optional[dict]: profiles=_STANDARD_PROFILES, ) def _aggregate_readability(doc_results: list) -> Optional[dict]: - from picarones.core.readability_runner import aggregate_readability_metrics + from picarones.measurements.readability_runner import aggregate_readability_metrics return aggregate_readability_metrics( [dr.readability_metrics for dr in doc_results], ) diff --git a/picarones/core/builtin_metrics.py b/picarones/measurements/builtin_metrics.py similarity index 100% rename from picarones/core/builtin_metrics.py rename to picarones/measurements/builtin_metrics.py diff --git a/picarones/measurements/cost_projection.py b/picarones/measurements/cost_projection.py index f9eab7a6d47731e7b6b0722cb86ad9b1aae0729b..f0fc9baca084ed9b92697636001dcac84c5254ef 100644 --- a/picarones/measurements/cost_projection.py +++ b/picarones/measurements/cost_projection.py @@ -20,7 +20,7 @@ le chercheur arbitre selon son budget. Dépendance ---------- -S'appuie sur ``picarones.core.pricing`` (Sprint 20) qui expose +S'appuie sur ``picarones.measurements.pricing`` (Sprint 20) qui expose ``EngineCost.cost_per_1k_pages_eur`` et ``co2_per_1k_pages_g``. """ @@ -31,7 +31,7 @@ import logging from dataclasses import dataclass from typing import Optional -from picarones.core.pricing import EngineCost +from picarones.measurements.pricing import EngineCost logger = logging.getLogger(__name__) diff --git a/picarones/measurements/difficulty.py b/picarones/measurements/difficulty.py index 7f037a48d4f67d06e7162473b901fc261681373a..10c5b72a32020f6b843774a68d6f1672e79e8b25 100644 --- a/picarones/measurements/difficulty.py +++ b/picarones/measurements/difficulty.py @@ -192,7 +192,7 @@ def difficulty_label(score: float) -> str: def difficulty_color(score: float) -> str: """Retourne une couleur CSS pour un score de difficulté.""" - from picarones.core.colors import COLOR_GREEN, COLOR_YELLOW, COLOR_ORANGE, COLOR_RED + from picarones.report.colors import COLOR_GREEN, COLOR_YELLOW, COLOR_ORANGE, COLOR_RED if score < 0.25: return COLOR_GREEN if score < 0.50: diff --git a/picarones/measurements/equivalence_profile.py b/picarones/measurements/equivalence_profile.py index e3c9adf1cb24c3e73567733ac16c883ba8396e81..cb8c001f0bb01baf48aac621b482e2cec4872701 100644 --- a/picarones/measurements/equivalence_profile.py +++ b/picarones/measurements/equivalence_profile.py @@ -42,7 +42,7 @@ import logging from dataclasses import dataclass from typing import Iterable, Optional -from picarones.core.normalization import ( +from picarones.measurements.normalization import ( DIPLOMATIC_EN_EARLY_MODERN, DIPLOMATIC_FR_EARLY_MODERN, DIPLOMATIC_LATIN_MEDIEVAL, @@ -178,10 +178,10 @@ def compute_cer_with_equivalences( """Calcule le CER après application des équivalences sélectionnées sur les **deux** côtés (GT et hypothèse). - Utilise ``picarones.core.metrics.compute_metrics`` et extrait + Utilise ``picarones.measurements.metrics.compute_metrics`` et extrait le champ ``cer`` du résultat. """ - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics selected_list = list(selected_names) ref = apply_selected_equivalences(reference or "", selected_list) diff --git a/picarones/measurements/history.py b/picarones/measurements/history.py index 9a360851ae767a99635a18eadae3fa0dcd9fb411..39aa55efed7a827af50c4c08387aebd607d60baf 100644 --- a/picarones/measurements/history.py +++ b/picarones/measurements/history.py @@ -21,7 +21,7 @@ Table ``runs`` : Usage ----- ->>> from picarones.core.history import BenchmarkHistory +>>> from picarones.measurements.history import BenchmarkHistory >>> history = BenchmarkHistory("~/.picarones/history.db") >>> history.record(benchmark_result) >>> df = history.query(engine="tesseract", corpus="chroniques") diff --git a/picarones/measurements/incremental_comparison.py b/picarones/measurements/incremental_comparison.py index 03f3ea0ee8a3b3eb7ed90af59349f49cb95386e1..8dcd0f6d95b85d94472aa99fffab926755e89be3 100644 --- a/picarones/measurements/incremental_comparison.py +++ b/picarones/measurements/incremental_comparison.py @@ -28,7 +28,7 @@ On ne reconstruit pas Friedman/Nemenyi (déjà dans Sprint 18) ; on agrège ici les données nécessaires pour qu'un tests statistique externe puisse les consommer. Le rapport existant reste libre de brancher -``picarones.core.statistics.friedman_test`` sur la sortie de +``picarones.measurements.statistics.friedman_test`` sur la sortie de ce module. Sortie diff --git a/picarones/core/metrics.py b/picarones/measurements/metrics.py similarity index 98% rename from picarones/core/metrics.py rename to picarones/measurements/metrics.py index 1ab3e5355f578ac72049735647931a418bbf12cf..66228fb095d230c74bcf930d99a381b7c1adddb0 100644 --- a/picarones/core/metrics.py +++ b/picarones/measurements/metrics.py @@ -195,7 +195,7 @@ def compute_metrics( cer_diplomatic: Optional[float] = None diplomatic_profile_name: Optional[str] = None try: - from picarones.core.normalization import DEFAULT_DIPLOMATIC_PROFILE + from picarones.measurements.normalization import DEFAULT_DIPLOMATIC_PROFILE profile = normalization_profile or DEFAULT_DIPLOMATIC_PROFILE ref_diplo = profile.normalize(reference) hyp_diplo = profile.normalize(hypothesis) @@ -288,4 +288,4 @@ def aggregate_metrics(results: list[MetricsResult]) -> dict: # Import paresseux pour éviter les imports circulaires from typing import TYPE_CHECKING if TYPE_CHECKING: - from picarones.core.normalization import NormalizationProfile + from picarones.measurements.normalization import NormalizationProfile diff --git a/picarones/measurements/narrative/__init__.py b/picarones/measurements/narrative/__init__.py index 12b7ce32122e4d51748a79c79e99220951cdcc81..54c5500c474cccd71139309260000abf67bda840 100644 --- a/picarones/measurements/narrative/__init__.py +++ b/picarones/measurements/narrative/__init__.py @@ -14,7 +14,7 @@ API publique - ``build_synthesis(data, lang="fr")`` : pipeline complet (Sprint 4) """ -from picarones.measurements.narrative.facts import ( +from picarones.core.facts import ( Fact, FactType, FactImportance, diff --git a/picarones/measurements/narrative/arbiter.py b/picarones/measurements/narrative/arbiter.py index 1b0625d3afcf7e0ff670ea28667344b3d867060e..9a5413005a07e5dbffb8112edfa634162113be33 100644 --- a/picarones/measurements/narrative/arbiter.py +++ b/picarones/measurements/narrative/arbiter.py @@ -21,7 +21,7 @@ from __future__ import annotations from typing import Iterable, Sequence -from picarones.measurements.narrative.facts import Fact, FactImportance, FactType +from picarones.core.facts import Fact, FactImportance, FactType # Ordre canonique des types pour départager les ex-aequo à l'importance égale. diff --git a/picarones/measurements/narrative/detectors/__init__.py b/picarones/measurements/narrative/detectors/__init__.py index cc71abb34614697b6e11e8af7eba2ecd1503b1c1..ed5dda3fc009448ca4eefe7d7e1e9405bf23809f 100644 --- a/picarones/measurements/narrative/detectors/__init__.py +++ b/picarones/measurements/narrative/detectors/__init__.py @@ -69,7 +69,7 @@ from picarones.measurements.narrative.detectors.ensemble import ( # Snapshot du registre + helper d'enregistrement legacy — déplacés # verbatim depuis l'ancien ``detectors.py`` (lignes 1193-1229). -from picarones.measurements.narrative.facts import DetectorFn, FactType +from picarones.core.facts import DetectorFn, FactType from picarones.measurements.narrative.registry import ( iter_detectors as _iter_detectors, populate_legacy_registry as _populate_legacy_registry, diff --git a/picarones/measurements/narrative/detectors/_helpers.py b/picarones/measurements/narrative/detectors/_helpers.py index 6b55645c418a456617b052fb41a1d52d755203fb..a5e05c694972f3a2c6abc444d2e44ba2e011e52f 100644 --- a/picarones/measurements/narrative/detectors/_helpers.py +++ b/picarones/measurements/narrative/detectors/_helpers.py @@ -1,13 +1,4 @@ -"""Helpers internes partagés par les détecteurs narratifs. - -Chantier 5 du plan d'évolution post-Sprint 97 — découpage de -``picarones/core/narrative/detectors.py`` (1229 lignes, 18 détecteurs) -en 6 sous-modules thématiques + ce module d'helpers communs. - -Ces fonctions étaient privées (préfixe ``_``) au module historique. -Elles sont conservées telles quelles ici ; les sous-modules les -importent. -""" +"""Helpers internes partagés par les détecteurs narratifs.""" from __future__ import annotations @@ -34,10 +25,8 @@ def _n_docs(data: dict) -> int: def _mean_duration_per_engine(data: dict) -> dict[str, float]: """Durée moyenne d'exécution par moteur (en secondes par document). - Source primaire : ``benchmark_data["documents"][i]["engine_results"][j]["duration"]`` - (format historique du runner). Fallback secondaire : - ``benchmark_data["engines"][i]["mean_duration"]`` (champ agrégé - quand fourni). Filtre les durées non-numériques. + Lit ``benchmark_data["documents"][i]["engine_results"][j]["duration"]`` + (format runner). Filtre les durées non-numériques. """ durations: dict[str, list[float]] = {} for doc in data.get("documents") or []: @@ -51,21 +40,4 @@ def _mean_duration_per_engine(data: dict) -> dict[str, float]: except (TypeError, ValueError): continue durations.setdefault(engine_name, []).append(d_f) - if durations: - return {k: sum(v) / len(v) for k, v in durations.items() if v} - # Fallback : champ agrégé sur le résumé moteur - out: dict[str, float] = {} - for e in _engines_summary(data): - name = e.get("name") - if not name: - continue - dur = e.get("mean_duration") - if dur is None: - continue - try: - dur_f = float(dur) - except (TypeError, ValueError): - continue - if dur_f > 0: - out[name] = dur_f - return out + return {k: sum(v) / len(v) for k, v in durations.items() if v} diff --git a/picarones/measurements/narrative/detectors/ensemble.py b/picarones/measurements/narrative/detectors/ensemble.py index 2d2f8a9c3c6463dc4c3561b171bf67076e46f0e7..37f1465469f5bc10bf51984988ea192e0b752807 100644 --- a/picarones/measurements/narrative/detectors/ensemble.py +++ b/picarones/measurements/narrative/detectors/ensemble.py @@ -9,7 +9,7 @@ from __future__ import annotations from typing import Optional -from picarones.measurements.narrative.facts import Fact, FactImportance, FactType +from picarones.core.facts import Fact, FactImportance, FactType from picarones.measurements.narrative.registry import register_detector diff --git a/picarones/measurements/narrative/detectors/history.py b/picarones/measurements/narrative/detectors/history.py index 0d08e9b54f927816e898917cf79c500cb0e6d5be..385a99e0b2378a112881f72497c5bde86a84eb71 100644 --- a/picarones/measurements/narrative/detectors/history.py +++ b/picarones/measurements/narrative/detectors/history.py @@ -10,7 +10,7 @@ from __future__ import annotations -from picarones.measurements.narrative.facts import Fact, FactImportance, FactType +from picarones.core.facts import Fact, FactImportance, FactType from picarones.measurements.narrative.registry import register_detector diff --git a/picarones/measurements/narrative/detectors/pareto.py b/picarones/measurements/narrative/detectors/pareto.py index 8735e0536e78562fda1a2bdead2df40e15ddfa31..e5f744ef22f63e21fb9716d1902e19a658f9294c 100644 --- a/picarones/measurements/narrative/detectors/pareto.py +++ b/picarones/measurements/narrative/detectors/pareto.py @@ -11,7 +11,7 @@ from __future__ import annotations import statistics as _stats from typing import Optional -from picarones.measurements.narrative.facts import Fact, FactImportance, FactType +from picarones.core.facts import Fact, FactImportance, FactType from picarones.measurements.narrative.registry import register_detector diff --git a/picarones/measurements/narrative/detectors/quality.py b/picarones/measurements/narrative/detectors/quality.py index e3180e254de8b65b6f34302ba28425b2ac193937..c7e42ff751d26183d0fe39321a055073405089ed 100644 --- a/picarones/measurements/narrative/detectors/quality.py +++ b/picarones/measurements/narrative/detectors/quality.py @@ -12,7 +12,7 @@ from __future__ import annotations import statistics as _stats -from picarones.measurements.narrative.facts import Fact, FactImportance, FactType +from picarones.core.facts import Fact, FactImportance, FactType from picarones.measurements.narrative.registry import register_detector from picarones.measurements.narrative.detectors._helpers import ( diff --git a/picarones/measurements/narrative/detectors/ranking.py b/picarones/measurements/narrative/detectors/ranking.py index b747b51401bb038cb6b0b60af12058ea3843f31a..4c52b599ab57e6d45b84713f6f19d98b6d47cf3f 100644 --- a/picarones/measurements/narrative/detectors/ranking.py +++ b/picarones/measurements/narrative/detectors/ranking.py @@ -16,7 +16,7 @@ from __future__ import annotations import statistics as _stats -from picarones.measurements.narrative.facts import Fact, FactImportance, FactType +from picarones.core.facts import Fact, FactImportance, FactType from picarones.measurements.narrative.registry import register_detector from picarones.measurements.narrative.detectors._helpers import ( diff --git a/picarones/measurements/narrative/detectors/stratum.py b/picarones/measurements/narrative/detectors/stratum.py index d58eae9119ed4ea47aeb73a197fb9bed41e2beb9..5db5b795b78fe53b6e557cc0444fde89d784d429 100644 --- a/picarones/measurements/narrative/detectors/stratum.py +++ b/picarones/measurements/narrative/detectors/stratum.py @@ -11,7 +11,7 @@ from __future__ import annotations -from picarones.measurements.narrative.facts import Fact, FactImportance, FactType +from picarones.core.facts import Fact, FactImportance, FactType from picarones.measurements.narrative.registry import register_detector from picarones.measurements.narrative.detectors._helpers import ( diff --git a/picarones/measurements/narrative/registry.py b/picarones/measurements/narrative/registry.py index 511ed2ed360a178b407183c05ab6ca7adf46dd8b..9415dc6a3cabb24b4acfae8b250811a378ef2227 100644 --- a/picarones/measurements/narrative/registry.py +++ b/picarones/measurements/narrative/registry.py @@ -51,7 +51,7 @@ import threading from dataclasses import dataclass from typing import Callable, Optional -from picarones.measurements.narrative.facts import ( +from picarones.core.facts import ( DetectorFn, DetectorRegistry, FactImportance, diff --git a/picarones/measurements/narrative/renderer.py b/picarones/measurements/narrative/renderer.py index cbb5d3da3f91db658339c78eb6be946177dfb0f9..c710e8687b5c598d4a33cd8796f63bce8618c3a4 100644 --- a/picarones/measurements/narrative/renderer.py +++ b/picarones/measurements/narrative/renderer.py @@ -15,7 +15,7 @@ from typing import Iterable import yaml -from picarones.measurements.narrative.facts import Fact +from picarones.core.facts import Fact logger = logging.getLogger(__name__) diff --git a/picarones/measurements/numerical_sequences.py b/picarones/measurements/numerical_sequences.py index 5698b4017fa693cec17a6dd1671bfed7d1cab38c..2e99e8f6de60f45bd0bbb60b6e659c5af01b3eff 100644 --- a/picarones/measurements/numerical_sequences.py +++ b/picarones/measurements/numerical_sequences.py @@ -17,7 +17,7 @@ Catégories couvertes (le module détecte les **années** sur 4 chiffres dans la plage [1000-2099]). 2. **Numéraux romains** : ``MDCLXVIII``, ``XIV``, ``Tome IV``. - Réutilise ``picarones.core.roman_numerals`` (Sprint 60). + Réutilise ``picarones.measurements.roman_numerals`` (Sprint 60). 3. **Foliotation** : ``f. 12``, ``f. 12r``, ``fol. 24v``, ``p. 5``, ``pp. 12-15``, ``n° 42``. 4. **Montants** : ``12 livres``, ``5 sols``, ``8 deniers``, @@ -86,7 +86,7 @@ from typing import Optional from picarones.core.metric_registry import register_metric from picarones.core.modules import ArtifactType -from picarones.core.roman_numerals import ( +from picarones.measurements.roman_numerals import ( detect_roman_numerals, roman_to_int, ) diff --git a/picarones/measurements/numerical_sequences_runner.py b/picarones/measurements/numerical_sequences_runner.py index c1d6d1f429e85c11efa6a2a5a6db632c0566c976..405f68109ce3486a82a9d21849ac1426a2b2b82c 100644 --- a/picarones/measurements/numerical_sequences_runner.py +++ b/picarones/measurements/numerical_sequences_runner.py @@ -18,7 +18,7 @@ from __future__ import annotations import logging from typing import Iterable, Optional -from picarones.core.numerical_sequences import ( +from picarones.measurements.numerical_sequences import ( CATEGORIES, compute_numerical_sequence_metrics, ) diff --git a/picarones/core/pipeline_benchmark.py b/picarones/measurements/pipeline_benchmark.py similarity index 99% rename from picarones/core/pipeline_benchmark.py rename to picarones/measurements/pipeline_benchmark.py index 40f3afc9b46ff598e8788e6a1c1ba7001c071c02..ef597c329b1a4ccca09be799507db22199445695 100644 --- a/picarones/core/pipeline_benchmark.py +++ b/picarones/measurements/pipeline_benchmark.py @@ -47,7 +47,7 @@ from typing import Any, Callable, Optional from picarones.core.corpus import Corpus, Document from picarones.core.modules import ArtifactType -from picarones.core.pipeline_runner import ( +from picarones.core.pipeline import ( PipelineResult, PipelineRunner, PipelineSpec, diff --git a/picarones/core/pipeline_comparison.py b/picarones/measurements/pipeline_comparison.py similarity index 99% rename from picarones/core/pipeline_comparison.py rename to picarones/measurements/pipeline_comparison.py index bf7f775bda2e1707d488c43e629b3adcd0b86b9e..5d960c9516c0a3dfd04da762568f68e948270b58 100644 --- a/picarones/core/pipeline_comparison.py +++ b/picarones/measurements/pipeline_comparison.py @@ -52,13 +52,13 @@ from typing import Optional from picarones.core.corpus import Corpus from picarones.core.modules import ArtifactType -from picarones.core.pipeline_benchmark import ( +from picarones.measurements.pipeline_benchmark import ( InitialInputsFactory, PipelineBenchmarkResult, default_initial_inputs, run_pipeline_benchmark, ) -from picarones.core.pipeline_runner import PipelineSpec +from picarones.core.pipeline import PipelineSpec logger = logging.getLogger(__name__) diff --git a/picarones/core/pipeline_spec_loader.py b/picarones/measurements/pipeline_spec_loader.py similarity index 99% rename from picarones/core/pipeline_spec_loader.py rename to picarones/measurements/pipeline_spec_loader.py index 0166b0e99ba128379c7fbf29ade75a4c1c390abd..9b76674f65a4352c76592af1790b2b3edbd43d22 100644 --- a/picarones/core/pipeline_spec_loader.py +++ b/picarones/measurements/pipeline_spec_loader.py @@ -68,7 +68,7 @@ from pathlib import Path from typing import Any from picarones.core.modules import ArtifactType, BaseModule -from picarones.core.pipeline_runner import PipelineSpec, PipelineStep +from picarones.core.pipeline import PipelineSpec, PipelineStep logger = logging.getLogger(__name__) diff --git a/picarones/measurements/readability_runner.py b/picarones/measurements/readability_runner.py index 073b5f16fca2a59993e08453c191f915d8f75405..2ad1a0129fb672457f3de6a8c976ae55b7d612ed 100644 --- a/picarones/measurements/readability_runner.py +++ b/picarones/measurements/readability_runner.py @@ -33,7 +33,7 @@ import logging import statistics from typing import Iterable, Optional -from picarones.core.readability import ( +from picarones.measurements.readability import ( Language, count_words, flesch_delta, diff --git a/picarones/measurements/reliability.py b/picarones/measurements/reliability.py index 116bdc28d8312cc1a702d6d0caf156d2db78e0b9..8a179ffe604fa7a4f0eb5d8483edcacaa14a4773 100644 --- a/picarones/measurements/reliability.py +++ b/picarones/measurements/reliability.py @@ -333,7 +333,7 @@ def compute_multirun_stability( cer_stdev: Optional[float] = None cer_cv: Optional[float] = None if reference is not None: - from picarones.core.metrics import _cer_from_strings + from picarones.measurements.metrics import _cer_from_strings cer_per_run = [_cer_from_strings(reference, r) for r in runs_list] cer_per_run = [v for v in cer_per_run if v is not None] if cer_per_run: diff --git a/picarones/measurements/robustness.py b/picarones/measurements/robustness.py index 27f407234709718dab9752114a00250c0fc2db60..bb455d6dbe303bf3c8e8102c05444ce7dd7e90f1 100644 --- a/picarones/measurements/robustness.py +++ b/picarones/measurements/robustness.py @@ -15,7 +15,7 @@ Fonctionnement Usage ----- ->>> from picarones.core.robustness import RobustnessAnalyzer +>>> from picarones.measurements.robustness import RobustnessAnalyzer >>> analyzer = RobustnessAnalyzer(engine, degradation_types=["noise", "blur"]) >>> report = analyzer.analyze(corpus) >>> print(report.critical_thresholds) @@ -420,7 +420,7 @@ class RobustnessAnalyzer: Examples -------- >>> from picarones.engines.tesseract import TesseractEngine - >>> from picarones.core.robustness import RobustnessAnalyzer + >>> from picarones.measurements.robustness import RobustnessAnalyzer >>> engine = TesseractEngine(config={"lang": "fra"}) >>> analyzer = RobustnessAnalyzer([engine], degradation_types=["noise", "blur"]) >>> report = analyzer.analyze(corpus) @@ -463,7 +463,7 @@ class RobustnessAnalyzer: ------- RobustnessReport """ - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics docs = corpus.documents[:max_docs] curves: list[DegradationCurve] = [] diff --git a/picarones/core/runner.py b/picarones/measurements/runner.py similarity index 95% rename from picarones/core/runner.py rename to picarones/measurements/runner.py index 9caca766da2bb3894ef099497768b5e578c2726f..2f89a9f9327ef4e14c8eadae6898b57f500c3661 100644 --- a/picarones/core/runner.py +++ b/picarones/measurements/runner.py @@ -24,7 +24,7 @@ from typing import Optional from tqdm import tqdm from picarones.core.corpus import Corpus -from picarones.core.metrics import MetricsResult, compute_metrics +from picarones.measurements.metrics import MetricsResult, compute_metrics from picarones.core.results import BenchmarkResult, DocumentResult, EngineReport from picarones.engines.base import BaseOCREngine, EngineResult @@ -126,19 +126,19 @@ def _io_doc_worker( # Chantier 2 (post-Sprint 97) — la logique du helper calibration vit -# désormais dans :mod:`picarones.core.builtin_hooks`. Ce nom reste exposé +# désormais dans :mod:`picarones.measurements.builtin_hooks`. Ce nom reste exposé # ici pour la rétrocompat des tests Sprint 42 qui font -# ``from picarones.core.runner import _calibration_from_engine_result``. +# ``from picarones.measurements.runner import _calibration_from_engine_result``. def _calibration_from_engine_result( ground_truth: str, token_confidences: list, ) -> Optional[dict]: - """Délégation vers :func:`picarones.core.builtin_hooks.calibration_from_engine_result`. + """Délégation vers :func:`picarones.measurements.builtin_hooks.calibration_from_engine_result`. Conservé pour la rétrocompat des tests existants ; toute évolution du calcul doit se faire dans ``builtin_hooks``. """ - from picarones.core.builtin_hooks import calibration_from_engine_result + from picarones.measurements.builtin_hooks import calibration_from_engine_result return calibration_from_engine_result(ground_truth, token_confidences) @@ -162,7 +162,7 @@ def _compute_document_result( Chantier 2 (post-Sprint 97) — refonte ------------------------------------ Les 11 ``try/except`` codés en dur (Sprints 5+10+39+42+61+86+87) sont - désormais centralisés dans ``picarones.core.builtin_hooks`` et + désormais centralisés dans ``picarones.measurements.builtin_hooks`` et sélectionnés via ``run_document_hooks(profile)``. Le profil ``"standard"`` (défaut) reproduit strictement le comportement pré-chantier-2. Les profils ``"minimal"``, ``"philological"``, @@ -175,7 +175,7 @@ def _compute_document_result( # Eager-load des hooks natifs pour peupler le registre dans les # sous-processus du pool (le top-level ``import`` du runner ne le fait # pas pour ne pas pénaliser le démarrage des moteurs minimaux). - import picarones.core.builtin_hooks # noqa: F401 + import picarones.measurements.builtin_hooks # noqa: F401 from picarones.core.metric_hooks import run_document_hooks if ocr_result.success: @@ -476,7 +476,7 @@ def run_benchmark( # aux pools. Eager-load des hooks natifs pour peupler le registre # dans le main process (les sous-processus du pool feront leur # propre import dans ``_compute_document_result``). - import picarones.core.builtin_hooks # noqa: F401 + import picarones.measurements.builtin_hooks # noqa: F401 from picarones.core.metric_hooks import ( run_corpus_aggregators, validate_profile, ) @@ -754,7 +754,7 @@ def run_benchmark( inter_engine_payload: Optional[dict] = None if len(engine_reports) >= 2: try: - from picarones.core.inter_engine import compute_inter_engine_analysis + from picarones.measurements.inter_engine import compute_inter_engine_analysis taxonomy_distros = { report.engine_name: ( @@ -842,50 +842,50 @@ def _build_pipeline_info(engine: BaseOCREngine, doc_results: list[DocumentResult # Helpers d'agrégation — délégations rétrocompat # --------------------------------------------------------------------------- # Chantier 2 (post-Sprint 97) : les implémentations vivent désormais dans -# :mod:`picarones.core.builtin_hooks` (single source of truth, exposé via +# :mod:`picarones.measurements.builtin_hooks` (single source of truth, exposé via # le registre :mod:`picarones.core.metric_hooks`). Les noms ci-dessous -# restent disponibles depuis ``picarones.core.runner`` pour la rétrocompat +# restent disponibles depuis ``picarones.measurements.runner`` pour la rétrocompat # des tests Sprint 13 / 42 qui les importent directement. def _aggregate_confusion(doc_results: list) -> Optional[dict]: """Délégation vers :func:`builtin_hooks._aggregate_confusion`.""" - from picarones.core.builtin_hooks import _aggregate_confusion as _impl + from picarones.measurements.builtin_hooks import _aggregate_confusion as _impl return _impl(doc_results) def _aggregate_char_scores(doc_results: list) -> Optional[dict]: """Délégation vers :func:`builtin_hooks._aggregate_char_scores`.""" - from picarones.core.builtin_hooks import _aggregate_char_scores as _impl + from picarones.measurements.builtin_hooks import _aggregate_char_scores as _impl return _impl(doc_results) def _aggregate_taxonomy(doc_results: list) -> Optional[dict]: """Délégation vers :func:`builtin_hooks._aggregate_taxonomy`.""" - from picarones.core.builtin_hooks import _aggregate_taxonomy as _impl + from picarones.measurements.builtin_hooks import _aggregate_taxonomy as _impl return _impl(doc_results) def _aggregate_structure(doc_results: list) -> Optional[dict]: """Délégation vers :func:`builtin_hooks._aggregate_structure`.""" - from picarones.core.builtin_hooks import _aggregate_structure as _impl + from picarones.measurements.builtin_hooks import _aggregate_structure as _impl return _impl(doc_results) def _aggregate_image_quality(doc_results: list) -> Optional[dict]: """Délégation vers :func:`builtin_hooks._aggregate_image_quality`.""" - from picarones.core.builtin_hooks import _aggregate_image_quality as _impl + from picarones.measurements.builtin_hooks import _aggregate_image_quality as _impl return _impl(doc_results) def _aggregate_line_metrics(doc_results: list) -> Optional[dict]: """Délégation vers :func:`builtin_hooks._aggregate_line_metrics`.""" - from picarones.core.builtin_hooks import _aggregate_line_metrics as _impl + from picarones.measurements.builtin_hooks import _aggregate_line_metrics as _impl return _impl(doc_results) def _aggregate_hallucination(doc_results: list) -> Optional[dict]: """Délégation vers :func:`builtin_hooks._aggregate_hallucination`.""" - from picarones.core.builtin_hooks import _aggregate_hallucination as _impl + from picarones.measurements.builtin_hooks import _aggregate_hallucination as _impl return _impl(doc_results) @@ -909,7 +909,7 @@ def _attach_ner_metrics( """ try: from picarones.core.corpus import GTLevel - from picarones.core.ner import compute_ner_metrics + from picarones.measurements.ner import compute_ner_metrics except ImportError as exc: logger.warning("[ner.attach] imports indisponibles : %s", exc) return @@ -942,11 +942,11 @@ def _aggregate_calibration(doc_results: list) -> Optional[dict]: """Délégation vers :func:`builtin_hooks._aggregate_calibration`. Conservé pour la rétrocompat du test ``test_sprint42_calibration_runner`` - qui importe directement depuis ``picarones.core.runner``. La logique - réelle vit dans :mod:`picarones.core.builtin_hooks` (chantier 2 + qui importe directement depuis ``picarones.measurements.runner``. La logique + réelle vit dans :mod:`picarones.measurements.builtin_hooks` (chantier 2 post-Sprint 97). """ - from picarones.core.builtin_hooks import _aggregate_calibration as _impl + from picarones.measurements.builtin_hooks import _aggregate_calibration as _impl return _impl(doc_results) diff --git a/picarones/measurements/searchability_runner.py b/picarones/measurements/searchability_runner.py index cf822338cd0fb86c4a58301f136568a77080a346..d4bd7d82340c4655dc58f7a27da2baa6ad5d1cd8 100644 --- a/picarones/measurements/searchability_runner.py +++ b/picarones/measurements/searchability_runner.py @@ -18,7 +18,7 @@ from __future__ import annotations import logging from typing import Iterable, Optional -from picarones.core.searchability import ( +from picarones.measurements.searchability import ( _split_words, compute_searchability, ) diff --git a/picarones/measurements/specialization.py b/picarones/measurements/specialization.py index a3f251c56c578701544d9b57aea1f9d21554f033..27e00a834594b081ccb770e0e068306f10d97e8d 100644 --- a/picarones/measurements/specialization.py +++ b/picarones/measurements/specialization.py @@ -32,7 +32,7 @@ intuitive : Dépendances ----------- -S'appuie strictement sur ``picarones.core.inter_engine`` (Sprint +S'appuie strictement sur ``picarones.measurements.inter_engine`` (Sprint 35) — pas de double calcul, pas de logique nouvelle de divergence. """ @@ -42,7 +42,7 @@ from __future__ import annotations import logging from typing import Optional -from picarones.core.inter_engine import jensen_shannon_divergence +from picarones.measurements.inter_engine import jensen_shannon_divergence logger = logging.getLogger(__name__) diff --git a/picarones/measurements/taxonomy.py b/picarones/measurements/taxonomy.py index a8d36076528d81c781e1ccbf4dd18c9341032237..fad325415384b32804931deff59ac3c8270d5eaa 100644 --- a/picarones/measurements/taxonomy.py +++ b/picarones/measurements/taxonomy.py @@ -53,7 +53,7 @@ for _a, _b in _VISUAL_PAIRS: VISUAL_CONFUSIONS[frozenset({_a, _b})] = f"{_a}/{_b}" #: Couples de ligatures pour la détection des erreurs de ligatures -from picarones.core.char_scores import LIGATURE_TABLE, DIACRITIC_MAP # noqa: E402 +from picarones.measurements.char_scores import LIGATURE_TABLE, DIACRITIC_MAP # noqa: E402 # Caractères hors-ASCII présumés hors-vocabulaire (alphabet non latin de base) _LATIN_BASIC = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" diff --git a/picarones/modules/alto_text_to_mono_region.py b/picarones/modules/alto_text_to_mono_region.py index f599cdd2018a42db7cbbbf885956b6f5a5589f39..e49d2cc2bda1282ea5879fc6b145ac0c723455e2 100644 --- a/picarones/modules/alto_text_to_mono_region.py +++ b/picarones/modules/alto_text_to_mono_region.py @@ -21,8 +21,8 @@ Ce reconstructeur est volontairement **primitif** : Cette baseline n'a pas vocation à être un bon reconstructeur — elle a vocation à être un **point de comparaison stable**. Un VLM produisant un ALTO doit faire mieux qu'elle ; c'est mesurable via Layout F1 -(:mod:`picarones.core.layout`) et via les métriques -``alto_text_cer``/``alto_text_wer`` (:mod:`picarones.core.alto_metrics`). +(:mod:`picarones.measurements.layout`) et via les métriques +``alto_text_cer``/``alto_text_wer`` (:mod:`picarones.measurements.alto_metrics`). Conformité ALTO 4.2 ------------------- diff --git a/picarones/report/baseline_render.py b/picarones/report/baseline_render.py index 19d7adda8b93da17ce240b2d9a6a068ca68171f1..abe5fe44f734cded914c8e677232f5fa06123663 100644 --- a/picarones/report/baseline_render.py +++ b/picarones/report/baseline_render.py @@ -161,7 +161,7 @@ def build_corpus_difficulty_baseline_html( ---------- percentile_data: Sortie de - ``picarones.core.baseline_comparison.compute_corpus_difficulty_percentile``. + ``picarones.measurements.baseline_comparison.compute_corpus_difficulty_percentile``. Si ``None``, retourne ``""`` (rapport adaptatif — historique trop court ou difficulté absente). historical_values: diff --git a/picarones/core/colors.py b/picarones/report/colors.py similarity index 100% rename from picarones/core/colors.py rename to picarones/report/colors.py diff --git a/picarones/report/comparison.py b/picarones/report/comparison.py index 9dcb22ca61eaad07e9b7be6bce8e47f75f13536f..a89f02137eab1c5fb6d739489783069e02e1087f 100644 --- a/picarones/report/comparison.py +++ b/picarones/report/comparison.py @@ -1,7 +1,7 @@ """Comparaison de deux runs de benchmark (Sprint 28). Le Sprint 8 a livré la persistance longitudinale via SQLite -(``picarones.core.history``) et un détecteur de régression CLI. Mais +(``picarones.measurements.history``) et un détecteur de régression CLI. Mais aucun outil n'exposait la **comparaison** de deux runs côté rapport : un chercheur qui itère sur 8 prompts ne pouvait pas voir d'un coup *« Tesseract → GPT-4o version V2 a régressé de 0,8 pp en CER moyen diff --git a/picarones/report/error_absorption_render.py b/picarones/report/error_absorption_render.py index 838d5528453c381b4321a3bf9e8128813e5e29f0..4280a21d207f13387abcd55e2828fee6f6b04529 100644 --- a/picarones/report/error_absorption_render.py +++ b/picarones/report/error_absorption_render.py @@ -25,7 +25,7 @@ l'utilisateur depuis son benchmark de pipeline composée : .. code-block:: python - from picarones.core.error_absorption import ( + from picarones.measurements.error_absorption import ( compute_error_absorption, aggregate_error_absorption, ) from picarones.report.error_absorption_render import ( diff --git a/picarones/report/generator.py b/picarones/report/generator.py index 4175a8f329de4b47ee567c1cc2e13ff9af58bb60..a5ef37d68728568854e1567184ae5a59551019ac 100644 --- a/picarones/report/generator.py +++ b/picarones/report/generator.py @@ -37,7 +37,7 @@ def _load_vendor_js(name: str) -> str: from picarones.core.results import BenchmarkResult from picarones.report.diff_utils import compute_char_diff, compute_word_diff -from picarones.core.statistics import ( +from picarones.measurements.statistics import ( compute_pairwise_stats, compute_reliability_curve, compute_correlation_matrix, @@ -49,8 +49,8 @@ from picarones.core.statistics import ( build_critical_difference_svg, compute_pareto_front, ) -from picarones.core.pricing import build_costs_for_benchmark, load_pricing_database -from picarones.core.difficulty import compute_all_difficulties, difficulty_label +from picarones.measurements.pricing import build_costs_for_benchmark, load_pricing_database +from picarones.measurements.difficulty import compute_all_difficulties, difficulty_label # --------------------------------------------------------------------------- @@ -103,7 +103,7 @@ def _encode_images_b64_from_result(benchmark: "BenchmarkResult", max_width: int def _cer_color(cer: float) -> str: """Retourne une couleur CSS pour un score CER donné (0→vert, 1→rouge).""" - from picarones.core.colors import COLOR_GREEN, COLOR_YELLOW, COLOR_ORANGE, COLOR_RED + from picarones.report.colors import COLOR_GREEN, COLOR_YELLOW, COLOR_ORANGE, COLOR_RED if cer < 0.05: return COLOR_GREEN if cer < 0.15: @@ -114,7 +114,7 @@ def _cer_color(cer: float) -> str: def _cer_bg(cer: float) -> str: - from picarones.core.colors import BG_GREEN, BG_YELLOW, BG_ORANGE, BG_RED + from picarones.report.colors import BG_GREEN, BG_YELLOW, BG_ORANGE, BG_RED if cer < 0.05: return BG_GREEN if cer < 0.15: @@ -718,7 +718,7 @@ class ReportGenerator: ) # Sprint 18 — synthèse factuelle narrative (déterministe, sans LLM) - from picarones.core.narrative import build_synthesis + from picarones.measurements.narrative import build_synthesis synthesis = build_synthesis(report_data, lang=self.lang) # Sprint 20 — glossaire contextuel chargé depuis YAML @@ -908,7 +908,7 @@ class ReportGenerator: data = _json.loads(Path(json_path).read_text(encoding="utf-8")) # Reconstruction minimale d'un BenchmarkResult depuis le dict - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult from picarones.core.results import DocumentResult, EngineReport engine_reports = [] diff --git a/picarones/report/incremental_comparison_render.py b/picarones/report/incremental_comparison_render.py index 841e07a084d8957704c6c35eefc5e6c58b0148cc..2c179e898d04e63938ffce9309386a21eb5b7549 100644 --- a/picarones/report/incremental_comparison_render.py +++ b/picarones/report/incremental_comparison_render.py @@ -19,7 +19,7 @@ Module pur — l'utilisateur compose : .. code-block:: python - from picarones.core.incremental_comparison import ( + from picarones.measurements.incremental_comparison import ( PipelineRun, compare_isolated_effect, ) from picarones.report.incremental_comparison_render import ( diff --git a/picarones/report/longitudinal_render.py b/picarones/report/longitudinal_render.py index 02a4abcea88dbf71ba93113cb85a56094f7187c1..b49fc375a44213db34bd9bc296b21325e9fbd26a 100644 --- a/picarones/report/longitudinal_render.py +++ b/picarones/report/longitudinal_render.py @@ -18,8 +18,8 @@ Module pur — l'utilisateur compose : .. code-block:: python - from picarones.core.history import BenchmarkHistory - from picarones.core.longitudinal import compute_corpus_longitudinal + from picarones.measurements.history import BenchmarkHistory + from picarones.measurements.longitudinal import compute_corpus_longitudinal from picarones.report.longitudinal_render import build_longitudinal_html hist = BenchmarkHistory(db_path) diff --git a/picarones/report/multirun_stability_render.py b/picarones/report/multirun_stability_render.py index dc996af1f5b263fb7101918e0485cee503881671..e0344fb0637553c15a898664beada886491fd572 100644 --- a/picarones/report/multirun_stability_render.py +++ b/picarones/report/multirun_stability_render.py @@ -15,7 +15,7 @@ l'utilisateur compose : .. code-block:: python - from picarones.core.reliability import compute_multirun_stability + from picarones.measurements.reliability import compute_multirun_stability from picarones.report.multirun_stability_render import ( build_multirun_stability_html, ) diff --git a/picarones/report/numerical_sequences_render.py b/picarones/report/numerical_sequences_render.py index ddc3592ea22a1b86327b9aced98c1055ff35b174..cab6883556abf18855afe0cdb8e26bf7e8d7fddd 100644 --- a/picarones/report/numerical_sequences_render.py +++ b/picarones/report/numerical_sequences_render.py @@ -23,7 +23,7 @@ from __future__ import annotations from html import escape as _e from typing import Optional -from picarones.core.numerical_sequences import CATEGORIES +from picarones.measurements.numerical_sequences import CATEGORIES def _color_for_score(score: float) -> str: diff --git a/picarones/report/pipeline_render.py b/picarones/report/pipeline_render.py index d2bccc66052ad883c08527463733b57a9397652f..723b477c53345b7cfc199b80cb5ac7a326f637b5 100644 --- a/picarones/report/pipeline_render.py +++ b/picarones/report/pipeline_render.py @@ -48,8 +48,8 @@ from html import escape as _e from typing import Optional from picarones.core.modules import ArtifactType -from picarones.core.pipeline_benchmark import PipelineBenchmarkResult -from picarones.core.pipeline_comparison import PipelineComparisonResult +from picarones.measurements.pipeline_benchmark import PipelineBenchmarkResult +from picarones.measurements.pipeline_comparison import PipelineComparisonResult # ────────────────────────────────────────────────────────────────────────── diff --git a/picarones/report/robustness_projection_render.py b/picarones/report/robustness_projection_render.py index 65febef413c3ef172860dd8666d2b0237fd604cc..5c700b84eebd34d6471c2d50a1c557a8a9eafb79 100644 --- a/picarones/report/robustness_projection_render.py +++ b/picarones/report/robustness_projection_render.py @@ -7,15 +7,15 @@ side, pas de JS, anti-injection systématique. Note d'intégration ------------------ -La robustesse synthétique (``picarones.core.robustness``) est +La robustesse synthétique (``picarones.measurements.robustness``) est exécutée par la CLI ``picarones robustness`` indépendamment du benchmark principal. Pour produire la vue de projection, l'utilisateur compose : .. code-block:: python - from picarones.core.robustness import analyze_robustness - from picarones.core.robustness_projection import ( + from picarones.measurements.robustness import analyze_robustness + from picarones.measurements.robustness_projection import ( project_robustness_on_corpus, aggregate_projection_per_engine, ) @@ -219,7 +219,7 @@ def build_robustness_projection_html( if not projection: return "" if aggregated is None: - from picarones.core.robustness_projection import ( + from picarones.measurements.robustness_projection import ( aggregate_projection_per_engine, ) aggregated = aggregate_projection_per_engine(projection) diff --git a/picarones/report/snapshot.py b/picarones/report/snapshot.py index bcba8fb721a6a564bae23b8fb280076872e1e534..e878eac9bc4226c1a796f378c19fefba3ede4159 100644 --- a/picarones/report/snapshot.py +++ b/picarones/report/snapshot.py @@ -56,11 +56,11 @@ def pricing_snapshot(pricing_path: Optional[Path] = None) -> dict[str, Any]: """Retourne le YAML brut + dict parsé de la table de prix utilisée. Si ``pricing_path`` n'est pas fourni, utilise le chemin par défaut - de ``picarones.core.pricing._DEFAULT_PRICING_PATH``. + de ``picarones.measurements.pricing._DEFAULT_PRICING_PATH``. """ if pricing_path is None: try: - from picarones.core.pricing import _DEFAULT_PRICING_PATH + from picarones.measurements.pricing import _DEFAULT_PRICING_PATH pricing_path = _DEFAULT_PRICING_PATH except ImportError: return {"available": False, "reason": "module pricing introuvable"} diff --git a/picarones/report/specialization_render.py b/picarones/report/specialization_render.py index 326d18ca337e86b2ef58356b2fe2bd167f7a2e74..976d6bc5cd05bc34ee2a37d3f56b5fc2b8638089 100644 --- a/picarones/report/specialization_render.py +++ b/picarones/report/specialization_render.py @@ -14,7 +14,7 @@ from __future__ import annotations from html import escape as _e from typing import Optional -from picarones.core.specialization import ( +from picarones.measurements.specialization import ( compute_specialization_matrix, top_specialized_pairs, ) diff --git a/picarones/report/throughput_render.py b/picarones/report/throughput_render.py index 3c3099ae77b9e8437517cfb36f84122c2e25e054..d7bdb7cde72f63ce0a2b064be6474ded8bcfc47f 100644 --- a/picarones/report/throughput_render.py +++ b/picarones/report/throughput_render.py @@ -20,7 +20,7 @@ Cette vue est un **module pur** — l'utilisateur compose : .. code-block:: python - from picarones.core.throughput import ( + from picarones.measurements.throughput import ( aggregate_effective_throughput, ) from picarones.report.throughput_render import ( diff --git a/picarones/report/views/advanced_taxonomy.py b/picarones/report/views/advanced_taxonomy.py index cf479f30c81c92d1675ac471a3962ad38994d372..0f30df95acd7579bf8274085ad5f111a4be1c274 100644 --- a/picarones/report/views/advanced_taxonomy.py +++ b/picarones/report/views/advanced_taxonomy.py @@ -25,11 +25,11 @@ Sources de données automatiques Sources de données opt-in (via ``opts``) ---------------------------------------- - ``opts["cooccurrence"]`` : sortie de - :func:`picarones.core.taxonomy_cooccurrence.compute_taxonomy_cooccurrence`. + :func:`picarones.measurements.taxonomy_cooccurrence.compute_taxonomy_cooccurrence`. - ``opts["intra_doc"]`` : sortie de - :func:`picarones.core.taxonomy_intra_doc.compute_taxonomy_position_heatmap`. + :func:`picarones.measurements.taxonomy_intra_doc.compute_taxonomy_position_heatmap`. - ``opts["lexical_modernization"]`` : sortie de - :func:`picarones.core.lexical_modernization.compute_lexical_modernization` + :func:`picarones.measurements.lexical_modernization.compute_lexical_modernization` agrégée corpus-wide. Ces calculs ne sont pas faits automatiquement par le runner standard @@ -110,15 +110,15 @@ def build_advanced_taxonomy_view_html( Dict i18n complet. cooccurrence: Sortie pré-calculée de - :func:`picarones.core.taxonomy_cooccurrence.compute_taxonomy_cooccurrence`. + :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.core.taxonomy_intra_doc.compute_taxonomy_position_heatmap`. + :func:`picarones.measurements.taxonomy_intra_doc.compute_taxonomy_position_heatmap`. Optionnel. lexical_modernization: Sortie pré-calculée de - :func:`picarones.core.lexical_modernization.aggregate_lexical_modernization`. + :func:`picarones.measurements.lexical_modernization.aggregate_lexical_modernization`. Optionnel. Returns @@ -135,7 +135,7 @@ def build_advanced_taxonomy_view_html( engines_summary = report_data.get("engines") or [] pair = _select_two_engines_for_comparison(engines_summary) if pair is not None: - from picarones.core.taxonomy_comparison import compare_taxonomies + from picarones.measurements.taxonomy_comparison import compare_taxonomies from picarones.report.taxonomy_comparison_render import ( build_taxonomy_comparison_html, ) diff --git a/picarones/report/views/diagnostics.py b/picarones/report/views/diagnostics.py index 0dc47643badbc022e80a8fb66e3e57b3bd7ce49b..6b227e3941495d8dcfccd88b12a0c322f3a09ea0 100644 --- a/picarones/report/views/diagnostics.py +++ b/picarones/report/views/diagnostics.py @@ -21,7 +21,7 @@ résultats »* : Sources de données automatiques ------------------------------- -- *Leviers* : :func:`picarones.core.levers.detect_levers` est appelée +- *Leviers* : :func:`picarones.measurements.levers.detect_levers` est appelée sur ``report_data``. Couvre : ``dominant_recoverable_class``, ``pareto_concentration``, ``complementarity_observation``, ``lexical_modernization_observation``, @@ -32,10 +32,10 @@ Sources de données opt-in (via ``opts``) - ``opts["benchmark"]`` : ``BenchmarkResult`` non compacté (worst lines). - ``opts["image_qualities"]`` : liste de dicts image_quality par doc. - ``opts["baseline_data"]`` : sortie de - :func:`picarones.core.baseline_comparison.compute_corpus_difficulty_percentile`. + :func:`picarones.measurements.baseline_comparison.compute_corpus_difficulty_percentile`. - ``opts["longitudinal"]`` : map ``{engine: longitudinal_data}``. - ``opts["stability"]`` : sortie de - :func:`picarones.core.reliability.compute_multirun_stability`. + :func:`picarones.measurements.reliability.compute_multirun_stability`. """ from __future__ import annotations @@ -76,16 +76,16 @@ def build_diagnostics_view_html( depuis les ``EngineReport.document_results`` avant compact). baseline_data: Sortie de - :func:`picarones.core.baseline_comparison.compute_corpus_difficulty_percentile`. + :func:`picarones.measurements.baseline_comparison.compute_corpus_difficulty_percentile`. Active l'encart « ce corpus est-il habituel ? ». longitudinal: Sortie de - :func:`picarones.core.longitudinal.compute_corpus_longitudinal`. + :func:`picarones.measurements.longitudinal.compute_corpus_longitudinal`. Active la table d'évolution. stability: Liste enrichie de ``{engine_name, ...stability_data}`` par moteur, sortie de - :func:`picarones.core.reliability.compute_multirun_stability`. + :func:`picarones.measurements.reliability.compute_multirun_stability`. Active la table de stabilité multi-runs. history_values: Valeurs historiques de difficulté du corpus, utilisées pour @@ -102,7 +102,7 @@ def build_diagnostics_view_html( # Sous-section 1 : leviers (calculés automatiquement) try: - from picarones.core.levers import detect_levers + from picarones.measurements.levers import detect_levers from picarones.report.levers_render import build_levers_section_html levers = detect_levers(report_data) html = build_levers_section_html(levers, labels=labels) @@ -141,7 +141,7 @@ def build_diagnostics_view_html( # Sous-section 3 : profil d'image du corpus (opt-in) if image_qualities: try: - from picarones.core.image_predictive import ( + from picarones.measurements.image_predictive import ( aggregate_corpus_predictive, ) from picarones.report.image_predictive_render import ( @@ -205,7 +205,7 @@ def build_diagnostics_view_html( # Sous-section 6 : worst lines (opt-in via benchmark non compacté) if benchmark is not None: try: - from picarones.core.worst_lines import extract_worst_lines + from picarones.measurements.worst_lines import extract_worst_lines from picarones.report.worst_lines_render import ( build_worst_lines_table_html, ) diff --git a/picarones/report/views/economics.py b/picarones/report/views/economics.py index 1e6ea377c04c89a1ad0ad98071dd92caaa0e3bdf..a72f83aba9b7330d1f3c010336ccfbd08308072f 100644 --- a/picarones/report/views/economics.py +++ b/picarones/report/views/economics.py @@ -31,7 +31,7 @@ def _estimate_engine_throughput_inputs( engine_reports: list, ) -> list[dict]: """Construit les entrées attendues par - :func:`picarones.core.throughput.aggregate_effective_throughput` + :func:`picarones.measurements.throughput.aggregate_effective_throughput` à partir des ``EngineReport`` du benchmark. Pour chaque moteur : @@ -131,7 +131,7 @@ def build_economics_view_html( # Sous-section 1 : throughput effectif if engine_reports: try: - from picarones.core.throughput import ( + from picarones.measurements.throughput import ( aggregate_effective_throughput, ) from picarones.report.throughput_render import ( diff --git a/picarones/report/views/pipeline.py b/picarones/report/views/pipeline.py index b63f969658f28e73170b25dc48fe4e5ad3fb7f54..e7fbc9971234041add2563c81b43b5044a16a9f8 100644 --- a/picarones/report/views/pipeline.py +++ b/picarones/report/views/pipeline.py @@ -168,7 +168,7 @@ def build_pipeline_view_html( # Sous-section 4 : comparaison incrémentale (effet d'un slot) if incremental_runs and incremental_varying_slot: try: - from picarones.core.incremental_comparison import ( + from picarones.measurements.incremental_comparison import ( compare_isolated_effect, ) from picarones.report.incremental_comparison_render import ( diff --git a/picarones/report/views/robustness.py b/picarones/report/views/robustness.py index fc03d458f8a680656eaaf3c9299c297ff8e6826d..14989451cab208e6a9f3fb174163673971bcfeab 100644 --- a/picarones/report/views/robustness.py +++ b/picarones/report/views/robustness.py @@ -12,9 +12,9 @@ de la CLI puisse composer un mini-rapport HTML autonome. Sources de données ------------------ - ``opts["projection"]`` : sortie de - :func:`picarones.core.robustness_projection.project_robustness_on_corpus`. + :func:`picarones.measurements.robustness_projection.project_robustness_on_corpus`. - ``opts["aggregated"]`` : sortie de - :func:`picarones.core.robustness_projection.aggregate_projection_per_engine`. + :func:`picarones.measurements.robustness_projection.aggregate_projection_per_engine`. """ from __future__ import annotations @@ -43,10 +43,10 @@ def build_robustness_view_html( Dict i18n complet. projection: Sortie de - :func:`picarones.core.robustness_projection.project_robustness_on_corpus`. + :func:`picarones.measurements.robustness_projection.project_robustness_on_corpus`. aggregated: Sortie de - :func:`picarones.core.robustness_projection.aggregate_projection_per_engine`. + :func:`picarones.measurements.robustness_projection.aggregate_projection_per_engine`. Si ``None`` mais ``projection`` fourni, recalculé. Returns diff --git a/picarones/report/worst_lines_render.py b/picarones/report/worst_lines_render.py index 16de39e7bf95cd036d78725b1e6943c50d044cee..10349e3c4d30264ca39824b77dfa74db59d60c07 100644 --- a/picarones/report/worst_lines_render.py +++ b/picarones/report/worst_lines_render.py @@ -18,7 +18,7 @@ from __future__ import annotations from html import escape as _e from typing import Optional -from picarones.core.worst_lines import WorstLineEntry +from picarones.measurements.worst_lines import WorstLineEntry from picarones.report.diff_utils import compute_char_diff diff --git a/picarones/web/app.py b/picarones/web/app.py index 99ada2dc2c1657a1c9575d1f2d39a19eef0717ba..b1a1ea62eee5f60a2cf58678518b8600d7693a34 100644 --- a/picarones/web/app.py +++ b/picarones/web/app.py @@ -47,7 +47,7 @@ from fastapi.responses import FileResponse, HTMLResponse, StreamingResponse from pydantic import BaseModel from picarones import __version__ -from picarones.core.jobs import JobStore, get_default_store +from picarones.web.jobs import JobStore, get_default_store from picarones.web.security import ( RateLimiter, assert_engines_allowed, @@ -1095,7 +1095,7 @@ async def api_corpus_delete(corpus_id: str) -> dict: @app.get("/api/normalization/profiles") async def api_normalization_profiles() -> dict: - from picarones.core.normalization import NORMALIZATION_PROFILES + from picarones.measurements.normalization import NORMALIZATION_PROFILES profiles = [ { @@ -1283,7 +1283,7 @@ async def api_benchmark_synthesis_preview(job_id: str, lang: str = "fr") -> dict except (OSError, json.JSONDecodeError) as exc: raise HTTPException(status_code=422, detail=f"Lecture JSON échouée : {exc}") - from picarones.core.narrative import build_synthesis + from picarones.measurements.narrative import build_synthesis synthesis = build_synthesis(report_json, lang=lang) return { @@ -1313,7 +1313,7 @@ async def api_history_regressions( un encart *« ⚠ Tesseract a régressé de 0,8 pp depuis le 12 janvier »* en tête de page. """ - from picarones.core.history import BenchmarkHistory + from picarones.measurements.history import BenchmarkHistory try: history = BenchmarkHistory(db_path) if db_path else BenchmarkHistory() @@ -1412,7 +1412,7 @@ async def api_htr_united_catalogue( language: str = Query(default="", description="Filtre langue"), script: str = Query(default="", description="Filtre type d'écriture"), ) -> dict: - from picarones.importers.htr_united import HTRUnitedCatalogue + from picarones.extras.importers.htr_united import HTRUnitedCatalogue cat = HTRUnitedCatalogue.from_demo() results = cat.search( @@ -1431,7 +1431,7 @@ async def api_htr_united_catalogue( @app.post("/api/htr-united/import") async def api_htr_united_import(req: HTRUnitedImportRequest) -> dict: - from picarones.importers.htr_united import HTRUnitedCatalogue, import_htr_united_corpus + from picarones.extras.importers.htr_united import HTRUnitedCatalogue, import_htr_united_corpus cat = HTRUnitedCatalogue.from_demo() entry = cat.get_by_id(req.entry_id) @@ -1457,7 +1457,7 @@ async def api_huggingface_search( tags: str = Query(default="", description="Tags séparés par des virgules"), limit: int = Query(default=20, ge=1, le=50), ) -> dict: - from picarones.importers.huggingface import HuggingFaceImporter + from picarones.extras.importers.huggingface import HuggingFaceImporter tag_list = [t.strip() for t in tags.split(",") if t.strip()] if tags else None importer = HuggingFaceImporter() @@ -1475,7 +1475,7 @@ async def api_huggingface_search( @app.post("/api/huggingface/import") async def api_huggingface_import(req: HuggingFaceImportRequest) -> dict: - from picarones.importers.huggingface import HuggingFaceImporter + from picarones.extras.importers.huggingface import HuggingFaceImporter importer = HuggingFaceImporter() result = importer.import_dataset( @@ -1823,7 +1823,7 @@ def _run_benchmark_thread_v2(job: BenchmarkJob, req: BenchmarkRunRequest) -> Non try: from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark corpus = load_corpus_from_directory(req.corpus_path) job.total_docs = len(corpus) @@ -1872,7 +1872,7 @@ def _run_benchmark_thread_v2(job: BenchmarkJob, req: BenchmarkRunRequest) -> Non "total": total_steps, }) - from picarones.core.normalization import _parse_exclude_chars + from picarones.measurements.normalization import _parse_exclude_chars char_excl = _parse_exclude_chars(req.char_exclude) if req.char_exclude else None result = run_benchmark( @@ -1919,7 +1919,7 @@ def _run_benchmark_thread(job: BenchmarkJob, req: BenchmarkRequest) -> None: try: from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark # Charger le corpus job.add_event("log", {"message": f"Chargement du corpus : {req.corpus_path}"}) @@ -1975,7 +1975,7 @@ def _run_benchmark_thread(job: BenchmarkJob, req: BenchmarkRequest) -> None: "total": total_steps, }) - from picarones.core.normalization import _parse_exclude_chars + from picarones.measurements.normalization import _parse_exclude_chars char_excl = _parse_exclude_chars(req.char_exclude) if req.char_exclude else None # Lancer le benchmark diff --git a/picarones/core/jobs.py b/picarones/web/jobs.py similarity index 99% rename from picarones/core/jobs.py rename to picarones/web/jobs.py index 6f3d84469e533bff108e237e435d2da58269c556..c4543472aca8a59835f37475c2ec9a6fc6d0e852 100644 --- a/picarones/core/jobs.py +++ b/picarones/web/jobs.py @@ -12,7 +12,7 @@ Avant le Sprint 26, l'état des benchmarks vivait uniquement en mémoire dans au-delà de ce que ``BenchmarkJob.events`` portait en RAM. Le Sprint 26 adresse les trois en persistant les jobs et leurs événements -dans une base SQLite locale (cohérent avec ``picarones.core.history``, +dans une base SQLite locale (cohérent avec ``picarones.measurements.history``, qui utilise déjà SQLite). La base joue trois rôles : - **Source de vérité** pour le statut/progression d'un job — ``BenchmarkJob`` diff --git a/tests/features/test_narrative_and_views.py b/tests/features/test_narrative_and_views.py index 900657e817202927c5abd1e90963389d4cbf1982..ed99e8c8bf9a794643471248cdf56a028866583d 100644 --- a/tests/features/test_narrative_and_views.py +++ b/tests/features/test_narrative_and_views.py @@ -8,7 +8,7 @@ Tests couvrant cette feature - :mod:`tests.test_chantier5` (classe ``TestDetectorsPackage``) — package thématique des détecteurs (chantier 5). - :mod:`tests.test_views` (chantier 3) — vue diagnostics qui consomme - les leviers calculés depuis ``picarones.core.levers``. + les leviers calculés depuis ``picarones.measurements.levers``. Sprints d'origine du moteur narratif ------------------------------------ diff --git a/tests/features/test_pipeline_ocr_to_alto.py b/tests/features/test_pipeline_ocr_to_alto.py index 285cb26264786a2e27e0248024e5244c14efca4d..6c19091cd48bf53c9500d8ccea0c4c8445b8f8c2 100644 --- a/tests/features/test_pipeline_ocr_to_alto.py +++ b/tests/features/test_pipeline_ocr_to_alto.py @@ -34,7 +34,7 @@ import pytest from picarones.core.corpus import AltoGT, Document, GTLevel, TextGT from picarones.core.metric_registry import select_metrics from picarones.core.modules import ArtifactType, BaseModule -from picarones.core.pipeline_runner import ( +from picarones.core.pipeline import ( PipelineRunner, PipelineSpec, PipelineStep, @@ -193,7 +193,7 @@ class TestYamlSpec: import yaml # noqa: F401 except ImportError: pytest.skip("PyYAML absent") - from picarones.core.pipeline_spec_loader import load_pipeline_spec_from_yaml + from picarones.measurements.pipeline_spec_loader import load_pipeline_spec_from_yaml repo_root = Path(__file__).resolve().parents[2] yaml_path = repo_root / "examples" / "pipelines" / "ocr_to_alto.yaml" @@ -206,7 +206,7 @@ class TestYamlSpec: # quand pytesseract ou pero-ocr ne sont pas installés # (CI minimaliste). Le test vérifie que la structure # YAML est syntaxiquement valide. - from picarones.core.pipeline_spec_loader import PipelineSpecLoadError + from picarones.measurements.pipeline_spec_loader import PipelineSpecLoadError if isinstance(exc, PipelineSpecLoadError) and ( "tesseract" in str(exc).lower() or "pytesseract" in str(exc).lower() ): diff --git a/tests/test_alto_baseline.py b/tests/test_alto_baseline.py index b29b5b81549b3338745190ab12bd4e7ba20738ad..9a9a9ad867f38edc1b5a08a297cec1425a20a5c9 100644 --- a/tests/test_alto_baseline.py +++ b/tests/test_alto_baseline.py @@ -5,7 +5,7 @@ Couvre : - :class:`picarones.modules.TextToAltoMonoRegion` : produit un ALTO 4.2 conforme, déterministe, qui tolère absence d'image / image introuvable / dimensions invalides. -- :func:`picarones.core.alto_metrics.extract_text_from_alto` : parsing +- :func:`picarones.measurements.alto_metrics.extract_text_from_alto` : parsing tolérant (avec/sans namespace, ALTO partiel, GT ``AltoGT`` ou ``str``). - Métriques ``alto_text_cer`` / ``alto_text_wer`` enregistrées sur ``(ALTO, ALTO)`` et découvrables via ``compute_at_junction``. @@ -22,14 +22,14 @@ from xml.etree import ElementTree as ET import pytest -from picarones.core.alto_metrics import ( +from picarones.measurements.alto_metrics import ( alto_text_cer, extract_text_from_alto, ) from picarones.core.corpus import AltoGT, Document, GTLevel, TextGT from picarones.core.metric_registry import compute_at_junction, select_metrics from picarones.core.modules import ArtifactType, BaseModule -from picarones.core.pipeline_runner import ( +from picarones.core.pipeline import ( PipelineRunner, PipelineSpec, PipelineStep, @@ -237,7 +237,7 @@ class TestExtractTextFromAlto: class TestAltoMetricsRegistration: def test_alto_metrics_are_registered(self): # L'import du module doit avoir peuplé le registre. - import picarones.core.alto_metrics # noqa: F401 + import picarones.measurements.alto_metrics # noqa: F401 applicable = select_metrics( (ArtifactType.ALTO, ArtifactType.ALTO), @@ -249,7 +249,7 @@ class TestAltoMetricsRegistration: assert "alto_text_wil" in names def test_compute_at_junction_runs_alto_metrics(self): - import picarones.core.alto_metrics # noqa: F401 + import picarones.measurements.alto_metrics # noqa: F401 ref = '' hyp = '' results = compute_at_junction( diff --git a/tests/test_chantier4.py b/tests/test_chantier4.py index 4684ce8b577b40737f538c01fa7af8bcb2ee5f0c..698b240776227b40f2afa749ee845877846529cb 100644 --- a/tests/test_chantier4.py +++ b/tests/test_chantier4.py @@ -5,7 +5,7 @@ Couvre : - Sous-chantier 4.A : ``normalize_llm_content`` + ``log_http_error`` factorisés dans :mod:`picarones.llm.base`, propagés aux 4 adapters. - Sous-chantier 4.B : helpers HTTP factorisés dans - :mod:`picarones.importers._http`, Gallica et IIIF y délèguent. + :mod:`picarones.extras.importers._http`, Gallica et IIIF y délèguent. - Sous-chantier 4.C : 3 nouvelles sous-commandes CLI ``diagnose``, ``economics``, ``edition`` qui mappent un profil de calcul (chantier 2) à un workflow. @@ -160,40 +160,40 @@ class TestLlmAdaptersInheritEnvVar: class TestHttpHelpers: def test_validate_http_url_accepts_https(self): - from picarones.importers._http import validate_http_url + from picarones.extras.importers._http import validate_http_url validate_http_url("https://gallica.bnf.fr/test") # ne lève pas def test_validate_http_url_accepts_http(self): - from picarones.importers._http import validate_http_url + from picarones.extras.importers._http import validate_http_url validate_http_url("http://localhost:8080/x") @pytest.mark.parametrize("scheme", ["file", "ftp", "data", "javascript", "ssh"]) def test_validate_http_url_rejects_other_schemes(self, scheme): - from picarones.importers._http import validate_http_url + from picarones.extras.importers._http import validate_http_url with pytest.raises(ValueError, match="non autorisé"): validate_http_url(f"{scheme}://example.com/x") class TestIiifAliasesDelegateToHttp: """Les noms ``_validate_url`` et ``_download_url`` exposés depuis - :mod:`picarones.importers.iiif` doivent rester disponibles + :mod:`picarones.extras.importers.iiif` doivent rester disponibles (rétrocompat des tests Sprint 4) — ils délèguent aux helpers factorisés.""" def test_iiif_validate_url_is_alias(self): - from picarones.importers import iiif - from picarones.importers._http import validate_http_url + from picarones.extras.importers import iiif + from picarones.extras.importers._http import validate_http_url assert iiif._validate_url is validate_http_url def test_iiif_download_url_is_alias(self): - from picarones.importers import iiif - from picarones.importers._http import download_url + from picarones.extras.importers import iiif + from picarones.extras.importers._http import download_url assert iiif._download_url is download_url class TestGallicaDelegatesToHttp: def test_gallica_validate_url_delegates(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() # Doit accepter https client._validate_url("https://gallica.bnf.fr/x") @@ -206,7 +206,7 @@ class TestGallicaDelegatesToHttp: # Lecture statique du source — pas d'appel réseau. # Chantier 5 (3 cercles) : le contenu vit désormais dans # ``picarones/extras/importers/gallica.py`` ; le module - # historique ``picarones.importers.gallica`` est un alias. + # historique ``picarones.extras.importers.gallica`` est un alias. from pathlib import Path gallica_src = ( Path(__file__).parent.parent @@ -215,7 +215,7 @@ class TestGallicaDelegatesToHttp: # Confirme que Gallica importe IIIFImporter assert "IIIFImporter" in gallica_src assert "from picarones.extras.importers.iiif" in gallica_src or \ - "from picarones.importers.iiif" in gallica_src + "from picarones.extras.importers.iiif" in gallica_src # ────────────────────────────────────────────────────────────────────────── diff --git a/tests/test_chantier5.py b/tests/test_chantier5.py index 19434864072885898e693f6e7d1edf0ad432ae63..b3d850fa5515f280a87d83e27e4b84b4e3fa612e 100644 --- a/tests/test_chantier5.py +++ b/tests/test_chantier5.py @@ -2,7 +2,7 @@ Couvre : -- 5.A : :mod:`picarones.core.narrative.detectors` est désormais un +- 5.A : :mod:`picarones.measurements.narrative.detectors` est désormais un package thématique de 6 sous-modules (1229 lignes → 6 fichiers). Tous les imports historiques restent accessibles. - 5.B : :mod:`picarones.cli` est désormais un package avec 6 @@ -23,7 +23,7 @@ import pytest class TestDetectorsPackage: def test_detectors_is_now_a_package(self): """``detectors.py`` est devenu ``detectors/`` (package).""" - from picarones.core.narrative import detectors + from picarones.measurements.narrative import detectors # Un package a __path__, un module simple ne l'a pas assert hasattr(detectors, "__path__"), ( "detectors devrait être un package depuis le chantier 5" @@ -52,19 +52,19 @@ class TestDetectorsPackage: def test_all_18_detectors_importable_from_root(self, name): """Rétrocompat : les 18 détecteurs s'importent depuis le package comme avant le chantier 5 (tests Sprints 20, 23, 29, 36, 44, 46, 73).""" - from picarones.core.narrative import detectors + from picarones.measurements.narrative import detectors assert hasattr(detectors, name), f"{name} disparu après chantier 5" assert callable(getattr(detectors, name)) def test_DETECTORS_BY_TYPE_still_exposed(self): - from picarones.core.narrative.detectors import DETECTORS_BY_TYPE + from picarones.measurements.narrative.detectors import DETECTORS_BY_TYPE assert isinstance(DETECTORS_BY_TYPE, dict) assert len(DETECTORS_BY_TYPE) == 18, ( f"DETECTORS_BY_TYPE doit contenir 18 entrées, en a {len(DETECTORS_BY_TYPE)}" ) def test_register_default_detectors_still_callable(self): - from picarones.core.narrative.detectors import register_default_detectors + from picarones.measurements.narrative.detectors import register_default_detectors assert callable(register_default_detectors) @pytest.mark.parametrize("submodule, detector_count", [ @@ -80,7 +80,7 @@ class TestDetectorsPackage: import importlib mod = importlib.import_module( - f"picarones.core.narrative.detectors.{submodule}" + f"picarones.measurements.narrative.detectors.{submodule}" ) detectors_in_sub = [ n for n in dir(mod) @@ -94,15 +94,15 @@ class TestDetectorsPackage: def test_identity_through_submodule_and_root(self): """Le détecteur exposé depuis __init__.py et depuis son sous-module est la même fonction (pas de redéfinition).""" - from picarones.core.narrative.detectors import detect_global_leader_cer - from picarones.core.narrative.detectors.ranking import ( + from picarones.measurements.narrative.detectors import detect_global_leader_cer + from picarones.measurements.narrative.detectors.ranking import ( detect_global_leader_cer as via_submodule, ) assert detect_global_leader_cer is via_submodule def test_detector_smoke_via_root(self): """Smoke test : un détecteur fonctionne via l'import root.""" - from picarones.core.narrative.detectors import detect_global_leader_cer + from picarones.measurements.narrative.detectors import detect_global_leader_cer result = detect_global_leader_cer({ "ranking": [ {"engine": "tess", "mean_cer": 0.05}, @@ -115,7 +115,7 @@ class TestDetectorsPackage: def test_helpers_are_in_dedicated_module(self): """Les helpers internes (_engines_summary, etc.) vivent dans ``_helpers.py`` (pattern modulaire propre).""" - from picarones.core.narrative.detectors import _helpers + from picarones.measurements.narrative.detectors import _helpers assert hasattr(_helpers, "_engines_summary") assert hasattr(_helpers, "_engine_by_name") assert hasattr(_helpers, "_n_docs") @@ -227,12 +227,12 @@ class TestRunnerStillReachable: ]) def test_function_still_in_runner(self, name): try: - from picarones.core import runner + from picarones.measurements import runner except ImportError as exc: if "tqdm" in str(exc): pytest.skip("tqdm non installé") raise assert hasattr(runner, name), ( - f"runner.{name} a disparu — chantier 5 n'aurait pas dû y toucher" + f"runner.{name} a disparu" ) assert callable(getattr(runner, name)) diff --git a/tests/test_char_scores.py b/tests/test_char_scores.py index f232c8c77d6d31aa8356d7eaf53436b1d9585100..4d8d09f1e819e7488c3deec7b34d65ac2db6c5d6 100644 --- a/tests/test_char_scores.py +++ b/tests/test_char_scores.py @@ -20,7 +20,7 @@ from __future__ import annotations import pytest -from picarones.core.char_scores import ( +from picarones.measurements.char_scores import ( DiacriticScore, LigatureScore, aggregate_diacritic_scores, diff --git a/tests/test_metric_hooks.py b/tests/test_metric_hooks.py index 35ff3bd564cc8e8a98e48a184dd35944ca81912a..788e582736d32ab21816e385cab7ba70baabc6af 100644 --- a/tests/test_metric_hooks.py +++ b/tests/test_metric_hooks.py @@ -4,11 +4,11 @@ Couvre : - :mod:`picarones.core.metric_hooks` : profils, registre, décorateurs, sélection par profil, exécution avec gestion d'erreurs. -- :mod:`picarones.core.builtin_hooks` : enregistre les 12+12 hooks +- :mod:`picarones.measurements.builtin_hooks` : enregistre les 12+12 hooks historiques sur le profil ``standard``. - Rétrocompat : les fonctions privées ``_aggregate_*`` et ``_calibration_from_engine_result`` restent accessibles depuis - ``picarones.core.runner`` (tests Sprint 13/42). + ``picarones.measurements.runner`` (tests Sprint 13/42). - Le profil ``standard`` (défaut) couvre **exactement** les 12 hooks documentaires et 12 agrégateurs historiques. - Le profil ``minimal`` n'active aucun hook (bench rapide). @@ -63,7 +63,7 @@ class TestProfiles: class TestBuiltinHooksRegistration: def test_twelve_document_hooks_registered(self): # Import déclenche l'enregistrement via décorateurs. - import picarones.core.builtin_hooks # noqa: F401 + import picarones.measurements.builtin_hooks # noqa: F401 from picarones.core.metric_hooks import _all_document_hook_names names = set(_all_document_hook_names()) @@ -76,7 +76,7 @@ class TestBuiltinHooksRegistration: assert expected.issubset(names), f"manquants : {expected - names}" def test_twelve_corpus_aggregators_registered(self): - import picarones.core.builtin_hooks # noqa: F401 + import picarones.measurements.builtin_hooks # noqa: F401 from picarones.core.metric_hooks import _all_corpus_aggregator_names names = set(_all_corpus_aggregator_names()) @@ -89,7 +89,7 @@ class TestBuiltinHooksRegistration: assert expected.issubset(names), f"manquants : {expected - names}" def test_standard_profile_activates_all_hooks(self): - import picarones.core.builtin_hooks # noqa: F401 + import picarones.measurements.builtin_hooks # noqa: F401 from picarones.core.metric_hooks import ( select_corpus_aggregators, select_document_hooks, ) @@ -100,7 +100,7 @@ class TestBuiltinHooksRegistration: assert len(agg_hooks) == 12, [a.name for a in agg_hooks] def test_minimal_profile_activates_zero_hooks(self): - import picarones.core.builtin_hooks # noqa: F401 + import picarones.measurements.builtin_hooks # noqa: F401 from picarones.core.metric_hooks import ( select_corpus_aggregators, select_document_hooks, ) @@ -112,7 +112,7 @@ class TestBuiltinHooksRegistration: """Les attributs déclarés par les hooks doivent correspondre aux champs réels du DocumentResult — sinon le runner planterait à l'instanciation du dataclass.""" - import picarones.core.builtin_hooks # noqa: F401 + import picarones.measurements.builtin_hooks # noqa: F401 from dataclasses import fields from picarones.core.metric_hooks import select_document_hooks @@ -126,7 +126,7 @@ class TestBuiltinHooksRegistration: ) def test_aggregator_attribute_names_match_enginereport(self): - import picarones.core.builtin_hooks # noqa: F401 + import picarones.measurements.builtin_hooks # noqa: F401 from dataclasses import fields from picarones.core.metric_hooks import select_corpus_aggregators @@ -264,7 +264,7 @@ class TestRunDocumentHooks: class TestRunnerBackwardCompat: """Les tests Sprint 13 et Sprint 42 importent directement depuis - ``picarones.core.runner``. Ces noms doivent rester disponibles + ``picarones.measurements.runner``. Ces noms doivent rester disponibles après le chantier 2.""" @pytest.mark.parametrize("name", [ @@ -281,11 +281,11 @@ class TestRunnerBackwardCompat: def test_helper_still_exported_from_runner(self, name): # Skip si tqdm ou autres deps absents (sandbox minimaliste). pytest.importorskip("tqdm") - from picarones.core import runner + from picarones.measurements import runner assert hasattr(runner, name), ( f"runner.{name} a disparu — casse les tests Sprint 13/42 " - "qui font ``from picarones.core.runner import {name}``" + "qui font ``from picarones.measurements.runner import {name}``" ) assert callable(getattr(runner, name)) diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 479541b4cf1e448e6ab589db09825bca2d1dcabb..54c864eb8cecb332a99b1fd702368865c81d2b52 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -1,8 +1,8 @@ -"""Tests unitaires pour le module picarones.core.metrics.""" +"""Tests unitaires pour le module picarones.measurements.metrics.""" import pytest -from picarones.core.metrics import aggregate_metrics, compute_metrics, MetricsResult +from picarones.measurements.metrics import aggregate_metrics, compute_metrics, MetricsResult class TestComputeMetrics: diff --git a/tests/test_phaseE_migration.py b/tests/test_phaseE_migration.py deleted file mode 100644 index f3082b4ab2d92fe3c34f070d631e9403ccf6a977..0000000000000000000000000000000000000000 --- a/tests/test_phaseE_migration.py +++ /dev/null @@ -1,273 +0,0 @@ -"""Tests de la phase E — séparation core/ + measurements/. - -Couvre : - -- ~41 modules métriques déplacés vers ``picarones/measurements/``. -- Sous-package ``narrative/`` (4 modules + 6 familles de détecteurs + - helper) déplacé vers ``picarones/measurements/narrative/``. -- Identité préservée à travers les shims. -- Le ``core/`` strict ne contient plus que ~13 fichiers (Cercle 1). -- Les hooks builtin restent enregistrés. -- Le moteur narratif fonctionne (détection + arbitre + rendu). -- Les vues du chantier 3 fonctionnent. -- Document ``docs/architecture-cercles.md`` mis à jour avec critère DDD. -""" - -from __future__ import annotations - -import importlib -import warnings -from pathlib import Path - -import pytest - - -# ────────────────────────────────────────────────────────────────────────── -# 1. Imports historiques rétrocompat des mesures -# ────────────────────────────────────────────────────────────────────────── - - -class TestMeasurementsRetrocompat: - @pytest.mark.parametrize("module_path, attribute", [ - ("picarones.core.confusion", "build_confusion_matrix"), - ("picarones.core.taxonomy", "classify_errors"), - ("picarones.core.calibration", "compute_calibration_metrics"), - ("picarones.core.layout", "compute_layout_metrics"), - ("picarones.core.reading_order", "compute_reading_order_metrics"), - ("picarones.core.error_absorption", "compute_error_absorption"), - ("picarones.core.searchability", "compute_searchability"), - ("picarones.core.numerical_sequences", - "compute_numerical_sequence_metrics"), - ("picarones.core.readability", "flesch_score"), - ("picarones.core.specialization", "compute_specialization_score"), - ("picarones.core.throughput", "compute_effective_throughput"), - ("picarones.core.cost_projection", "project_engine"), - ("picarones.core.statistics", "bootstrap_ci"), - ("picarones.core.history", "BenchmarkHistory"), - ("picarones.core.builtin_hooks", "calibration_from_engine_result"), - ("picarones.core.line_metrics", "compute_line_metrics"), - ("picarones.core.hallucination", "compute_hallucination_metrics"), - ("picarones.core.image_quality", "analyze_image_quality"), - ("picarones.core.normalization", "NORMALIZATION_PROFILES"), - ("picarones.core.rare_tokens", "extract_rare_tokens"), - ]) - def test_legacy_path_works(self, module_path: str, attribute: str): - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - mod = importlib.import_module(module_path) - assert hasattr(mod, attribute), f"{module_path}.{attribute}" - - -# ────────────────────────────────────────────────────────────────────────── -# 2. Sous-package narrative/ déplacé -# ────────────────────────────────────────────────────────────────────────── - - -class TestNarrativePackageMigration: - def test_narrative_root_import(self): - from picarones.core.narrative import build_synthesis - assert callable(build_synthesis) - - def test_narrative_facts_via_shim(self): - from picarones.core.narrative.facts import Fact, FactType - assert Fact is not None - assert FactType is not None - - def test_narrative_registry_via_shim(self): - from picarones.core.narrative.registry import register_detector - assert callable(register_detector) - - def test_narrative_detectors_via_shim(self): - from picarones.core.narrative.detectors import ( - DETECTORS_BY_TYPE, - detect_global_leader_cer, - ) - assert callable(detect_global_leader_cer) - assert len(DETECTORS_BY_TYPE) == 18 - - def test_narrative_detector_family_modules(self): - # Les 6 familles restent accessibles via leur ancien chemin - from picarones.core.narrative.detectors.ranking import ( - detect_global_leader_cer, - ) - from picarones.core.narrative.detectors.pareto import ( - detect_pareto_alternative, - ) - from picarones.core.narrative.detectors.history import ( - detect_engine_unstable, - ) - from picarones.core.narrative.detectors.quality import ( - detect_confidence_warning, - ) - from picarones.core.narrative.detectors.stratum import ( - detect_stratum_winner, - ) - from picarones.core.narrative.detectors.ensemble import ( - detect_ensemble_opportunity, - ) - assert all(callable(f) for f in [ - detect_global_leader_cer, - detect_pareto_alternative, - detect_engine_unstable, - detect_confidence_warning, - detect_stratum_winner, - detect_ensemble_opportunity, - ]) - - -# ────────────────────────────────────────────────────────────────────────── -# 3. Identité préservée -# ────────────────────────────────────────────────────────────────────────── - - -class TestIdentityThroughShim: - def test_confusion_identity(self): - from picarones.core.confusion import build_confusion_matrix as via_old - from picarones.measurements.confusion import ( - build_confusion_matrix as via_new, - ) - assert via_old is via_new - - def test_narrative_facts_identity(self): - from picarones.core.narrative.facts import Fact as via_old - from picarones.measurements.narrative.facts import Fact as via_new - assert via_old is via_new - - def test_narrative_detector_identity(self): - from picarones.core.narrative.detectors.ranking import ( - detect_speed_winner as via_old, - ) - from picarones.measurements.narrative.detectors.ranking import ( - detect_speed_winner as via_new, - ) - assert via_old is via_new - - -# ────────────────────────────────────────────────────────────────────────── -# 4. core/ Cercle 1 strict — ne contient plus que ~13 modules -# ────────────────────────────────────────────────────────────────────────── - - -class TestCoreIsLean: - """Le ``core/`` post-phase E ne contient plus que les modules - Cercle 1 (abstractions + orchestration). Tout le reste est shim.""" - - @pytest.mark.parametrize("name", [ - "corpus", "modules", "results", "metrics", - "runner", "pipeline_runner", "pipeline_benchmark", - "pipeline_comparison", "pipeline_spec_loader", - "metric_registry", "metric_hooks", - "builtin_metrics", "alto_metrics", - ]) - def test_cercle1_module_present(self, name): - """Les modules Cercle 1 doivent rester dans ``core/`` (pas de shim).""" - repo = Path(__file__).parent.parent - path = repo / "picarones" / "core" / f"{name}.py" - assert path.exists() - content = path.read_text(encoding="utf-8") - # Un module Cercle 1 a > 30 lignes (vraie logique, pas shim) - n_lines = len([line for line in content.splitlines() if line.strip()]) - assert n_lines > 30, ( - f"core/{name}.py fait {n_lines} lignes — ne devrait pas être " - "un shim, c'est un module Cercle 1" - ) - - -# ────────────────────────────────────────────────────────────────────────── -# 5. Hooks builtin enregistrés (12 doc + 12 corpus) -# ────────────────────────────────────────────────────────────────────────── - - -class TestHooksStillRegistered: - def test_12_doc_hooks(self): - # Eager-load des hooks via builtin_hooks (qui est maintenant un shim - # vers measurements/builtin_hooks). - import picarones.core.builtin_hooks # noqa: F401 - from picarones.core.metric_hooks import _all_document_hook_names - - hooks = _all_document_hook_names() - # Le compte exact dépend des hooks expérimentaux qui tests - # pourraient ajouter, donc on vérifie >= 12 et la présence des - # 12 attendus. - expected = { - "confusion", "char_scores", "taxonomy", "structure", - "image_quality", "line_metrics", "hallucination", - "calibration", "philological", "searchability", - "numerical_sequences", "readability", - } - assert expected.issubset(set(hooks)) - - def test_alto_metrics_registered(self): - from picarones.core.metric_registry import select_metrics - from picarones.core.modules import ArtifactType - - metrics = select_metrics((ArtifactType.ALTO, ArtifactType.ALTO)) - names = {s.name for s in metrics} - assert "alto_text_cer" in names - assert "alto_text_wer" in names - - -# ────────────────────────────────────────────────────────────────────────── -# 6. build_synthesis fonctionne (intégration narrative complète) -# ────────────────────────────────────────────────────────────────────────── - - -class TestNarrativeIntegration: - def test_build_synthesis_works(self): - from picarones.core.narrative import build_synthesis - - synth = build_synthesis({ - "ranking": [ - {"engine": "tess", "mean_cer": 0.05}, - {"engine": "pero", "mean_cer": 0.08}, - ], - }, lang="fr") - assert "sentences" in synth - assert "facts" in synth - assert len(synth["sentences"]) >= 1 - - -# ────────────────────────────────────────────────────────────────────────── -# 7. Vues du chantier 3 fonctionnent -# ────────────────────────────────────────────────────────────────────────── - - -class TestChantier3ViewsAfterPhaseE: - def test_views_still_work(self): - from picarones.report.views import ( - build_advanced_taxonomy_view_html, - ) - report_data = {"engines": [ - {"name": "tess", "cer": 0.05, - "aggregated_taxonomy": {"class_distribution": {"x": 5}}}, - {"name": "pero", "cer": 0.08, - "aggregated_taxonomy": {"class_distribution": {"x": 8}}}, - ]} - # Au moins advanced_taxonomy doit produire du HTML - html = build_advanced_taxonomy_view_html(report_data, {}) - assert isinstance(html, str) - - -# ────────────────────────────────────────────────────────────────────────── -# 8. Documentation cercles mise à jour -# ────────────────────────────────────────────────────────────────────────── - - -class TestArchitectureCerclesDocUpdated: - @pytest.fixture - def doc(self) -> str: - path = Path(__file__).parent.parent / "docs" / "architecture-cercles.md" - return path.read_text(encoding="utf-8") - - def test_critere_corrige(self, doc): - """Le critère DDD remplace l'ancien critère ambigu.""" - assert "abstractions et logique métier du domaine" in doc - assert "indépendantes de l'interface utilisateur" in doc - - def test_mention_phase_e(self, doc): - """Le doc référence le sous-package measurements/.""" - # Au moins une mention du nouveau dossier - # (chemin physique du Cercle 2) - # NB : le doc parle de ``measurements/`` mais la lettre exacte - # dépend de la formulation. On accepte plusieurs variantes. - assert "measurements" in doc.lower() or "Cercle 2" in doc diff --git a/tests/test_pricing_degenerate_cases.py b/tests/test_pricing_degenerate_cases.py index 98e17b1a869c6d96173d6149a6d7f83a203141f2..88d5fd439b42e2baf4cde1c449dc4220063bb8b4 100644 --- a/tests/test_pricing_degenerate_cases.py +++ b/tests/test_pricing_degenerate_cases.py @@ -19,14 +19,14 @@ from __future__ import annotations import pytest -from picarones.core.pricing import ( +from picarones.measurements.pricing import ( EngineCost, PricingDefaults, build_costs_for_benchmark, estimate_cost, load_pricing_database, ) -from picarones.core.statistics import compute_pareto_front +from picarones.measurements.statistics import compute_pareto_front # --------------------------------------------------------------------------- diff --git a/tests/test_public_api.py b/tests/test_public_api.py index aa30e2cba7f23e51258eaa0c2b634ea06887869e..5f71a2508cc6a45c94b8d661f8886102c67c1e8a 100644 --- a/tests/test_public_api.py +++ b/tests/test_public_api.py @@ -151,24 +151,24 @@ class TestResultsApi: # ────────────────────────────────────────────────────────────────────────── -# 4. picarones.core.metrics — métriques de base +# 4. picarones.measurements.metrics — métriques de base # ────────────────────────────────────────────────────────────────────────── class TestMetricsApi: def test_metrics_result_class(self): - _assert_class("picarones.core.metrics", "MetricsResult") + _assert_class("picarones.measurements.metrics", "MetricsResult") @pytest.mark.parametrize("name", [ "compute_metrics", "aggregate_metrics", ]) def test_function_exists(self, name): - _assert_function("picarones.core.metrics", name) + _assert_function("picarones.measurements.metrics", name) def test_compute_metrics_signature(self): """``compute_metrics(reference, hypothesis, char_exclude=None)`` est contractuel — les 2 premiers args sont positionnels, le 3ᵉ keyword.""" - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics sig = inspect.signature(compute_metrics) params = list(sig.parameters.values()) # Au moins 2 paramètres positionnels (reference, hypothesis) @@ -182,14 +182,14 @@ class TestMetricsApi: # ────────────────────────────────────────────────────────────────────────── -# 5. picarones.core.runner — run_benchmark +# 5. picarones.measurements.runner — run_benchmark # ────────────────────────────────────────────────────────────────────────── class TestRunnerApi: def test_run_benchmark_exists(self): try: - _assert_function("picarones.core.runner", "run_benchmark") + _assert_function("picarones.measurements.runner", "run_benchmark") except ImportError as exc: if "tqdm" in str(exc): pytest.skip("tqdm non installé en sandbox") @@ -199,7 +199,7 @@ class TestRunnerApi: """Les paramètres clés (corpus, engines, profile…) doivent rester accessibles. Ajout d'un argument requis = breaking change.""" try: - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark except ImportError as exc: if "tqdm" in str(exc): pytest.skip("tqdm non installé") @@ -218,7 +218,7 @@ class TestRunnerApi: # ────────────────────────────────────────────────────────────────────────── -# 6. picarones.core.pipeline_runner — banc d'essai pipelines +# 6. picarones.core.pipeline — banc d'essai pipelines # ────────────────────────────────────────────────────────────────────────── @@ -228,7 +228,7 @@ class TestPipelineRunnerApi: "StepResult", "PipelineResult", "PipelineRunner", ]) def test_class_exists(self, name): - _assert_class("picarones.core.pipeline_runner", name) + _assert_class("picarones.core.pipeline", name) class TestPipelineBenchmarkApi: @@ -236,31 +236,31 @@ class TestPipelineBenchmarkApi: "StepAggregate", "PipelineBenchmarkResult", ]) def test_class_exists(self, name): - _assert_class("picarones.core.pipeline_benchmark", name) + _assert_class("picarones.measurements.pipeline_benchmark", name) @pytest.mark.parametrize("name", [ "default_initial_inputs", "run_pipeline_benchmark", ]) def test_function_exists(self, name): - _assert_function("picarones.core.pipeline_benchmark", name) + _assert_function("picarones.measurements.pipeline_benchmark", name) class TestPipelineComparisonApi: def test_pipeline_comparison_result(self): _assert_class( - "picarones.core.pipeline_comparison", "PipelineComparisonResult", + "picarones.measurements.pipeline_comparison", "PipelineComparisonResult", ) def test_compare_pipelines(self): _assert_function( - "picarones.core.pipeline_comparison", "compare_pipelines", + "picarones.measurements.pipeline_comparison", "compare_pipelines", ) class TestPipelineSpecLoaderApi: def test_pipeline_spec_load_error(self): cls = _assert_class( - "picarones.core.pipeline_spec_loader", "PipelineSpecLoadError", + "picarones.measurements.pipeline_spec_loader", "PipelineSpecLoadError", ) assert issubclass(cls, ValueError) @@ -271,7 +271,7 @@ class TestPipelineSpecLoaderApi: "load_comparison_specs_from_dict", ]) def test_function_exists(self, name): - _assert_function("picarones.core.pipeline_spec_loader", name) + _assert_function("picarones.measurements.pipeline_spec_loader", name) # ────────────────────────────────────────────────────────────────────────── @@ -343,7 +343,7 @@ class TestMetricHooksApi: # ────────────────────────────────────────────────────────────────────────── -# 9. picarones.core.builtin_metrics — CER/WER/MER/WIL natifs +# 9. picarones.measurements.builtin_metrics — CER/WER/MER/WIL natifs # ────────────────────────────────────────────────────────────────────────── @@ -353,40 +353,40 @@ class TestBuiltinMetricsApi: "text_preservation_after_reconstruction", ]) def test_function_exists(self, name): - _assert_function("picarones.core.builtin_metrics", name) + _assert_function("picarones.measurements.builtin_metrics", name) # ────────────────────────────────────────────────────────────────────────── -# 10. picarones.core.alto_metrics — métriques (ALTO, ALTO) +# 10. picarones.measurements.alto_metrics — métriques (ALTO, ALTO) # ────────────────────────────────────────────────────────────────────────── class TestAltoMetricsApi: def test_extract_text_from_alto(self): - _assert_function("picarones.core.alto_metrics", "extract_text_from_alto") + _assert_function("picarones.measurements.alto_metrics", "extract_text_from_alto") @pytest.mark.parametrize("name", [ "alto_text_cer", "alto_text_wer", "alto_text_mer", "alto_text_wil", ]) def test_alto_metric_function(self, name): - _assert_function("picarones.core.alto_metrics", name) + _assert_function("picarones.measurements.alto_metrics", name) # ────────────────────────────────────────────────────────────────────────── -# 11. picarones.core.jobs — JobStore (utilisé par web/) +# 11. picarones.web.jobs — JobStore (utilisé par web/) # ────────────────────────────────────────────────────────────────────────── class TestJobsApi: def test_job_store(self): - _assert_class("picarones.core.jobs", "JobStore") + _assert_class("picarones.web.jobs", "JobStore") @pytest.mark.parametrize("name", [ "get_default_store", "reset_default_store", ]) def test_function_exists(self, name): - _assert_function("picarones.core.jobs", name) + _assert_function("picarones.web.jobs", name) # ────────────────────────────────────────────────────────────────────────── @@ -399,17 +399,13 @@ class TestCercle1IsLean: (les autres sont des shims). Ce test garde-fou empêche un module métrique d'être réintroduit dans le cœur sans RFC.""" - # Modules Cercle 1 « gros » (> 30 lignes de logique). ``colors.py`` - # est un fichier utilitaire de constantes (13 lignes) co-localisé - # dans ``core/`` pour éviter le churn — il n'est pas dans cette - # liste car le seuil 30 lignes ne le détecte pas comme « réel », - # mais sa présence dans le dossier est tolérée. + # Modules Cercle 1 — abstractions pures (corpus, contrats, registres). + # Tout module avec de la logique métier (calcul, orchestration) + # appartient au Cercle 2 (``measurements/``) ou au Cercle 3 + # (``extras/``, ``report/``). EXPECTED_CERCLE1 = { - "alto_metrics.py", "builtin_metrics.py", "corpus.py", "jobs.py", - "metric_hooks.py", "metric_registry.py", "metrics.py", "modules.py", - "pipeline_benchmark.py", "pipeline_comparison.py", - "pipeline_runner.py", "pipeline_spec_loader.py", - "results.py", "runner.py", + "corpus.py", "facts.py", "metric_hooks.py", "metric_registry.py", + "modules.py", "pipeline.py", "results.py", } def test_cercle1_files_lean(self): @@ -460,17 +456,17 @@ class TestApiStableDoc: "picarones.core.corpus", "picarones.core.modules", "picarones.core.results", - "picarones.core.metrics", - "picarones.core.runner", - "picarones.core.pipeline_runner", - "picarones.core.pipeline_benchmark", - "picarones.core.pipeline_comparison", - "picarones.core.pipeline_spec_loader", + "picarones.measurements.metrics", + "picarones.measurements.runner", + "picarones.core.pipeline", + "picarones.measurements.pipeline_benchmark", + "picarones.measurements.pipeline_comparison", + "picarones.measurements.pipeline_spec_loader", "picarones.core.metric_registry", "picarones.core.metric_hooks", - "picarones.core.builtin_metrics", - "picarones.core.alto_metrics", - "picarones.core.jobs", + "picarones.measurements.builtin_metrics", + "picarones.measurements.alto_metrics", + "picarones.web.jobs", ]: assert module in content, ( f"docs/api-stable.md ne mentionne pas {module}" diff --git a/tests/test_results.py b/tests/test_results.py index f4b24beb34364a398aa9750b6144000d4d19378b..f16f78d1c7955b3637832c0c4f64904cc4d479d4 100644 --- a/tests/test_results.py +++ b/tests/test_results.py @@ -3,7 +3,7 @@ import json import pytest -from picarones.core.metrics import MetricsResult +from picarones.measurements.metrics import MetricsResult from picarones.core.results import BenchmarkResult, DocumentResult, EngineReport diff --git a/tests/test_sprint10_error_distribution.py b/tests/test_sprint10_error_distribution.py index c7d383fc7fe617e9434c9552c6b75eedcc70ea24..0070eaa0514578c7a1ce9818fa335dbbca58d50a 100644 --- a/tests/test_sprint10_error_distribution.py +++ b/tests/test_sprint10_error_distribution.py @@ -41,31 +41,31 @@ HYP_HALLUCINATED = ( # =========================================================================== class TestLineMetrics: - """Tests pour picarones.core.line_metrics.compute_line_metrics.""" + """Tests pour picarones.measurements.line_metrics.compute_line_metrics.""" def test_import(self): - from picarones.core.line_metrics import compute_line_metrics, LineMetrics + from picarones.measurements.line_metrics import compute_line_metrics, LineMetrics assert callable(compute_line_metrics) assert LineMetrics is not None def test_perfect_match_cer_zero(self): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_PERFECT) assert result.mean_cer == pytest.approx(0.0, abs=1e-9) assert all(v == pytest.approx(0.0, abs=1e-9) for v in result.cer_per_line) def test_line_count(self): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS) assert result.line_count == 3 def test_cer_per_line_length(self): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS) assert len(result.cer_per_line) == 3 def test_percentiles_keys(self): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS) for key in ("p50", "p75", "p90", "p95", "p99"): assert key in result.percentiles @@ -73,23 +73,23 @@ class TestLineMetrics: def test_percentile_ordering(self): """p50 ≤ p75 ≤ p90 ≤ p95 ≤ p99.""" - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS) p = result.percentiles assert p["p50"] <= p["p75"] <= p["p90"] <= p["p95"] <= p["p99"] def test_gini_zero_for_perfect(self): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_PERFECT) assert result.gini == pytest.approx(0.0, abs=1e-9) def test_gini_range(self): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS) assert 0.0 <= result.gini <= 1.0 def test_catastrophic_rate_keys(self): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS, thresholds=[0.30, 0.50, 1.00]) for t in (0.30, 0.50, 1.00): @@ -97,12 +97,12 @@ class TestLineMetrics: assert 0.0 <= result.catastrophic_rate[t] <= 1.0 def test_heatmap_length(self): - from picarones.core.line_metrics import compute_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS, heatmap_bins=5) assert len(result.heatmap) == 5 def test_as_dict_and_from_dict_roundtrip(self): - from picarones.core.line_metrics import compute_line_metrics, LineMetrics + from picarones.measurements.line_metrics import compute_line_metrics, LineMetrics result = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS) d = result.as_dict() restored = LineMetrics.from_dict(d) @@ -111,7 +111,7 @@ class TestLineMetrics: assert len(restored.cer_per_line) == len(result.cer_per_line) def test_aggregate_line_metrics(self): - from picarones.core.line_metrics import compute_line_metrics, aggregate_line_metrics + from picarones.measurements.line_metrics import compute_line_metrics, aggregate_line_metrics r1 = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_PERFECT) r2 = compute_line_metrics(GT_MULTILINE, HYP_MULTILINE_ERRORS) agg = aggregate_line_metrics([r1, r2]) @@ -128,64 +128,64 @@ class TestLineMetrics: # =========================================================================== class TestHallucinationMetrics: - """Tests pour picarones.core.hallucination.compute_hallucination_metrics.""" + """Tests pour picarones.measurements.hallucination.compute_hallucination_metrics.""" def test_import(self): - from picarones.core.hallucination import compute_hallucination_metrics, HallucinationMetrics + from picarones.measurements.hallucination import compute_hallucination_metrics, HallucinationMetrics assert callable(compute_hallucination_metrics) assert HallucinationMetrics is not None def test_perfect_match_anchor_one(self): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics(GT_SIMPLE, HYP_PERFECT) # Ancrage parfait → score proche de 1.0 assert result.anchor_score == pytest.approx(1.0, abs=0.05) assert result.is_hallucinating is False def test_length_ratio_perfect(self): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics(GT_SIMPLE, HYP_PERFECT) assert result.length_ratio == pytest.approx(1.0, abs=0.05) def test_hallucination_detected(self): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics(GT_MEDIEVAL, HYP_HALLUCINATED) # L'hypothèse est beaucoup plus longue assert result.length_ratio > 1.0 assert result.is_hallucinating is True def test_hallucinated_blocks_detected(self): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics(GT_MEDIEVAL, HYP_HALLUCINATED, anchor_threshold=0.5, min_block_length=3) # Des blocs hallucinés doivent être détectés assert len(result.hallucinated_blocks) > 0 def test_net_insertion_rate_range(self): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics(GT_MEDIEVAL, HYP_HALLUCINATED) assert 0.0 <= result.net_insertion_rate <= 1.0 def test_word_counts(self): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics(GT_SIMPLE, HYP_PERFECT) assert result.gt_word_count > 0 assert result.hyp_word_count > 0 def test_empty_reference(self): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics("", "some text here added by model") # Référence vide : insertion nette maximale assert result.net_insertion_rate == pytest.approx(1.0, abs=0.05) def test_empty_hypothesis(self): - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics(GT_SIMPLE, "") assert result.hyp_word_count == 0 assert result.net_insertion_rate == pytest.approx(0.0) def test_as_dict_and_from_dict_roundtrip(self): - from picarones.core.hallucination import compute_hallucination_metrics, HallucinationMetrics + from picarones.measurements.hallucination import compute_hallucination_metrics, HallucinationMetrics result = compute_hallucination_metrics(GT_MEDIEVAL, HYP_HALLUCINATED) d = result.as_dict() restored = HallucinationMetrics.from_dict(d) @@ -194,7 +194,7 @@ class TestHallucinationMetrics: assert len(restored.hallucinated_blocks) == len(result.hallucinated_blocks) def test_aggregate_hallucination_metrics(self): - from picarones.core.hallucination import compute_hallucination_metrics, aggregate_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics, aggregate_hallucination_metrics r1 = compute_hallucination_metrics(GT_SIMPLE, HYP_PERFECT) r2 = compute_hallucination_metrics(GT_MEDIEVAL, HYP_HALLUCINATED) agg = aggregate_hallucination_metrics([r1, r2]) @@ -207,7 +207,7 @@ class TestHallucinationMetrics: def test_anchor_threshold_respected(self): """Un ancrage très bas déclenche le badge hallucination.""" - from picarones.core.hallucination import compute_hallucination_metrics + from picarones.measurements.hallucination import compute_hallucination_metrics result = compute_hallucination_metrics( "abc def ghi", "xyz uvw rst opq lmn", anchor_threshold=0.5 @@ -225,7 +225,7 @@ class TestLineMetricsInResults: def test_document_result_has_line_metrics_field(self): from picarones.core.results import DocumentResult - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult dr = DocumentResult( doc_id="test_001", image_path="/test/img.jpg", @@ -245,7 +245,7 @@ class TestLineMetricsInResults: def test_document_result_has_hallucination_metrics_field(self): from picarones.core.results import DocumentResult - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult dr = DocumentResult( doc_id="test_002", image_path="/test/img.jpg", @@ -265,7 +265,7 @@ class TestLineMetricsInResults: def test_document_result_as_dict_includes_sprint10_fields(self): from picarones.core.results import DocumentResult - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult dr = DocumentResult( doc_id="test_003", image_path="/test/img.jpg", @@ -287,7 +287,7 @@ class TestLineMetricsInResults: def test_engine_report_has_aggregated_sprint10_fields(self): from picarones.core.results import EngineReport, DocumentResult - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult dr = DocumentResult( doc_id="test_004", image_path="/test/img.jpg", diff --git a/tests/test_sprint11_i18n_english.py b/tests/test_sprint11_i18n_english.py index 439ca23cf142cb59f8bf8f4401ae2dfc76795e60..31929fa93474a8c6f86679e2c1a139b2f0494287 100644 --- a/tests/test_sprint11_i18n_english.py +++ b/tests/test_sprint11_i18n_english.py @@ -26,7 +26,7 @@ class TestEarlyModernEnglish: @pytest.fixture def profile(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile return get_builtin_profile("early_modern_english") def test_profile_exists(self, profile): @@ -89,7 +89,7 @@ class TestMedievalEnglish: @pytest.fixture def profile(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile return get_builtin_profile("medieval_english") def test_profile_exists(self, profile): @@ -129,7 +129,7 @@ class TestSecretaryHand: @pytest.fixture def profile(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile return get_builtin_profile("secretary_hand") def test_profile_exists(self, profile): @@ -160,18 +160,18 @@ class TestBuiltinProfilesListing: """Vérifie que les 3 nouveaux profils sont bien accessibles.""" def test_all_english_profiles_accessible(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile for name in ("early_modern_english", "medieval_english", "secretary_hand"): p = get_builtin_profile(name) assert p.name == name def test_unknown_profile_raises_key_error(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile with pytest.raises(KeyError): get_builtin_profile("unknown_lang_profile_xyz") def test_existing_profiles_still_work(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile for name in ("medieval_french", "early_modern_french", "medieval_latin", "nfc", "caseless", "minimal"): p = get_builtin_profile(name) assert p.name == name diff --git a/tests/test_sprint12_nouvelles_fonctionnalites.py b/tests/test_sprint12_nouvelles_fonctionnalites.py index e2cf636ea2c293ba92893a9fe3c9b85e0ef6225c..958cc1076a7fa01e2bb25f820ea364abb42fe47f 100644 --- a/tests/test_sprint12_nouvelles_fonctionnalites.py +++ b/tests/test_sprint12_nouvelles_fonctionnalites.py @@ -71,7 +71,7 @@ class TestMacOSHiddenFilesFiltering: class TestExcludeCharsNormalization: def test_parse_exclude_chars_from_comma_string(self): - from picarones.core.normalization import _parse_exclude_chars + from picarones.measurements.normalization import _parse_exclude_chars result = _parse_exclude_chars("', -, –") assert "'" in result @@ -79,7 +79,7 @@ class TestExcludeCharsNormalization: assert "–" in result def test_parse_exclude_chars_from_plain_string(self): - from picarones.core.normalization import _parse_exclude_chars + from picarones.measurements.normalization import _parse_exclude_chars result = _parse_exclude_chars(".,;:!?") assert "." in result @@ -87,13 +87,13 @@ class TestExcludeCharsNormalization: assert "?" in result def test_parse_exclude_chars_empty(self): - from picarones.core.normalization import _parse_exclude_chars + from picarones.measurements.normalization import _parse_exclude_chars assert _parse_exclude_chars("") == frozenset() assert _parse_exclude_chars(None) == frozenset() def test_normalize_strips_excluded_chars(self): - from picarones.core.normalization import NormalizationProfile + from picarones.measurements.normalization import NormalizationProfile profile = NormalizationProfile( name="test", @@ -102,7 +102,7 @@ class TestExcludeCharsNormalization: assert profile.normalize("Bonjour, monde.") == "Bonjour monde" def test_sans_ponctuation_profile_exists(self): - from picarones.core.normalization import NORMALIZATION_PROFILES + from picarones.measurements.normalization import NORMALIZATION_PROFILES assert "sans_ponctuation" in NORMALIZATION_PROFILES p = NORMALIZATION_PROFILES["sans_ponctuation"] @@ -111,7 +111,7 @@ class TestExcludeCharsNormalization: assert "?" in p.exclude_chars def test_sans_apostrophes_profile_exists(self): - from picarones.core.normalization import NORMALIZATION_PROFILES + from picarones.measurements.normalization import NORMALIZATION_PROFILES assert "sans_apostrophes" in NORMALIZATION_PROFILES p = NORMALIZATION_PROFILES["sans_apostrophes"] @@ -119,7 +119,7 @@ class TestExcludeCharsNormalization: assert "\u2019" in p.exclude_chars # apostrophe typographique def test_compute_metrics_with_char_exclude(self): - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics ref = "Bonjour, monde!" hyp = "Bonjour monde" @@ -135,7 +135,7 @@ class TestExcludeCharsNormalization: def test_char_exclude_propagated_in_run_benchmark(self, tmp_path): """char_exclude doit être transmis à run_benchmark et réduire le CER.""" from picarones.core.corpus import Corpus, Document - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine, EngineResult class MockEngine(BaseOCREngine): @@ -195,7 +195,7 @@ def sample_generator(): """Fixture partagée : crée un ReportGenerator avec des données fictives.""" from picarones.report.generator import ReportGenerator from picarones.core.results import BenchmarkResult, DocumentResult, EngineReport - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult def _make_metric(cer=0.1): return MetricsResult( diff --git a/tests/test_sprint13_parallelisation_stats.py b/tests/test_sprint13_parallelisation_stats.py index 80b9fbcdcae430d6680f3e907a2eed5bacd32184..25b589bf74f64b444decebe6e2531864b9a39a85 100644 --- a/tests/test_sprint13_parallelisation_stats.py +++ b/tests/test_sprint13_parallelisation_stats.py @@ -133,31 +133,31 @@ class TestRunnerParallelParams: def test_max_workers_param_exists(self): """run_benchmark doit accepter max_workers.""" - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark sig = inspect.signature(run_benchmark) assert "max_workers" in sig.parameters def test_max_workers_default_is_4(self): """max_workers doit avoir 4 comme valeur par défaut.""" - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark sig = inspect.signature(run_benchmark) assert sig.parameters["max_workers"].default == 4 def test_timeout_seconds_param_exists(self): """run_benchmark doit accepter timeout_seconds.""" - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark sig = inspect.signature(run_benchmark) assert "timeout_seconds" in sig.parameters def test_timeout_seconds_default_is_60(self): """timeout_seconds doit avoir 60.0 comme valeur par défaut.""" - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark sig = inspect.signature(run_benchmark) assert sig.parameters["timeout_seconds"].default == 60.0 def test_partial_dir_param_exists(self): """run_benchmark doit accepter partial_dir (None par défaut).""" - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark sig = inspect.signature(run_benchmark) assert "partial_dir" in sig.parameters assert sig.parameters["partial_dir"].default is None @@ -172,7 +172,7 @@ class TestRunnerTimeout: def test_timeout_doc_result_has_error(self, tmp_corpus): """Un document ayant dépassé le timeout doit avoir engine_error contenant 'timeout'.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine import time @@ -201,7 +201,7 @@ class TestRunnerTimeout: def test_timeout_doc_result_cer_is_one(self, tmp_corpus): """Un document timeout doit avoir CER = 1.0.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine import time @@ -227,7 +227,7 @@ class TestRunnerTimeout: def test_fast_docs_not_affected_by_timeout(self, tmp_corpus): """Des documents rapides ne doivent pas être touchés par un timeout généreux.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine class FastEngine(BaseOCREngine): @@ -258,9 +258,9 @@ class TestRunnerPartialResults: def test_partial_file_created_during_run(self, tmp_corpus, tmp_path): """_save_partial_line doit être appelée pour chaque document traité.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine - import picarones.core.runner as runner_mod + import picarones.measurements.runner as runner_mod save_calls: list[str] = [] original_save = runner_mod._save_partial_line @@ -289,7 +289,7 @@ class TestRunnerPartialResults: def test_partial_file_deleted_after_success(self, tmp_corpus, tmp_path): """Le fichier .partial.json doit être supprimé après un benchmark réussi.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine class MockEngine(BaseOCREngine): @@ -310,7 +310,7 @@ class TestRunnerPartialResults: def test_partial_load_skips_already_done_docs(self, tmp_corpus, tmp_path): """La reprise depuis un fichier partiel doit sauter les documents déjà traités.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import _load_partial, _partial_path + from picarones.measurements.runner import _load_partial, _partial_path corpus = load_corpus_from_directory(str(tmp_corpus)) corpus_name = corpus.name @@ -338,7 +338,7 @@ class TestRunnerPartialResults: def test_partial_load_returns_empty_for_missing_file(self, tmp_path): """Si aucun fichier partiel n'existe, la liste doit être vide.""" - from picarones.core.runner import _load_partial + from picarones.measurements.runner import _load_partial _, loaded = _load_partial("corpus_inexistant", "moteur_inexistant", tmp_path) assert loaded == [] @@ -353,7 +353,7 @@ class TestRunnerSilentExceptions: """Une erreur dans build_confusion_matrix doit être loguée, pas ignorée.""" import logging from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine class MockEngine(BaseOCREngine): @@ -364,10 +364,10 @@ class TestRunnerSilentExceptions: corpus = load_corpus_from_directory(str(tmp_corpus)) with patch( - "picarones.core.runner._compute_document_result", - wraps=__import__("picarones.core.runner", fromlist=["_compute_document_result"])._compute_document_result, + "picarones.measurements.runner._compute_document_result", + wraps=__import__("picarones.measurements.runner", fromlist=["_compute_document_result"])._compute_document_result, ): - with patch("picarones.core.confusion.build_confusion_matrix", side_effect=RuntimeError("crash test")): + with patch("picarones.measurements.confusion.build_confusion_matrix", side_effect=RuntimeError("crash test")): with caplog.at_level(logging.WARNING): result = run_benchmark(corpus, [MockEngine()], show_progress=False) @@ -379,7 +379,7 @@ class TestRunnerSilentExceptions: """Une exception dans le progress_callback doit être loguée, pas propagée.""" import logging from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine class MockEngine(BaseOCREngine): @@ -406,11 +406,11 @@ class TestRunnerSilentExceptions: def test_aggregate_helpers_log_on_failure(self, caplog): """Les helpers _aggregate_* doivent logger en WARNING et retourner None.""" import logging - from picarones.core.runner import _aggregate_confusion + from picarones.measurements.runner import _aggregate_confusion # Créer un doc_result avec des données de confusion corrompues from picarones.core.results import DocumentResult - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult bad_dr = DocumentResult( doc_id="x", image_path="x.png", ground_truth="gt", hypothesis="hyp", metrics=MetricsResult(cer=0.1, cer_nfc=0.1, cer_caseless=0.1, @@ -433,7 +433,7 @@ class TestWilcoxonValidation: def test_identical_sequences_not_significant(self): """Séquences identiques → pas de différence, p = 1.0, significant = False.""" - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test a = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] r = wilcoxon_test(a, a) assert r["significant"] is False @@ -442,7 +442,7 @@ class TestWilcoxonValidation: def test_all_positive_diffs_w_minus_is_zero(self): """Si toutes les différences a−b sont positives : W⁻ = 0, W⁺ = n(n+1)/2.""" - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test n = 10 a = [float(i) for i in range(1, n + 1)] b = [0.0] * n @@ -453,7 +453,7 @@ class TestWilcoxonValidation: def test_w_plus_w_minus_sum_invariant(self): """W⁺ + W⁻ doit toujours être égal à n(n+1)/2 (n = nombre de paires non nulles).""" - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test a = [0.10, 0.25, 0.05, 0.40, 0.30, 0.15, 0.20, 0.35, 0.08, 0.18] b = [0.12, 0.20, 0.08, 0.35, 0.28, 0.18, 0.15, 0.40, 0.10, 0.20] r = wilcoxon_test(a, b) @@ -466,7 +466,7 @@ class TestWilcoxonValidation: def test_clearly_different_sequences_significant(self): """Deux séquences très différentes (n=15) doivent donner p < 0.05.""" - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test a = [0.05] * 15 # moteur A très performant b = [0.60] * 15 # moteur B peu performant — toutes diff = −0.55 # Diffs a−b = −0.55 pour tous → W⁺ = 0 → devrait être significatif @@ -476,7 +476,7 @@ class TestWilcoxonValidation: def test_large_n_normal_approximation_reasonable(self): """Pour n = 20, l'approximation normale doit donner une p-value dans [0, 1].""" - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test import random rng = random.Random(42) a = [rng.uniform(0.1, 0.5) for _ in range(20)] @@ -487,7 +487,7 @@ class TestWilcoxonValidation: def test_small_n_returns_conservative_p(self): """Pour n < 10, la p-value doit être 0.04 (significatif) ou 0.20 (non sign.).""" - from picarones.core.statistics import wilcoxon_test, _SCIPY_AVAILABLE + from picarones.measurements.statistics import wilcoxon_test, _SCIPY_AVAILABLE if _SCIPY_AVAILABLE: pytest.skip("scipy disponible — la table exacte n'est pas utilisée") a = [0.1, 0.2, 0.3] @@ -498,7 +498,7 @@ class TestWilcoxonValidation: def test_result_keys_complete(self): """Le dict retourné doit contenir toutes les clés documentées.""" - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test r = wilcoxon_test([0.1, 0.3, 0.2, 0.4, 0.15, 0.35, 0.25, 0.5, 0.45, 0.05], [0.2, 0.2, 0.3, 0.3, 0.25, 0.25, 0.35, 0.35, 0.40, 0.15]) for key in ("statistic", "p_value", "significant", "interpretation", "n_pairs", "W_plus", "W_minus"): @@ -513,12 +513,12 @@ class TestWilcoxonScipyIntegration: def test_scipy_available_flag_is_bool(self): """_SCIPY_AVAILABLE doit être un booléen.""" - from picarones.core.statistics import _SCIPY_AVAILABLE + from picarones.measurements.statistics import _SCIPY_AVAILABLE assert isinstance(_SCIPY_AVAILABLE, bool) def test_scipy_and_native_agree_on_significance(self): """Scipy et l'implémentation native doivent s'accorder sur la significativité.""" - from picarones.core.statistics import wilcoxon_test, _SCIPY_AVAILABLE + from picarones.measurements.statistics import wilcoxon_test, _SCIPY_AVAILABLE if not _SCIPY_AVAILABLE: pytest.skip("scipy non disponible") @@ -534,7 +534,7 @@ class TestWilcoxonScipyIntegration: def test_scipy_p_value_in_valid_range(self): """La p-value fournie par scipy doit être dans [0, 1].""" - from picarones.core.statistics import wilcoxon_test, _SCIPY_AVAILABLE + from picarones.measurements.statistics import wilcoxon_test, _SCIPY_AVAILABLE if not _SCIPY_AVAILABLE: pytest.skip("scipy non disponible") diff --git a/tests/test_sprint14_robust_filtering.py b/tests/test_sprint14_robust_filtering.py index 05b17b92054c5003e35595667bc6bd2a93939dc8..965ea9b0a2ec72b00f23fd75ffbca2360e9ce1d8 100644 --- a/tests/test_sprint14_robust_filtering.py +++ b/tests/test_sprint14_robust_filtering.py @@ -23,7 +23,7 @@ import pytest def _make_fake_benchmark(): """Retourne un BenchmarkResult minimal pour tester le générateur.""" from picarones.core.results import BenchmarkResult, EngineReport, DocumentResult - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult def _metrics(cer, wer=0.2): return MetricsResult( diff --git a/tests/test_sprint15_llm_pipeline_bugs.py b/tests/test_sprint15_llm_pipeline_bugs.py index d73a6ddc5fb0d2afc64a345145c8738f05e548de..34237954721b7b3c21e0f653c0f1ce0f1bdffe17 100644 --- a/tests/test_sprint15_llm_pipeline_bugs.py +++ b/tests/test_sprint15_llm_pipeline_bugs.py @@ -20,13 +20,13 @@ class TestEmptyHypothesisMetrics: """compute_metrics doit retourner CER=1.0, pas 0.0, pour hypothèse vide.""" def test_empty_hypothesis_cer_is_one(self): - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics result = compute_metrics("Bonjour le monde", "") assert result.cer == pytest.approx(1.0) assert result.error is None def test_empty_hypothesis_all_metrics_are_one(self): - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics result = compute_metrics("hello world", "") assert result.cer == pytest.approx(1.0) assert result.wer == pytest.approx(1.0) @@ -34,13 +34,13 @@ class TestEmptyHypothesisMetrics: assert result.wil == pytest.approx(1.0) def test_whitespace_only_hypothesis_cer_is_one(self): - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics result = compute_metrics("Bonjour", " \t\n") assert result.cer == pytest.approx(1.0) def test_none_hypothesis_guarded(self): """compute_metrics ne doit pas planter si hypothesis=None.""" - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics # None ne sera jamais passé en pratique, mais on teste la robustesse # via une chaîne vide (le runner convertit None → "") result = compute_metrics("test", "") @@ -48,19 +48,19 @@ class TestEmptyHypothesisMetrics: def test_both_empty_cer_is_zero(self): """Référence ET hypothèse vides → CER=0.0 (pas d'erreur à mesurer).""" - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics result = compute_metrics("", "") assert result.cer == pytest.approx(0.0) def test_empty_reference_nonempty_hypothesis(self): """Référence vide avec hypothèse non vide → CER=1.0 (comportement existant).""" - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics result = compute_metrics("", "something") assert result.cer == pytest.approx(1.0) def test_normal_case_unchanged(self): """Un cas normal ne doit pas être affecté par le guard.""" - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics result = compute_metrics("abcd", "abce") assert result.cer == pytest.approx(0.25) assert result.error is None @@ -238,7 +238,7 @@ class TestRunnerDocumentResultCohérence: def test_empty_hypothesis_stored_as_cer_one(self): """_compute_document_result avec text="" → metrics.cer = 1.0.""" - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult ocr_result = EngineResult( @@ -266,7 +266,7 @@ class TestRunnerDocumentResultCohérence: def test_engine_error_also_gives_cer_one(self): """EngineResult avec error → metrics.cer = 1.0 (comportement existant).""" - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult ocr_result = EngineResult( diff --git a/tests/test_sprint16_narrative_foundations.py b/tests/test_sprint16_narrative_foundations.py index 5defda5ad72b221a6ec3e6a33a3baefeed535d80..1e92501586105e33c2a68ddf3aea120a067a620a 100644 --- a/tests/test_sprint16_narrative_foundations.py +++ b/tests/test_sprint16_narrative_foundations.py @@ -16,14 +16,14 @@ import json from pathlib import Path from picarones.core.corpus import Corpus, Document -from picarones.core.narrative import ( +from picarones.measurements.narrative import ( DetectorRegistry, Fact, FactImportance, FactType, detect_all, ) -from picarones.core.runner import ( +from picarones.measurements.runner import ( _aggregate_hallucination, _aggregate_line_metrics, _compute_document_result, diff --git a/tests/test_sprint18_friedman_nemenyi_cdd.py b/tests/test_sprint18_friedman_nemenyi_cdd.py index 6c167ffe56210743c00fb4bfc6b972da3c02cd58..9df4871d30aab043a96226fb09bcd020c59a969f 100644 --- a/tests/test_sprint18_friedman_nemenyi_cdd.py +++ b/tests/test_sprint18_friedman_nemenyi_cdd.py @@ -14,7 +14,7 @@ import re import pytest -from picarones.core.statistics import ( +from picarones.measurements.statistics import ( build_critical_difference_svg, friedman_test, nemenyi_posthoc, @@ -361,8 +361,8 @@ class TestReportIntegration: class TestStatisticalTieDetector: def test_detector_emits_fact_when_engines_are_tied(self): - from picarones.core.narrative.detectors import detect_statistical_tie - from picarones.core.narrative.facts import FactType + from picarones.measurements.narrative.detectors import detect_statistical_tie + from picarones.core.facts import FactType benchmark_data = { "statistics": { @@ -384,7 +384,7 @@ class TestStatisticalTieDetector: assert f.payload["critical_distance"] == 0.9 def test_detector_ignores_singletons(self): - from picarones.core.narrative.detectors import detect_statistical_tie + from picarones.measurements.narrative.detectors import detect_statistical_tie benchmark_data = { "statistics": { @@ -401,14 +401,14 @@ class TestStatisticalTieDetector: assert facts == [] def test_detector_returns_empty_on_missing_data(self): - from picarones.core.narrative.detectors import detect_statistical_tie + from picarones.measurements.narrative.detectors import detect_statistical_tie assert detect_statistical_tie({}) == [] assert detect_statistical_tie({"statistics": {}}) == [] assert detect_statistical_tie({"statistics": {"nemenyi": {"error": "no_data"}}}) == [] def test_non_leader_tie_is_high_not_critical(self): - from picarones.core.narrative.detectors import detect_statistical_tie - from picarones.core.narrative.facts import FactImportance + from picarones.measurements.narrative.detectors import detect_statistical_tie + from picarones.core.facts import FactImportance benchmark_data = { "statistics": { diff --git a/tests/test_sprint19_narrative_engine.py b/tests/test_sprint19_narrative_engine.py index ce0d502f1c7b3dbb6da4d45254d4f9e25f67f041..a0749c13f3766a6ba49470b1bfd98a49e23d169d 100644 --- a/tests/test_sprint19_narrative_engine.py +++ b/tests/test_sprint19_narrative_engine.py @@ -15,7 +15,7 @@ import re import pytest -from picarones.core.narrative import ( +from picarones.measurements.narrative import ( Fact, FactImportance, FactType, @@ -25,7 +25,7 @@ from picarones.core.narrative import ( render_synthesis, select_facts, ) -from picarones.core.narrative.detectors import ( +from picarones.measurements.narrative.detectors import ( detect_confidence_warning, detect_error_profile_outlier, detect_global_leader_cer, @@ -579,8 +579,8 @@ class TestReportIntegration: hashlib.sha256(s2.group().encode()).hexdigest() def test_default_registry_has_all_types_registered(self): - from picarones.core.narrative import _DEFAULT_REGISTRY - from picarones.core.narrative.facts import FactType + from picarones.measurements.narrative import _DEFAULT_REGISTRY + from picarones.core.facts import FactType registered = set(_DEFAULT_REGISTRY.registered_types()) # Tous les types de FactType doivent avoir un détecteur enregistré. diff --git a/tests/test_sprint20_pareto_pricing.py b/tests/test_sprint20_pareto_pricing.py index 50ca2d1c348de943546fc7c045a6b2b94fb8a08d..bd80d6f09262ba4a4ad8ee4d11573b86433f6501 100644 --- a/tests/test_sprint20_pareto_pricing.py +++ b/tests/test_sprint20_pareto_pricing.py @@ -15,18 +15,18 @@ from pathlib import Path import pytest -from picarones.core.narrative import build_synthesis -from picarones.core.narrative.detectors import ( +from picarones.measurements.narrative import build_synthesis +from picarones.measurements.narrative.detectors import ( detect_cost_outlier, detect_pareto_alternative, ) -from picarones.core.narrative.facts import FactType -from picarones.core.pricing import ( +from picarones.core.facts import FactType +from picarones.measurements.pricing import ( build_costs_for_benchmark, estimate_cost, load_pricing_database, ) -from picarones.core.statistics import compute_pareto_front +from picarones.measurements.statistics import compute_pareto_front # --------------------------------------------------------------------------- @@ -306,7 +306,7 @@ class TestReportIntegration: def test_pricing_yaml_is_packaged(self): """Garde-fou : le YAML doit être accessible depuis le package.""" - from picarones.core.pricing import _DEFAULT_PRICING_PATH + from picarones.measurements.pricing import _DEFAULT_PRICING_PATH assert Path(_DEFAULT_PRICING_PATH).exists() def test_english_locale_renders_pareto_labels(self, benchmark_result, tmp_path): diff --git a/tests/test_sprint23_anti_hallucination.py b/tests/test_sprint23_anti_hallucination.py index e3f6b76307519d65a558432195e1163bc044aa63..fa3c73a033f5d20dce48629a6271cdf1c753a787 100644 --- a/tests/test_sprint23_anti_hallucination.py +++ b/tests/test_sprint23_anti_hallucination.py @@ -30,18 +30,18 @@ from pathlib import Path import pytest -from picarones.core.narrative import ( +from picarones.measurements.narrative import ( Fact, FactImportance, FactType, build_synthesis, select_facts, ) -from picarones.core.narrative.arbiter import DEFAULT_TYPE_ORDER -from picarones.core.statistics import bootstrap_ci +from picarones.measurements.narrative.arbiter import DEFAULT_TYPE_ORDER +from picarones.measurements.statistics import bootstrap_ci ROOT = Path(__file__).parent.parent -TEMPLATES_DIR = ROOT / "picarones" / "core" / "narrative" / "templates" +TEMPLATES_DIR = ROOT / "picarones" / "measurements" / "narrative" / "templates" # --------------------------------------------------------------------------- @@ -84,7 +84,7 @@ def _full_data() -> dict: class TestPayloadsCarryFormerlyHardcodedConstants: def test_confidence_warning_payload_carries_confidence_level(self): - from picarones.core.narrative.detectors import detect_confidence_warning + from picarones.measurements.narrative.detectors import detect_confidence_warning facts = detect_confidence_warning(_full_data()) assert facts, "fixture devrait déclencher au moins un confidence_warning" @@ -95,7 +95,7 @@ class TestPayloadsCarryFormerlyHardcodedConstants: ) def test_pareto_alternative_payload_carries_cost_unit(self): - from picarones.core.narrative.detectors import detect_pareto_alternative + from picarones.measurements.narrative.detectors import detect_pareto_alternative facts = detect_pareto_alternative(_full_data()) assert facts, "fixture devrait déclencher au moins un pareto_alternative" @@ -103,7 +103,7 @@ class TestPayloadsCarryFormerlyHardcodedConstants: assert f.payload.get("cost_unit_pages") == 1000 def test_cost_outlier_payload_carries_cost_unit(self): - from picarones.core.narrative.detectors import detect_cost_outlier + from picarones.measurements.narrative.detectors import detect_cost_outlier facts = detect_cost_outlier(_full_data()) assert facts, "fixture devrait déclencher au moins un cost_outlier" @@ -161,7 +161,7 @@ class TestEndToEndWithEmptyWhitelist: @pytest.mark.parametrize("lang", ["fr", "en"]) def test_every_number_traceable_with_empty_whitelist(self, lang): - from picarones.core.narrative import extract_numbers + from picarones.measurements.narrative import extract_numbers from tests.test_sprint19_narrative_engine import _numbers_in_payload diff --git a/tests/test_sprint26_jobs_persistence.py b/tests/test_sprint26_jobs_persistence.py index f7534055ad3187eede3d52946d69163e8a47e022..26cf1ad8b60f2668fa9fa1854e81e88dc3cc9ae7 100644 --- a/tests/test_sprint26_jobs_persistence.py +++ b/tests/test_sprint26_jobs_persistence.py @@ -27,7 +27,7 @@ import time import pytest from fastapi.testclient import TestClient -from picarones.core.jobs import JobStore, get_default_store, reset_default_store +from picarones.web.jobs import JobStore, get_default_store, reset_default_store # --------------------------------------------------------------------------- diff --git a/tests/test_sprint27_reproducibility_snapshots.py b/tests/test_sprint27_reproducibility_snapshots.py index 853274f560798cf6bd88a7e0615c066d9e59f547..2018c1dedcbf96e41782b8285761666e47a3cf49 100644 --- a/tests/test_sprint27_reproducibility_snapshots.py +++ b/tests/test_sprint27_reproducibility_snapshots.py @@ -101,7 +101,7 @@ class TestGlossarySnapshot: class TestNormalizationSnapshot: def test_builtin_profile_serializes(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile from picarones.report.snapshot import normalization_snapshot p = get_builtin_profile("medieval_french") s = normalization_snapshot(p) @@ -117,7 +117,7 @@ class TestNormalizationSnapshot: assert s["available"] is False def test_exclude_chars_sorted(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile from picarones.report.snapshot import normalization_snapshot p = get_builtin_profile("sans_ponctuation") s = normalization_snapshot(p) @@ -171,7 +171,7 @@ class TestSnapshotAll: assert s["schema_version"] == 1 def test_deterministic_for_same_inputs(self): - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile from picarones.report.snapshot import snapshot_all profile = get_builtin_profile("nfc") @@ -192,7 +192,7 @@ class TestSnapshotAll: def generated_report_html(tmp_path_factory) -> str: """Génère un rapport démo et retourne son contenu HTML.""" from picarones import fixtures - from picarones.core.normalization import get_builtin_profile + from picarones.measurements.normalization import get_builtin_profile from picarones.report.generator import ReportGenerator b = fixtures.generate_sample_benchmark(n_docs=6) diff --git a/tests/test_sprint28_ux_save_compare.py b/tests/test_sprint28_ux_save_compare.py index 33ffce92ea984280d98b617d45abde49e786f788..9a089fe0bfd1c9642879001e82d86d4c8b0375f7 100644 --- a/tests/test_sprint28_ux_save_compare.py +++ b/tests/test_sprint28_ux_save_compare.py @@ -261,7 +261,7 @@ class TestSynthesisPreviewEndpoint: def job_with_results(self, monkeypatch, tmp_path): """Crée un job 'complete' + JSON résultat sur disque.""" from picarones import fixtures - from picarones.core.jobs import get_default_store, reset_default_store + from picarones.web.jobs import get_default_store, reset_default_store from picarones.web import app as web_app # Isolate store monkeypatch.setenv("PICARONES_JOBS_DB", str(tmp_path / "jobs.db")) @@ -295,7 +295,7 @@ class TestSynthesisPreviewEndpoint: assert r.status_code == 404 def test_409_when_job_not_complete(self, monkeypatch, tmp_path): - from picarones.core.jobs import get_default_store, reset_default_store + from picarones.web.jobs import get_default_store, reset_default_store from picarones.web import app as web_app monkeypatch.setenv("PICARONES_JOBS_DB", str(tmp_path / "jobs.db")) reset_default_store() diff --git a/tests/test_sprint29_detector_registry.py b/tests/test_sprint29_detector_registry.py index 5b13846103250aa617268215efd457b2d6926695..81b081cd019e69e386e180fc301886e1262a16e5 100644 --- a/tests/test_sprint29_detector_registry.py +++ b/tests/test_sprint29_detector_registry.py @@ -28,13 +28,13 @@ from __future__ import annotations import pytest -from picarones.core.narrative import build_synthesis -from picarones.core.narrative.facts import ( +from picarones.measurements.narrative import build_synthesis +from picarones.core.facts import ( Fact, FactImportance, FactType, ) -from picarones.core.narrative.registry import ( +from picarones.measurements.narrative.registry import ( clear_registry, default_type_order, detector_for, @@ -68,7 +68,7 @@ class TestRegistryPopulatedAtImport: def test_priorities_match_historical_order(self): """Les priorités définies au Sprint 29 doivent reproduire l'ordre canonique pré-Sprint 29 pour ne pas casser la lecture du rapport.""" - from picarones.core.narrative.arbiter import _FALLBACK_TYPE_ORDER + from picarones.measurements.narrative.arbiter import _FALLBACK_TYPE_ORDER live = default_type_order() # Ils doivent contenir les mêmes types dans le même ordre. assert live == _FALLBACK_TYPE_ORDER @@ -225,7 +225,7 @@ class TestEmptyRegistryFallback: et ne pas planter.""" def test_select_facts_works_on_empty_registry(self): - from picarones.core.narrative.arbiter import select_facts + from picarones.measurements.narrative.arbiter import select_facts # Sauvegarder l'état complet pour le restaurer backup = list(iter_detectors()) try: @@ -256,7 +256,7 @@ class TestEmptyRegistryFallback: class TestLegacyAliasStillWorks: def test_detectors_by_type_matches_registry(self): - from picarones.core.narrative.detectors import DETECTORS_BY_TYPE + from picarones.measurements.narrative.detectors import DETECTORS_BY_TYPE registry_types = {e.fact_type for e in iter_detectors()} legacy_types = set(DETECTORS_BY_TYPE) # Les deux ensembles peuvent diverger si DETECTORS_BY_TYPE est diff --git a/tests/test_sprint34_metric_registry.py b/tests/test_sprint34_metric_registry.py index 42f5a8a024a6fb2775689a6edcfd54a53fd40360..58bfe104430cf8078cca8553d1b5089c7fa416cd 100644 --- a/tests/test_sprint34_metric_registry.py +++ b/tests/test_sprint34_metric_registry.py @@ -34,7 +34,7 @@ from picarones.core.modules import ArtifactType # tests s'exécutent avec ce registre peuplé ; on n'utilise pas # ``_reset_registry_for_tests`` parce qu'on veut justement tester l'état # par défaut visible par le runner en production. -import picarones.core.builtin_metrics # noqa: F401 +import picarones.measurements.builtin_metrics # noqa: F401 # ────────────────────────────────────────────────────────────────────────── @@ -171,7 +171,7 @@ class TestParityWithLegacy: ], ) def test_cer_matches_compute_metrics(self, ref: str, hyp: str) -> None: - from picarones.core.metrics import compute_metrics + from picarones.measurements.metrics import compute_metrics legacy = compute_metrics(ref, hyp) registered = compute_at_junction( diff --git a/tests/test_sprint35_inter_engine.py b/tests/test_sprint35_inter_engine.py index 8e0e9346c498a288e6e3b4ab6ab4bfe14279506b..1d10aec2bb0d9caf4eac5fb7d6ba6b04216c63df 100644 --- a/tests/test_sprint35_inter_engine.py +++ b/tests/test_sprint35_inter_engine.py @@ -1,6 +1,6 @@ """Tests Sprint 35 — métriques inter-moteurs (Étape 2 du plan). -Couvre les deux familles de mesures du module ``picarones.core.inter_engine`` : +Couvre les deux familles de mesures du module ``picarones.measurements.inter_engine`` : 1. **Divergence taxonomique** : KL et JS-divergence sur les distributions de classes d'erreur, plus la matrice triangulaire @@ -23,7 +23,7 @@ import math import pytest -from picarones.core.inter_engine import ( +from picarones.measurements.inter_engine import ( complementarity_gap, jensen_shannon_divergence, kl_divergence, @@ -187,7 +187,7 @@ class TestOracleTokenRecall: } assert oracle_token_recall(ref, hyps) == pytest.approx(1.0) # Et chacun seul ne fait que la moitié - from picarones.core.inter_engine import complementarity_gap + from picarones.measurements.inter_engine import complementarity_gap gap = complementarity_gap(ref, hyps) assert gap["best_single_recall"] == pytest.approx(0.5) assert gap["oracle_recall"] == pytest.approx(1.0) diff --git a/tests/test_sprint36_ensemble_narrative.py b/tests/test_sprint36_ensemble_narrative.py index 8afa26df845a27f8d2243d125dd71d080224927d..60e2a08beb42ecfee14eaca77c7f88e7c24a640a 100644 --- a/tests/test_sprint36_ensemble_narrative.py +++ b/tests/test_sprint36_ensemble_narrative.py @@ -22,10 +22,10 @@ import re import pytest -from picarones.core.inter_engine import compute_inter_engine_analysis -from picarones.core.narrative.detectors import detect_ensemble_opportunity -from picarones.core.narrative.facts import FactImportance, FactType -from picarones.core.narrative.renderer import extract_numbers, render_fact +from picarones.measurements.inter_engine import compute_inter_engine_analysis +from picarones.measurements.narrative.detectors import detect_ensemble_opportunity +from picarones.core.facts import FactImportance, FactType +from picarones.measurements.narrative.renderer import extract_numbers, render_fact # ────────────────────────────────────────────────────────────────────────── @@ -233,7 +233,7 @@ class TestEnsembleOpportunityDetector: class TestSynthesisIntegration: def test_detector_registered_by_default(self) -> None: - from picarones.core.narrative.registry import iter_detectors + from picarones.measurements.narrative.registry import iter_detectors types = {entry.fact_type for entry in iter_detectors()} assert FactType.ENSEMBLE_OPPORTUNITY in types @@ -241,7 +241,7 @@ class TestSynthesisIntegration: def test_synthesis_includes_ensemble_phrase(self) -> None: """Le détecteur s'active dans le pipeline complet et la phrase rendue contient bien les chiffres clés.""" - from picarones.core.narrative import build_synthesis + from picarones.measurements.narrative import build_synthesis # benchmark_data minimal qui n'active QUE notre détecteur (pas # de ranking, pas de stats — pour isoler). @@ -251,7 +251,7 @@ class TestSynthesisIntegration: assert any("voting" in s.lower() or "tess" in s for s in sentences) def test_synthesis_en_locale(self) -> None: - from picarones.core.narrative import build_synthesis + from picarones.measurements.narrative import build_synthesis data = _build_data(relative_gap=0.83) out = build_synthesis(data, lang="en", max_facts=5) @@ -301,7 +301,7 @@ class TestTraceability: def test_no_extraneous_numbers_in_template(self) -> None: """Le template lui-même ne contient pas de nombres en dur.""" - from picarones.core.narrative.renderer import _load_templates + from picarones.measurements.narrative.renderer import _load_templates tpl = _load_templates("fr").get("ensemble_opportunity", "") assert tpl diff --git a/tests/test_sprint38_ner_metrics.py b/tests/test_sprint38_ner_metrics.py index 30aae2bc1a80c414f66e1121ed7c205055558061..841bec73673b39b837f6cf8676d65f0454af341e 100644 --- a/tests/test_sprint38_ner_metrics.py +++ b/tests/test_sprint38_ner_metrics.py @@ -1,6 +1,6 @@ """Tests Sprint 38 — métriques NER (couche de calcul pure). -Le module ``picarones.core.ner`` expose : +Le module ``picarones.measurements.ner`` expose : - la dataclass ``Entity`` ; - ``compute_ner_metrics(ref, hyp, iou_threshold=0.5)`` qui aligne deux @@ -33,7 +33,7 @@ import pytest from picarones.core.metric_registry import compute_at_junction, select_metrics from picarones.core.modules import ArtifactType -from picarones.core.ner import Entity, compute_ner_metrics, ner_f1 +from picarones.measurements.ner import Entity, compute_ner_metrics, ner_f1 # ────────────────────────────────────────────────────────────────────────── @@ -255,7 +255,7 @@ class TestEntityValidation: class TestRegistryIntegration: def test_ner_f1_registered_for_entities_pair(self) -> None: # Force l'enregistrement - import picarones.core.ner # noqa: F401 + import picarones.measurements.ner # noqa: F401 selected = select_metrics( (ArtifactType.ENTITIES, ArtifactType.ENTITIES), @@ -264,7 +264,7 @@ class TestRegistryIntegration: assert "ner_f1" in names def test_compute_at_junction_uses_ner_f1(self) -> None: - import picarones.core.ner # noqa: F401 + import picarones.measurements.ner # noqa: F401 ref = [{"label": "PER", "start": 0, "end": 5, "text": "Marie"}] hyp = [{"label": "PER", "start": 0, "end": 5, "text": "Marie"}] diff --git a/tests/test_sprint39_calibration.py b/tests/test_sprint39_calibration.py index bc814da3e5b22184e06701ee9bff253fc1b9684c..6eb3fcde92e9d50caaf81e4d061ba4cc8a9959a0 100644 --- a/tests/test_sprint39_calibration.py +++ b/tests/test_sprint39_calibration.py @@ -1,6 +1,6 @@ """Tests Sprint 39 — métriques de calibration (ECE, MCE, reliability). -Le module ``picarones.core.calibration`` expose : +Le module ``picarones.measurements.calibration`` expose : - ``CalibrationBin`` : un bin du reliability diagram - ``reliability_diagram(confidences, is_correct, n_bins=10)`` @@ -34,7 +34,7 @@ from __future__ import annotations import pytest -from picarones.core.calibration import ( +from picarones.measurements.calibration import ( CalibrationBin, compute_calibration_metrics, expected_calibration_error, diff --git a/tests/test_sprint40_ner_runner.py b/tests/test_sprint40_ner_runner.py index 22ff9c5227b94f44b41229801e0793fa0b6333e7..6c9e2d1f0d8509490b099ce27015c9c865c7a653 100644 --- a/tests/test_sprint40_ner_runner.py +++ b/tests/test_sprint40_ner_runner.py @@ -27,14 +27,14 @@ from pathlib import Path import pytest from picarones.core.corpus import Corpus, Document, EntitiesGT, GTLevel, TextGT -from picarones.core.ner_backends import ( +from picarones.measurements.ner_backends import ( SPACY_PROFILES, SpacyEntityExtractor, get_extractor, is_spacy_available, ) from picarones.core.results import DocumentResult, EngineReport -from picarones.core.runner import _aggregate_ner, _attach_ner_metrics +from picarones.measurements.runner import _aggregate_ner, _attach_ner_metrics # ────────────────────────────────────────────────────────────────────────── @@ -49,7 +49,7 @@ class TestSpacyExtractor: """Sans spaCy installé, l'extracteur retourne [] avec un warning explicite et ne lève pas.""" ext = SpacyEntityExtractor("fr_core_news_sm") - with caplog.at_level("WARNING", logger="picarones.core.ner_backends"): + with caplog.at_level("WARNING", logger="picarones.measurements.ner_backends"): result = ext("Marie de Bourgogne en 1477.") # Sans spaCy, on a toujours [] et un warning if not is_spacy_available(): @@ -97,7 +97,7 @@ def _make_document_result( hypothesis: str = "Marie de Bourgogne en 1477.", ner_metrics: dict | None = None, ) -> DocumentResult: - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult return DocumentResult( doc_id=doc_id, @@ -291,7 +291,7 @@ class TestRobustness: dr1 = _make_document_result( doc_id="doc1", hypothesis="Marie de Bourgogne en 1477.", ) - with caplog.at_level("WARNING", logger="picarones.core.runner"): + with caplog.at_level("WARNING", logger="picarones.measurements.runner"): _attach_ner_metrics(corpus, [dr1], _broken_extractor) # Pas de propagation, ner_metrics reste None diff --git a/tests/test_sprint42_calibration_runner.py b/tests/test_sprint42_calibration_runner.py index 8aab14bdbb12409641780c4021cee43d8419bec3..beff814b238eddb560b533989e8e9f00e9fc0089 100644 --- a/tests/test_sprint42_calibration_runner.py +++ b/tests/test_sprint42_calibration_runner.py @@ -29,7 +29,7 @@ from __future__ import annotations import pytest -from picarones.core.runner import ( +from picarones.measurements.runner import ( _aggregate_calibration, _calibration_from_engine_result, ) @@ -59,7 +59,7 @@ class TestEngineResultExtension: def _make_dr(calibration_metrics: dict | None = None) -> DocumentResult: - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult return DocumentResult( doc_id="d1", image_path="/tmp/x.png", @@ -243,7 +243,7 @@ class TestBackwardCompat: def test_engine_result_default_no_calibration(self) -> None: # Un EngineResult sans token_confidences → calibration_metrics # ne doit pas être calculée. - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result ocr = EngineResult( engine_name="e", image_path="/tmp/x.png", @@ -260,7 +260,7 @@ class TestBackwardCompat: assert dr.calibration_metrics is None def test_engine_result_with_confs_triggers_calibration(self) -> None: - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result ocr = EngineResult( engine_name="e", image_path="/tmp/x.png", diff --git a/tests/test_sprint44_median_default.py b/tests/test_sprint44_median_default.py index 1b9f174db358f3116f9c8c6793cd77bf5320b008..29902bc8fa2622907aa80b0632872b68f0ce4ff3 100644 --- a/tests/test_sprint44_median_default.py +++ b/tests/test_sprint44_median_default.py @@ -23,10 +23,10 @@ import re import pytest -from picarones.core.metrics import MetricsResult -from picarones.core.narrative.detectors import detect_median_mean_gap_warning -from picarones.core.narrative.facts import FactImportance, FactType -from picarones.core.narrative.renderer import extract_numbers, render_fact +from picarones.measurements.metrics import MetricsResult +from picarones.measurements.narrative.detectors import detect_median_mean_gap_warning +from picarones.core.facts import FactImportance, FactType +from picarones.measurements.narrative.renderer import extract_numbers, render_fact from picarones.core.results import BenchmarkResult, DocumentResult, EngineReport @@ -225,7 +225,7 @@ class TestTraceability: ) def test_template_has_no_hardcoded_numbers(self) -> None: - from picarones.core.narrative.renderer import _load_templates + from picarones.measurements.narrative.renderer import _load_templates for lang in ("fr", "en"): tpl = _load_templates(lang).get("median_mean_gap_warning", "") assert tpl, f"Template absent pour {lang}" @@ -242,12 +242,12 @@ class TestTraceability: class TestSynthesisIntegration: def test_detector_registered_by_default(self) -> None: - from picarones.core.narrative.registry import iter_detectors + from picarones.measurements.narrative.registry import iter_detectors types = {entry.fact_type for entry in iter_detectors()} assert FactType.MEDIAN_MEAN_GAP_WARNING in types def test_synthesis_includes_warning_when_asymmetric(self) -> None: - from picarones.core.narrative import build_synthesis + from picarones.measurements.narrative import build_synthesis data = {"ranking": [{ "engine": "tess", "median_cer": 0.03, "mean_cer": 0.07, "documents": 100, diff --git a/tests/test_sprint45_stratification.py b/tests/test_sprint45_stratification.py index 7f64c7b7e1f8ddaacdba7f914f122d67060e74a0..84b58928f42e39a31680c890b08ffaff43b07929 100644 --- a/tests/test_sprint45_stratification.py +++ b/tests/test_sprint45_stratification.py @@ -26,7 +26,7 @@ from __future__ import annotations import pytest -from picarones.core.metrics import MetricsResult +from picarones.measurements.metrics import MetricsResult from picarones.core.results import BenchmarkResult, DocumentResult, EngineReport diff --git a/tests/test_sprint46_stratification_html.py b/tests/test_sprint46_stratification_html.py index a3922f4b9aab51cb2b378ebfee4361d23f37c496..9605de042879486c486e82a6645c606a78b6950a 100644 --- a/tests/test_sprint46_stratification_html.py +++ b/tests/test_sprint46_stratification_html.py @@ -26,10 +26,10 @@ from pathlib import Path import pytest -from picarones.core.metrics import MetricsResult -from picarones.core.narrative.detectors import detect_stratification_recommended -from picarones.core.narrative.facts import FactImportance, FactType -from picarones.core.narrative.renderer import extract_numbers, render_fact +from picarones.measurements.metrics import MetricsResult +from picarones.measurements.narrative.detectors import detect_stratification_recommended +from picarones.core.facts import FactImportance, FactType +from picarones.measurements.narrative.renderer import extract_numbers, render_fact from picarones.core.results import DocumentResult from picarones.report.generator import ReportGenerator from picarones.report.stratification_render import build_stratified_ranking_html @@ -260,7 +260,7 @@ class TestTraceability: ) def test_template_has_no_hardcoded_numbers(self) -> None: - from picarones.core.narrative.renderer import _load_templates + from picarones.measurements.narrative.renderer import _load_templates for lang in ("fr", "en"): tpl = _load_templates(lang).get("stratification_recommended", "") assert tpl, f"Template absent pour {lang}" diff --git a/tests/test_sprint47_tesseract_confidences.py b/tests/test_sprint47_tesseract_confidences.py index 8e0d470d2bb9682ce136274e508aa9807b7efe8d..00304ccc158b82f55e4294762a3a0968ea19c524 100644 --- a/tests/test_sprint47_tesseract_confidences.py +++ b/tests/test_sprint47_tesseract_confidences.py @@ -249,7 +249,7 @@ class TestEndToEndWithRunner: def test_runner_picks_up_confidences_and_computes_calibration( self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path, ) -> None: - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult # Simulation : on appelle directement _compute_document_result @@ -289,5 +289,5 @@ class TestPytesseractAbsent: monkeypatch.setattr(tesseract_module, "_PYTESSERACT_AVAILABLE", False) engine = TesseractEngine() - result = engine._extract_token_confidences(tmp_path / "p.png") + result = engine.run(tmp_path / "p.png").token_confidences assert result is None diff --git a/tests/test_sprint48_pero_confidences.py b/tests/test_sprint48_pero_confidences.py index ed4157036ba2361969281399056b702bdfd18934..e96acef6db8ee520dd123306874060443c683250 100644 --- a/tests/test_sprint48_pero_confidences.py +++ b/tests/test_sprint48_pero_confidences.py @@ -67,7 +67,7 @@ class TestExtractFromLayout: _mock_line("Bonjour le monde", 0.92), ]), ]) - out = engine._extract_token_confidences_from_layout(layout) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(layout)) assert out is not None assert out == [ {"token": "Bonjour", "confidence": 0.92}, @@ -83,7 +83,7 @@ class TestExtractFromLayout: _mock_line("Deuxième ligne", 0.80), ]), ]) - out = engine._extract_token_confidences_from_layout(layout) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(layout)) assert out is not None # Chaque mot porte la confidence de SA ligne assert {"token": "Première", "confidence": 0.95} in out @@ -98,7 +98,7 @@ class TestExtractFromLayout: _mock_line("ok", 0.95), # ok ]), ]) - out = engine._extract_token_confidences_from_layout(layout) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(layout)) assert out == [{"token": "ok", "confidence": 0.95}] def test_skips_none_confidence(self) -> None: @@ -109,7 +109,7 @@ class TestExtractFromLayout: _mock_line("sans_conf", None), ]), ]) - out = engine._extract_token_confidences_from_layout(layout) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(layout)) assert out == [{"token": "avec_conf", "confidence": 0.85}] def test_skips_negative_confidence(self) -> None: @@ -120,7 +120,7 @@ class TestExtractFromLayout: _mock_line("dropped", -0.1), ]), ]) - out = engine._extract_token_confidences_from_layout(layout) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(layout)) assert out == [{"token": "ok", "confidence": 0.9}] @@ -135,7 +135,7 @@ class TestExposeFlag: layout = _mock_layout([ _mock_region([_mock_line("hello", 0.9)]), ]) - assert engine._extract_token_confidences_from_layout(layout) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(layout)) is None # ────────────────────────────────────────────────────────────────────────── @@ -146,12 +146,12 @@ class TestExposeFlag: class TestDegenerateLayouts: def test_none_layout(self) -> None: engine = PeroOCREngine() - assert engine._extract_token_confidences_from_layout(None) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(None)) is None def test_empty_regions(self) -> None: engine = PeroOCREngine() layout = _mock_layout([]) - assert engine._extract_token_confidences_from_layout(layout) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(layout)) is None def test_only_lines_without_conf_returns_none(self) -> None: engine = PeroOCREngine() @@ -161,7 +161,7 @@ class TestDegenerateLayouts: _mock_line("ok2", None), ]), ]) - assert engine._extract_token_confidences_from_layout(layout) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(layout)) is None # ────────────────────────────────────────────────────────────────────────── @@ -242,7 +242,7 @@ class TestRunPipeline: class TestEndToEndWithRunner: def test_runner_picks_up_confidences(self) -> None: - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult ocr = EngineResult( diff --git a/tests/test_sprint49_mistral_confidences.py b/tests/test_sprint49_mistral_confidences.py index 325ab80a28915d3784132f826ffacf014d2196d8..c657b1ac7e6140d899238db85eb64a93cab997e6 100644 --- a/tests/test_sprint49_mistral_confidences.py +++ b/tests/test_sprint49_mistral_confidences.py @@ -42,7 +42,7 @@ class TestExtractFromResponse: ], }], } - out = engine._extract_token_confidences_from_response(response) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(response)) assert out == [ {"token": "Bonjour", "confidence": 0.95}, {"token": "monde", "confidence": 0.90}, @@ -58,7 +58,7 @@ class TestExtractFromResponse: ], }], } - out = engine._extract_token_confidences_from_response(response) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(response)) assert out is not None # 3 tokens (2 mots + 1 mot), avec leurs confidences respectives assert {"token": "première", "confidence": 0.88} in out @@ -74,7 +74,7 @@ class TestExtractFromResponse: ], }], } - out = engine._extract_token_confidences_from_response(response) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(response)) assert out == [ {"token": "bloc1", "confidence": 0.82}, {"token": "mot2", "confidence": 0.82}, @@ -90,7 +90,7 @@ class TestExtractFromResponse: ], }], } - out = engine._extract_token_confidences_from_response(response) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(response)) assert out == [{"token": "ok", "confidence": 0.9}] def test_skips_none_confidence(self) -> None: @@ -104,7 +104,7 @@ class TestExtractFromResponse: ], }], } - out = engine._extract_token_confidences_from_response(response) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(response)) assert out == [{"token": "avec_conf", "confidence": 0.85}] def test_skips_negative_confidence(self) -> None: @@ -117,7 +117,7 @@ class TestExtractFromResponse: ], }], } - out = engine._extract_token_confidences_from_response(response) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(response)) assert out == [{"token": "ok", "confidence": 0.9}] def test_combines_words_and_lines(self) -> None: @@ -128,7 +128,7 @@ class TestExtractFromResponse: "lines": [{"text": "ligne mots", "confidence": 0.7}], }], } - out = engine._extract_token_confidences_from_response(response) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(response)) assert out is not None assert len(out) == 3 # 1 word explicit + 2 mots de la ligne @@ -141,17 +141,17 @@ class TestExtractFromResponse: class TestDegenerateResponses: def test_none_response(self) -> None: engine = MistralOCREngine() - assert engine._extract_token_confidences_from_response(None) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(None)) is None def test_empty_dict(self) -> None: engine = MistralOCREngine() - assert engine._extract_token_confidences_from_response({}) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences({})) is None def test_no_pages(self) -> None: engine = MistralOCREngine() - assert engine._extract_token_confidences_from_response( + assert engine._normalize_token_confidences(engine._extract_raw_confidences( {"pages": []}, - ) is None + )) is None def test_pages_without_confidences(self) -> None: engine = MistralOCREngine() @@ -160,12 +160,12 @@ class TestDegenerateResponses: {"markdown": "Texte sans annotation de confidence"}, ], } - assert engine._extract_token_confidences_from_response(response) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(response)) is None def test_non_dict_input(self) -> None: engine = MistralOCREngine() - assert engine._extract_token_confidences_from_response("not a dict") is None - assert engine._extract_token_confidences_from_response([1, 2, 3]) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences("not a dict")) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences([1, 2, 3])) is None # ────────────────────────────────────────────────────────────────────────── @@ -181,7 +181,7 @@ class TestExposeFlag: "words": [{"text": "ok", "confidence": 0.9}], }], } - assert engine._extract_token_confidences_from_response(response) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(response)) is None # ────────────────────────────────────────────────────────────────────────── @@ -207,7 +207,7 @@ def _mock_run_with_response( return text, raw_response monkeypatch.setattr( - MistralOCREngine, "_run_ocr_with_response", _fake, + MistralOCREngine, "_run_with_native", _fake, ) return engine @@ -274,7 +274,7 @@ class TestRunOverride: class TestEndToEndWithRunner: def test_runner_picks_up_mistral_confidences(self) -> None: - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult ocr = EngineResult( diff --git a/tests/test_sprint4_normalization_iiif.py b/tests/test_sprint4_normalization_iiif.py index 4d8001e9ee3d5b5684cdb9807852a73a0a4772a9..016cbc9a9cda3f7c3e4a93d0a138b8d02d07ee51 100644 --- a/tests/test_sprint4_normalization_iiif.py +++ b/tests/test_sprint4_normalization_iiif.py @@ -4,14 +4,14 @@ from __future__ import annotations import pytest -from picarones.core.normalization import ( +from picarones.measurements.normalization import ( NormalizationProfile, DEFAULT_DIPLOMATIC_PROFILE, _apply_diplomatic_table, get_builtin_profile, ) -from picarones.core.metrics import compute_metrics, aggregate_metrics, MetricsResult -from picarones.importers.iiif import ( +from picarones.measurements.metrics import compute_metrics, aggregate_metrics, MetricsResult +from picarones.extras.importers.iiif import ( IIIFManifestParser, parse_page_selector, _extract_label, @@ -205,7 +205,7 @@ class TestDiplomaticCER: assert "diplomatic_profile_name" in d def test_cer_diplomatic_with_custom_profile(self): - from picarones.core.normalization import NormalizationProfile + from picarones.measurements.normalization import NormalizationProfile profile = NormalizationProfile( name="test_profile", diplomatic_table={"ſ": "s"} diff --git a/tests/test_sprint50_google_vision_confidences.py b/tests/test_sprint50_google_vision_confidences.py index cbefd7b3fbc0818ce20c50e7d256e141cfc0960c..79742af78f64a87da712524b956962ed285a5a64 100644 --- a/tests/test_sprint50_google_vision_confidences.py +++ b/tests/test_sprint50_google_vision_confidences.py @@ -65,7 +65,7 @@ class TestExtractFromFullText: def test_reconstructs_word_from_symbols(self) -> None: engine = GoogleVisionEngine() full = _full_text([_word("Bonjour", 0.95)]) - assert engine._extract_token_confidences_from_full_text(full) == [ + assert engine._normalize_token_confidences(engine._extract_raw_confidences(full)) == [ {"token": "Bonjour", "confidence": 0.95}, ] @@ -75,7 +75,7 @@ class TestExtractFromFullText: _word("Bonjour", 0.95), _word("monde", 0.88), ]) - out = engine._extract_token_confidences_from_full_text(full) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(full)) assert out == [ {"token": "Bonjour", "confidence": 0.95}, {"token": "monde", "confidence": 0.88}, @@ -88,7 +88,7 @@ class TestExtractFromFullText: {"symbols": [{"text": "nope"}]}, # pas de confidence {"confidence": None, "symbols": [{"text": "nope"}]}, # None ]) - out = engine._extract_token_confidences_from_full_text(full) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(full)) assert out == [{"token": "ok", "confidence": 0.95}] def test_skips_negative_confidence(self) -> None: @@ -97,7 +97,7 @@ class TestExtractFromFullText: _word("ok", 0.9), _word("dropped", -0.1), ]) - out = engine._extract_token_confidences_from_full_text(full) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(full)) assert out == [{"token": "ok", "confidence": 0.9}] def test_skips_empty_text(self) -> None: @@ -106,7 +106,7 @@ class TestExtractFromFullText: _word("", 0.95), _word("ok", 0.9), ]) - out = engine._extract_token_confidences_from_full_text(full) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(full)) assert out == [{"token": "ok", "confidence": 0.9}] def test_traverses_multiple_pages_and_blocks(self) -> None: @@ -122,7 +122,7 @@ class TestExtractFromFullText: ]}, ], } - out = engine._extract_token_confidences_from_full_text(full) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(full)) assert out is not None tokens = [tc["token"] for tc in out] assert tokens == ["alpha", "beta", "gamma"] @@ -137,7 +137,7 @@ class TestExposeFlag: def test_disabled_returns_none(self) -> None: engine = GoogleVisionEngine(config={"expose_confidences": False}) full = _full_text([_word("ok", 0.95)]) - assert engine._extract_token_confidences_from_full_text(full) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(full)) is None # ────────────────────────────────────────────────────────────────────────── @@ -148,23 +148,23 @@ class TestExposeFlag: class TestDegenerateInputs: def test_none(self) -> None: engine = GoogleVisionEngine() - assert engine._extract_token_confidences_from_full_text(None) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(None)) is None def test_empty_dict(self) -> None: engine = GoogleVisionEngine() - assert engine._extract_token_confidences_from_full_text({}) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences({})) is None def test_no_pages(self) -> None: engine = GoogleVisionEngine() - assert engine._extract_token_confidences_from_full_text( + assert engine._normalize_token_confidences(engine._extract_raw_confidences( {"pages": []}, - ) is None + )) is None def test_pages_without_blocks(self) -> None: engine = GoogleVisionEngine() - assert engine._extract_token_confidences_from_full_text( + assert engine._normalize_token_confidences(engine._extract_raw_confidences( {"pages": [{"text": "raw text only"}]}, - ) is None + )) is None # ────────────────────────────────────────────────────────────────────────── @@ -223,7 +223,7 @@ def _patch_run_with_full( return text, full monkeypatch.setattr( - GoogleVisionEngine, "_run_ocr_with_full_annotation", _fake, + GoogleVisionEngine, "_run_with_native", _fake, ) return engine @@ -320,7 +320,7 @@ class TestRESTPath: assert "pages" in full # L'extraction passe ensuite normalement - out = engine._extract_token_confidences_from_full_text(full) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(full)) assert out == [{"token": "Bonjour", "confidence": 0.97}] @@ -331,7 +331,7 @@ class TestRESTPath: class TestEndToEndWithRunner: def test_runner_picks_up_google_vision_confidences(self) -> None: - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult ocr = EngineResult( diff --git a/tests/test_sprint51_azure_confidences.py b/tests/test_sprint51_azure_confidences.py index 357a190f12725bcf5643d8932b552245f4280295..d09f45c7bfdb87465d02806c0702c89bdacda32f 100644 --- a/tests/test_sprint51_azure_confidences.py +++ b/tests/test_sprint51_azure_confidences.py @@ -52,7 +52,7 @@ class TestExtractFromResult: _word("Bonjour", 0.97), _word("monde", 0.93), ]) - out = engine._extract_token_confidences_from_result(result) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(result)) assert out == [ {"token": "Bonjour", "confidence": 0.97}, {"token": "monde", "confidence": 0.93}, @@ -65,7 +65,7 @@ class TestExtractFromResult: {"content": "no_conf"}, # pas de confidence _word("none_conf", None), ]) - out = engine._extract_token_confidences_from_result(result) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(result)) assert out == [{"token": "ok", "confidence": 0.95}] def test_skips_negative_confidence(self) -> None: @@ -74,7 +74,7 @@ class TestExtractFromResult: _word("ok", 0.9), _word("dropped", -0.1), ]) - out = engine._extract_token_confidences_from_result(result) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(result)) assert out == [{"token": "ok", "confidence": 0.9}] def test_skips_empty_content(self) -> None: @@ -83,7 +83,7 @@ class TestExtractFromResult: _word("", 0.95), _word("ok", 0.9), ]) - out = engine._extract_token_confidences_from_result(result) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(result)) assert out == [{"token": "ok", "confidence": 0.9}] def test_traverses_multiple_pages(self) -> None: @@ -94,7 +94,7 @@ class TestExtractFromResult: {"words": [_word("gamma", 0.8)]}, ], } - out = engine._extract_token_confidences_from_result(result) + out = engine._normalize_token_confidences(engine._extract_raw_confidences(result)) assert [tc["token"] for tc in (out or [])] == ["alpha", "beta", "gamma"] @@ -106,8 +106,8 @@ class TestExtractFromResult: class TestExposeFlag: def test_disabled_returns_none(self) -> None: engine = AzureDocIntelEngine(config={"expose_confidences": False}) - assert engine._extract_token_confidences_from_result( - _result([_word("ok", 0.9)]), + assert engine._normalize_token_confidences( + engine._extract_raw_confidences(_result([_word("ok", 0.9)])), ) is None @@ -119,23 +119,23 @@ class TestExposeFlag: class TestDegenerateInputs: def test_none(self) -> None: engine = AzureDocIntelEngine() - assert engine._extract_token_confidences_from_result(None) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences(None)) is None def test_empty_dict(self) -> None: engine = AzureDocIntelEngine() - assert engine._extract_token_confidences_from_result({}) is None + assert engine._normalize_token_confidences(engine._extract_raw_confidences({})) is None def test_no_pages(self) -> None: engine = AzureDocIntelEngine() - assert engine._extract_token_confidences_from_result( + assert engine._normalize_token_confidences(engine._extract_raw_confidences( {"pages": []}, - ) is None + )) is None def test_pages_without_words(self) -> None: engine = AzureDocIntelEngine() - assert engine._extract_token_confidences_from_result( + assert engine._normalize_token_confidences(engine._extract_raw_confidences( {"pages": [{"lines": [{"content": "no words"}]}]}, - ) is None + )) is None # ────────────────────────────────────────────────────────────────────────── @@ -194,7 +194,7 @@ def _patch_run_with_result( return text, analyze_result monkeypatch.setattr( - AzureDocIntelEngine, "_run_ocr_with_result", _fake, + AzureDocIntelEngine, "_run_with_native", _fake, ) return engine @@ -252,7 +252,7 @@ class TestRunOverride: class TestEndToEndWithRunner: def test_runner_picks_up_azure_confidences(self) -> None: - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult ocr = EngineResult( diff --git a/tests/test_sprint52_readability.py b/tests/test_sprint52_readability.py index 27dafdb9a3a6ffdae33375c5f7190b2c2a647431..effba6db03029bec9f87e37739f6fe511f093d51 100644 --- a/tests/test_sprint52_readability.py +++ b/tests/test_sprint52_readability.py @@ -30,7 +30,7 @@ import pytest from picarones.core.metric_registry import select_metrics from picarones.core.modules import ArtifactType -from picarones.core.readability import ( +from picarones.measurements.readability import ( count_sentences, count_syllables, count_syllables_word, @@ -226,7 +226,7 @@ class TestFleschDelta: class TestRegistryIntegration: def test_flesch_metrics_registered_for_text_text(self) -> None: # Force l'import qui peuple le registre - import picarones.core.readability # noqa: F401 + import picarones.measurements.readability # noqa: F401 selected = select_metrics( (ArtifactType.TEXT, ArtifactType.TEXT), diff --git a/tests/test_sprint53_reading_order.py b/tests/test_sprint53_reading_order.py index 4426a0a04170a0a96f7f1cd1ba566c0db3f467e5..72e1d18bdca80ac65c3473bef8a0cf4bfa62e672 100644 --- a/tests/test_sprint53_reading_order.py +++ b/tests/test_sprint53_reading_order.py @@ -28,7 +28,7 @@ import pytest from picarones.core.metric_registry import compute_at_junction, select_metrics from picarones.core.modules import ArtifactType -from picarones.core.reading_order import ( +from picarones.measurements.reading_order import ( compute_reading_order_metrics, reading_order_f1, ) @@ -184,7 +184,7 @@ class TestDetailedCounts: class TestRegistryIntegration: def test_metric_registered_for_reading_order_pair(self) -> None: # Force l'import qui peuple le registre - import picarones.core.reading_order # noqa: F401 + import picarones.measurements.reading_order # noqa: F401 selected = select_metrics( (ArtifactType.READING_ORDER, ArtifactType.READING_ORDER), diff --git a/tests/test_sprint54_layout.py b/tests/test_sprint54_layout.py index 13b5c0bf4aceb2dc69f81b9af88a651e8ab65ef4..f7afb4f1da430f9148dedbcc6ad07716fb194c6c 100644 --- a/tests/test_sprint54_layout.py +++ b/tests/test_sprint54_layout.py @@ -22,7 +22,7 @@ from __future__ import annotations import pytest -from picarones.core.layout import ( +from picarones.measurements.layout import ( Region, _iou_bbox, compute_layout_metrics, diff --git a/tests/test_sprint55_unicode_blocks.py b/tests/test_sprint55_unicode_blocks.py index 848cd526b4ec5cf2b50d05a5e1e5777b79ae7f23..14d5b0c7a7dc0c72b19ddc8f398d4fc357d32484 100644 --- a/tests/test_sprint55_unicode_blocks.py +++ b/tests/test_sprint55_unicode_blocks.py @@ -25,7 +25,7 @@ import pytest from picarones.core.metric_registry import compute_at_junction, select_metrics from picarones.core.modules import ArtifactType -from picarones.core.unicode_blocks import ( +from picarones.measurements.unicode_blocks import ( compute_unicode_block_accuracy, get_block, unicode_block_global_accuracy, @@ -187,7 +187,7 @@ class TestShortcut: class TestRegistryIntegration: def test_metric_registered_for_text_text(self) -> None: # Force l'import qui peuple le registre - import picarones.core.unicode_blocks # noqa: F401 + import picarones.measurements.unicode_blocks # noqa: F401 selected = select_metrics( (ArtifactType.TEXT, ArtifactType.TEXT), diff --git a/tests/test_sprint56_abbreviations.py b/tests/test_sprint56_abbreviations.py index 6b4993c684a5d5c9ab8fececb4b4f80d6d3a32ae..10e14cfe602cdc0826d72243ae5543afeea279a0 100644 --- a/tests/test_sprint56_abbreviations.py +++ b/tests/test_sprint56_abbreviations.py @@ -27,7 +27,7 @@ from __future__ import annotations import pytest -from picarones.core.abbreviations import ( +from picarones.measurements.abbreviations import ( ABBREVIATION_EXPANSIONS, abbreviation_expansion_score, abbreviation_strict_score, @@ -205,7 +205,7 @@ class TestShortcuts: class TestRegistryIntegration: def test_metrics_registered_for_text_text(self) -> None: # Force l'import qui peuple le registre - import picarones.core.abbreviations # noqa: F401 + import picarones.measurements.abbreviations # noqa: F401 selected = select_metrics( (ArtifactType.TEXT, ArtifactType.TEXT), diff --git a/tests/test_sprint57_mufi.py b/tests/test_sprint57_mufi.py index becae6f72894e0db2288d7b2874fd64d2346f4be..c5c5aa63de92b8c00ba4cb8ad6a7ba7a257d6ff2 100644 --- a/tests/test_sprint57_mufi.py +++ b/tests/test_sprint57_mufi.py @@ -33,7 +33,7 @@ import pytest from picarones.core.metric_registry import compute_at_junction, select_metrics from picarones.core.modules import ArtifactType -from picarones.core.mufi import ( +from picarones.measurements.mufi import ( compute_mufi_coverage, is_mufi_char, mufi_coverage, @@ -203,7 +203,7 @@ class TestShortcut: class TestRegistryIntegration: def test_metric_registered_for_text_text(self) -> None: # Force l'import qui peuple le registre - import picarones.core.mufi # noqa: F401 + import picarones.measurements.mufi # noqa: F401 selected = select_metrics( (ArtifactType.TEXT, ArtifactType.TEXT), diff --git a/tests/test_sprint58_early_modern.py b/tests/test_sprint58_early_modern.py index 96d5701a8aa6c0fbc3c4b0c788acf7d319b728b6..c806e32bcb338583ec2bc3ef3df6ea55ef59b762 100644 --- a/tests/test_sprint58_early_modern.py +++ b/tests/test_sprint58_early_modern.py @@ -27,7 +27,7 @@ from __future__ import annotations import pytest -from picarones.core.early_modern_typography import ( +from picarones.measurements.early_modern_typography import ( AMPERSAND, DOTLESS_I, LIGATURES, @@ -271,7 +271,7 @@ class TestShortcut: class TestRegistryIntegration: def test_metric_registered(self) -> None: # Force l'import qui peuple le registre - import picarones.core.early_modern_typography # noqa: F401 + import picarones.measurements.early_modern_typography # noqa: F401 selected = select_metrics( (ArtifactType.TEXT, ArtifactType.TEXT), diff --git a/tests/test_sprint59_modern_archives.py b/tests/test_sprint59_modern_archives.py index 45c0efca71be559510e5f0ffdb850c87bca3cf0f..492956fa7547568be1988c38f2bf28d92b1a1d97 100644 --- a/tests/test_sprint59_modern_archives.py +++ b/tests/test_sprint59_modern_archives.py @@ -36,7 +36,7 @@ from __future__ import annotations import pytest from picarones.core.metric_registry import compute_at_junction, select_metrics -from picarones.core.modern_archives import ( +from picarones.measurements.modern_archives import ( ADDRESS, ADMINISTRATIVE, BIBLIOGRAPHIC, @@ -497,7 +497,7 @@ class TestShortcuts: class TestRegistryIntegration: def test_strict_metric_registered(self) -> None: - import picarones.core.modern_archives # noqa: F401 + import picarones.measurements.modern_archives # noqa: F401 selected = select_metrics( (ArtifactType.TEXT, ArtifactType.TEXT), diff --git a/tests/test_sprint5_advanced_metrics.py b/tests/test_sprint5_advanced_metrics.py index a50814d43587ed0fb8c65c4e6c7ce19f8ae12daa..4c1b7b2d4fa910567eaa0cc4c81ac5e900e483ba 100644 --- a/tests/test_sprint5_advanced_metrics.py +++ b/tests/test_sprint5_advanced_metrics.py @@ -17,7 +17,7 @@ import pytest # Tests ConfusionMatrix # =========================================================================== -from picarones.core.confusion import ( +from picarones.measurements.confusion import ( EMPTY_CHAR, build_confusion_matrix, aggregate_confusion_matrices, @@ -146,7 +146,7 @@ class TestTopConfusedChars: # Tests LigatureScore # =========================================================================== -from picarones.core.char_scores import ( +from picarones.measurements.char_scores import ( LIGATURE_TABLE, LigatureScore, DiacriticScore, @@ -288,7 +288,7 @@ class TestAggregateDiacriticScores: # Tests TaxonomyResult # =========================================================================== -from picarones.core.taxonomy import ( +from picarones.measurements.taxonomy import ( TaxonomyResult, ERROR_CLASSES, classify_errors, @@ -395,7 +395,7 @@ class TestAggregateTaxonomy: # Tests StructureResult # =========================================================================== -from picarones.core.structure import ( +from picarones.measurements.structure import ( StructureResult, analyze_structure, aggregate_structure, @@ -504,7 +504,7 @@ class TestAggregateStructure: # Tests ImageQualityResult # =========================================================================== -from picarones.core.image_quality import ( +from picarones.measurements.image_quality import ( ImageQualityResult, generate_mock_quality_scores, aggregate_image_quality, diff --git a/tests/test_sprint60_roman_numerals.py b/tests/test_sprint60_roman_numerals.py index 487b1db34df4d85bf282096d4f643f4694f642be..cc5b91aaafbcbbe72e7e15f193d11b0fa4445717 100644 --- a/tests/test_sprint60_roman_numerals.py +++ b/tests/test_sprint60_roman_numerals.py @@ -23,7 +23,7 @@ import pytest from picarones.core.metric_registry import compute_at_junction, select_metrics from picarones.core.modules import ArtifactType -from picarones.core.roman_numerals import ( +from picarones.measurements.roman_numerals import ( ALL_STATUSES, STATUS_CASE_CHANGED, STATUS_CONVERTED_TO_ARABIC, @@ -385,7 +385,7 @@ class TestShortcuts: class TestRegistryIntegration: def test_metrics_registered(self) -> None: - import picarones.core.roman_numerals # noqa: F401 + import picarones.measurements.roman_numerals # noqa: F401 selected = select_metrics( (ArtifactType.TEXT, ArtifactType.TEXT), diff --git a/tests/test_sprint61_philological_runner.py b/tests/test_sprint61_philological_runner.py index 9828f04f99193c7c1f78c13e499fb3eb516f72d5..73e9f794de06cc58bfc0dd1c3f489615d2148fde 100644 --- a/tests/test_sprint61_philological_runner.py +++ b/tests/test_sprint61_philological_runner.py @@ -24,12 +24,12 @@ Couvre : from __future__ import annotations -from picarones.core.philological_runner import ( +from picarones.measurements.philological_runner import ( aggregate_philological_metrics, compute_philological_metrics, ) from picarones.core.results import DocumentResult, EngineReport -from picarones.core.metrics import MetricsResult +from picarones.measurements.metrics import MetricsResult def _make_doc( @@ -255,7 +255,7 @@ class TestRunnerIntegration: ``philological_metrics`` quand la GT a du signal.""" def test_runner_attaches_philological(self, tmp_path) -> None: - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult # Créer une image fictive (le module image_quality échouera @@ -280,7 +280,7 @@ class TestRunnerIntegration: assert "roman_numerals" in dr.philological_metrics def test_runner_omits_philological_on_plain_text(self, tmp_path) -> None: - from picarones.core.runner import _compute_document_result + from picarones.measurements.runner import _compute_document_result from picarones.engines.base import EngineResult img = tmp_path / "doc.png" diff --git a/tests/test_sprint63_pipeline_runner.py b/tests/test_sprint63_pipeline_runner.py index 167869b87dca88a92b6db87aedd2378d508046a8..3eba51d4ae8986474b67390a7a45ed27caaa18f9 100644 --- a/tests/test_sprint63_pipeline_runner.py +++ b/tests/test_sprint63_pipeline_runner.py @@ -28,7 +28,7 @@ from typing import Any from picarones.core.corpus import Document, GTLevel, TextGT from picarones.core.modules import ArtifactType, BaseModule -from picarones.core.pipeline_runner import ( +from picarones.core.pipeline import ( PipelineResult, PipelineRunner, PipelineSpec, diff --git a/tests/test_sprint64_pipeline_benchmark.py b/tests/test_sprint64_pipeline_benchmark.py index 05dba05412e672032b1cf50717db43ef293f4fe3..03cebd0b50f9c5136a603659b14c7f658b063eef 100644 --- a/tests/test_sprint64_pipeline_benchmark.py +++ b/tests/test_sprint64_pipeline_benchmark.py @@ -31,13 +31,13 @@ from typing import Any from picarones.core.corpus import Corpus, Document, GTLevel, TextGT from picarones.core.modules import ArtifactType, BaseModule -from picarones.core.pipeline_benchmark import ( +from picarones.measurements.pipeline_benchmark import ( PipelineBenchmarkResult, StepAggregate, default_initial_inputs, run_pipeline_benchmark, ) -from picarones.core.pipeline_runner import PipelineSpec, PipelineStep +from picarones.core.pipeline import PipelineSpec, PipelineStep # ────────────────────────────────────────────────────────────────────────── diff --git a/tests/test_sprint65_pipeline_comparison.py b/tests/test_sprint65_pipeline_comparison.py index 546cf56171251a055e768b8fd44f742b33ed0d03..e39ee8c09f0bc5a12c6a91bdf60fcc93f50434a9 100644 --- a/tests/test_sprint65_pipeline_comparison.py +++ b/tests/test_sprint65_pipeline_comparison.py @@ -33,11 +33,11 @@ import pytest from picarones.core.corpus import Corpus, Document, GTLevel, TextGT from picarones.core.modules import ArtifactType, BaseModule -from picarones.core.pipeline_comparison import ( +from picarones.measurements.pipeline_comparison import ( PipelineComparisonResult, compare_pipelines, ) -from picarones.core.pipeline_runner import PipelineSpec, PipelineStep +from picarones.core.pipeline import PipelineSpec, PipelineStep # ────────────────────────────────────────────────────────────────────────── diff --git a/tests/test_sprint66_dag_branching.py b/tests/test_sprint66_dag_branching.py index e2f45c8e6aa9cefb9cd65c959eddbbca0b63e383..4108ce33beadc9134ee85dacbd2243c955ef206a 100644 --- a/tests/test_sprint66_dag_branching.py +++ b/tests/test_sprint66_dag_branching.py @@ -32,7 +32,7 @@ from typing import Any from picarones.core.corpus import Document, GTLevel, TextGT from picarones.core.modules import ArtifactType, BaseModule -from picarones.core.pipeline_runner import ( +from picarones.core.pipeline import ( PipelineRunner, PipelineSpec, PipelineStep, diff --git a/tests/test_sprint67_pipeline_html.py b/tests/test_sprint67_pipeline_html.py index 5cc29073795525c94658bd0a6522796b74e14ab2..717346860ae6438c60957933a3d6c2acb8bf73ac 100644 --- a/tests/test_sprint67_pipeline_html.py +++ b/tests/test_sprint67_pipeline_html.py @@ -21,7 +21,7 @@ from __future__ import annotations import json from pathlib import Path -from picarones.core.pipeline_benchmark import ( +from picarones.measurements.pipeline_benchmark import ( PipelineBenchmarkResult, StepAggregate, ) diff --git a/tests/test_sprint68_pipeline_comparison_html.py b/tests/test_sprint68_pipeline_comparison_html.py index 8921a394fc89d7c9b675f6b218f8a96d327ffde3..3ae7a7d6151f61e604db8e869b86d9d80c56d01a 100644 --- a/tests/test_sprint68_pipeline_comparison_html.py +++ b/tests/test_sprint68_pipeline_comparison_html.py @@ -32,11 +32,11 @@ import json from pathlib import Path from picarones.core.modules import ArtifactType -from picarones.core.pipeline_benchmark import ( +from picarones.measurements.pipeline_benchmark import ( PipelineBenchmarkResult, StepAggregate, ) -from picarones.core.pipeline_comparison import PipelineComparisonResult +from picarones.measurements.pipeline_comparison import PipelineComparisonResult from picarones.report.pipeline_render import ( RankingSpec, build_pipeline_comparison_report_html, diff --git a/tests/test_sprint69_user_doc.py b/tests/test_sprint69_user_doc.py index 3466c73604aba062745065c3d02caa3b98f4875e..519b9c757c0576b90e98b09b06d882bfc94eaa6a 100644 --- a/tests/test_sprint69_user_doc.py +++ b/tests/test_sprint69_user_doc.py @@ -151,7 +151,7 @@ class TestCodeSnippets: # Les imports doivent pointer vers les vrais modules # picarones.core.* et picarones.report.* assert "from picarones.core.modules import" in doc - assert "from picarones.core.pipeline_runner import" in doc - assert "from picarones.core.pipeline_benchmark import" in doc - assert "from picarones.core.pipeline_comparison import" in doc + assert "from picarones.core.pipeline import" in doc + assert "from picarones.measurements.pipeline_benchmark import" in doc + assert "from picarones.measurements.pipeline_comparison import" in doc assert "from picarones.report.pipeline_render import" in doc diff --git a/tests/test_sprint6_web_interface.py b/tests/test_sprint6_web_interface.py index 78b1f89ac02597d1dcbced43d0da4ea63e529ad6..2ed63c64819d56ab0691504a60dca7a707e1840d 100644 --- a/tests/test_sprint6_web_interface.py +++ b/tests/test_sprint6_web_interface.py @@ -57,13 +57,13 @@ def client(): @pytest.fixture def htr_catalogue(): - from picarones.importers.htr_united import HTRUnitedCatalogue + from picarones.extras.importers.htr_united import HTRUnitedCatalogue return HTRUnitedCatalogue.from_demo() @pytest.fixture def hf_importer(): - from picarones.importers.huggingface import HuggingFaceImporter + from picarones.extras.importers.huggingface import HuggingFaceImporter return HuggingFaceImporter() @@ -74,7 +74,7 @@ def hf_importer(): class TestHTRUnitedEntry: def test_from_dict_basic(self): - from picarones.importers.htr_united import HTRUnitedEntry + from picarones.extras.importers.htr_united import HTRUnitedEntry d = { "id": "test-corpus", "title": "Test Corpus", "url": "https://github.com/test/corpus", "language": ["French"], "script": ["Gothic"], "century": [14, 15], @@ -88,7 +88,7 @@ class TestHTRUnitedEntry: assert e.lines == 5000 def test_as_dict_roundtrip(self): - from picarones.importers.htr_united import HTRUnitedEntry + from picarones.extras.importers.htr_united import HTRUnitedEntry d = { "id": "rtrip", "title": "Round Trip", "url": "https://github.com/a/b", "language": ["Latin"], "script": ["Caroline"], "century": [9], @@ -102,19 +102,19 @@ class TestHTRUnitedEntry: assert out["format"] == "PAGE" def test_century_str_roman(self): - from picarones.importers.htr_united import HTRUnitedEntry + from picarones.extras.importers.htr_united import HTRUnitedEntry e = HTRUnitedEntry(id="x", title="x", url="x", century=[12, 14]) cs = e.century_str assert "XIIe" in cs assert "XIVe" in cs def test_century_str_single(self): - from picarones.importers.htr_united import HTRUnitedEntry + from picarones.extras.importers.htr_united import HTRUnitedEntry e = HTRUnitedEntry(id="x", title="x", url="x", century=[19]) assert "XIXe" in e.century_str def test_default_fields(self): - from picarones.importers.htr_united import HTRUnitedEntry + from picarones.extras.importers.htr_united import HTRUnitedEntry e = HTRUnitedEntry(id="minimal", title="Min", url="http://x") assert e.language == [] assert e.lines == 0 @@ -122,14 +122,14 @@ class TestHTRUnitedEntry: assert e.tags == [] def test_from_dict_missing_fields(self): - from picarones.importers.htr_united import HTRUnitedEntry + from picarones.extras.importers.htr_united import HTRUnitedEntry e = HTRUnitedEntry.from_dict({"id": "sparse", "title": "Sparse"}) assert e.id == "sparse" assert e.institution == "" assert e.lines == 0 def test_as_dict_has_all_keys(self): - from picarones.importers.htr_united import HTRUnitedEntry + from picarones.extras.importers.htr_united import HTRUnitedEntry e = HTRUnitedEntry(id="k", title="K", url="http://k") d = e.as_dict() for key in ["id", "title", "url", "language", "script", "century", @@ -137,7 +137,7 @@ class TestHTRUnitedEntry: assert key in d, f"Missing key: {key}" def test_url_preserved(self): - from picarones.importers.htr_united import HTRUnitedEntry + from picarones.extras.importers.htr_united import HTRUnitedEntry url = "https://github.com/HTR-United/cremma-medieval" e = HTRUnitedEntry(id="c", title="CREMMA", url=url) assert e.url == url @@ -243,14 +243,14 @@ class TestHTRUnitedSearch: class TestHTRUnitedImport: def test_import_creates_meta_file(self, tmp_path, htr_catalogue): - from picarones.importers.htr_united import import_htr_united_corpus + from picarones.extras.importers.htr_united import import_htr_united_corpus entry = htr_catalogue.entries[0] result = import_htr_united_corpus(entry, tmp_path, max_samples=5) meta_file = Path(result["metadata_file"]) assert meta_file.exists() def test_import_meta_content(self, tmp_path, htr_catalogue): - from picarones.importers.htr_united import import_htr_united_corpus + from picarones.extras.importers.htr_united import import_htr_united_corpus entry = htr_catalogue.entries[0] result = import_htr_united_corpus(entry, tmp_path, max_samples=5) meta = json.loads(Path(result["metadata_file"]).read_text()) @@ -258,14 +258,14 @@ class TestHTRUnitedImport: assert meta["entry_id"] == entry.id def test_import_returns_dict_keys(self, tmp_path, htr_catalogue): - from picarones.importers.htr_united import import_htr_united_corpus + from picarones.extras.importers.htr_united import import_htr_united_corpus entry = htr_catalogue.entries[0] result = import_htr_united_corpus(entry, tmp_path, max_samples=5) for k in ["entry_id", "title", "output_dir", "files_imported", "metadata_file"]: assert k in result, f"Missing key: {k}" def test_import_creates_output_dir(self, tmp_path, htr_catalogue): - from picarones.importers.htr_united import import_htr_united_corpus + from picarones.extras.importers.htr_united import import_htr_united_corpus entry = htr_catalogue.entries[0] new_dir = tmp_path / "new_subdir" / "corpus" import_htr_united_corpus(entry, new_dir, max_samples=5) @@ -279,7 +279,7 @@ class TestHTRUnitedImport: class TestHuggingFaceDataset: def test_from_dict_basic(self): - from picarones.importers.huggingface import HuggingFaceDataset + from picarones.extras.importers.huggingface import HuggingFaceDataset d = { "dataset_id": "test/dataset", "title": "Test Dataset", "description": "A test dataset.", "language": ["French"], @@ -292,7 +292,7 @@ class TestHuggingFaceDataset: assert ds.downloads == 500 def test_as_dict_roundtrip(self): - from picarones.importers.huggingface import HuggingFaceDataset + from picarones.extras.importers.huggingface import HuggingFaceDataset ds = HuggingFaceDataset( dataset_id="a/b", title="AB", description="desc", language=["Latin"], tags=["htr"], @@ -302,12 +302,12 @@ class TestHuggingFaceDataset: assert d["language"] == ["Latin"] def test_hf_url(self): - from picarones.importers.huggingface import HuggingFaceDataset + from picarones.extras.importers.huggingface import HuggingFaceDataset ds = HuggingFaceDataset(dataset_id="CATMuS/medieval", title="CATMuS") assert ds.hf_url == "https://huggingface.co/datasets/CATMuS/medieval" def test_as_dict_has_all_keys(self): - from picarones.importers.huggingface import HuggingFaceDataset + from picarones.extras.importers.huggingface import HuggingFaceDataset ds = HuggingFaceDataset(dataset_id="x/y", title="XY") d = ds.as_dict() for k in ["dataset_id", "title", "description", "language", "tags", @@ -315,17 +315,17 @@ class TestHuggingFaceDataset: assert k in d, f"Missing: {k}" def test_default_source(self): - from picarones.importers.huggingface import HuggingFaceDataset + from picarones.extras.importers.huggingface import HuggingFaceDataset ds = HuggingFaceDataset(dataset_id="x/y", title="XY") assert ds.source == "reference" def test_from_dict_uses_id_as_fallback_title(self): - from picarones.importers.huggingface import HuggingFaceDataset + from picarones.extras.importers.huggingface import HuggingFaceDataset ds = HuggingFaceDataset.from_dict({"dataset_id": "owner/repo"}) assert ds.title == "owner/repo" def test_replace_source_helper(self): - from picarones.importers.huggingface import HuggingFaceDataset + from picarones.extras.importers.huggingface import HuggingFaceDataset ds = HuggingFaceDataset(dataset_id="x/y", title="XY", source="reference") ds2 = ds._replace_source("api") assert ds2.source == "api" @@ -392,23 +392,23 @@ class TestHuggingFaceImporter: class TestHuggingFaceReferenceData: def test_reference_datasets_loaded(self): - from picarones.importers.huggingface import _REFERENCE_DATASETS + from picarones.extras.importers.huggingface import _REFERENCE_DATASETS assert len(_REFERENCE_DATASETS) >= 5 def test_catmus_present(self): - from picarones.importers.huggingface import _REFERENCE_DATASETS + from picarones.extras.importers.huggingface import _REFERENCE_DATASETS ids = [d["dataset_id"] for d in _REFERENCE_DATASETS] assert any("CATMuS" in did or "catmus" in did.lower() for did in ids) def test_all_have_required_fields(self): - from picarones.importers.huggingface import _REFERENCE_DATASETS + from picarones.extras.importers.huggingface import _REFERENCE_DATASETS for d in _REFERENCE_DATASETS: assert "dataset_id" in d assert "title" in d assert "language" in d def test_all_are_image_to_text(self): - from picarones.importers.huggingface import _REFERENCE_DATASETS + from picarones.extras.importers.huggingface import _REFERENCE_DATASETS for d in _REFERENCE_DATASETS: assert d.get("task", "image-to-text") == "image-to-text" @@ -900,14 +900,14 @@ class TestRunnerProgressCallback: def test_callback_signature_accepted(self): """run_benchmark accepte un paramètre progress_callback.""" import inspect - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark sig = inspect.signature(run_benchmark) assert "progress_callback" in sig.parameters def test_callback_is_optional(self): """progress_callback est optionnel (valeur par défaut None).""" import inspect - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark sig = inspect.signature(run_benchmark) param = sig.parameters["progress_callback"] assert param.default is None @@ -915,7 +915,7 @@ class TestRunnerProgressCallback: def test_callback_called_with_mock_engine(self, tmp_corpus): """Le callback est appelé pour chaque document.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine class MockEngine(BaseOCREngine): @@ -936,7 +936,7 @@ class TestRunnerProgressCallback: def test_callback_receives_engine_name(self, tmp_corpus): """Le callback reçoit le nom du moteur.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine class MockEngine(BaseOCREngine): @@ -957,7 +957,7 @@ class TestRunnerProgressCallback: def test_callback_exception_does_not_crash(self, tmp_corpus): """Une exception dans le callback ne plante pas le benchmark.""" from picarones.core.corpus import load_corpus_from_directory - from picarones.core.runner import run_benchmark + from picarones.measurements.runner import run_benchmark from picarones.engines.base import BaseOCREngine class MockEngine(BaseOCREngine): diff --git a/tests/test_sprint70_pipeline_cli.py b/tests/test_sprint70_pipeline_cli.py index 05c9d707f6620aecd7262e3a7f91478492275c80..f1e04eb147cdd4ca00cf469a78a55516748512f0 100644 --- a/tests/test_sprint70_pipeline_cli.py +++ b/tests/test_sprint70_pipeline_cli.py @@ -27,7 +27,7 @@ import pytest from click.testing import CliRunner from picarones.core.modules import ArtifactType, BaseModule -from picarones.core.pipeline_spec_loader import ( +from picarones.measurements.pipeline_spec_loader import ( PipelineSpecLoadError, _resolve_class, load_comparison_specs_from_dict, diff --git a/tests/test_sprint71_rare_tokens.py b/tests/test_sprint71_rare_tokens.py index 0d15d74e011448ffbc1e8c23c96f6a412ddfd6b5..44b4a89f0c60ba523dc19c591cf14737ee2b80b9 100644 --- a/tests/test_sprint71_rare_tokens.py +++ b/tests/test_sprint71_rare_tokens.py @@ -24,7 +24,7 @@ from __future__ import annotations import pytest -from picarones.core.rare_tokens import ( +from picarones.measurements.rare_tokens import ( compute_rare_token_recall, extract_rare_tokens, frequency_distribution, diff --git a/tests/test_sprint72_worst_lines.py b/tests/test_sprint72_worst_lines.py index a823bc807a943b67dc9f64e65fa45e169716bed4..377352fad9671bad96e05da76dd5b8f2069e737d 100644 --- a/tests/test_sprint72_worst_lines.py +++ b/tests/test_sprint72_worst_lines.py @@ -27,7 +27,7 @@ from __future__ import annotations from dataclasses import dataclass, field from typing import Any -from picarones.core.worst_lines import WorstLineEntry, extract_worst_lines +from picarones.measurements.worst_lines import WorstLineEntry, extract_worst_lines from picarones.report.worst_lines_render import build_worst_lines_table_html diff --git a/tests/test_sprint73_baseline_comparison.py b/tests/test_sprint73_baseline_comparison.py index dc9487e569a1f2ea8e0ee76f330f57d4d40b3868..1eabf03aee40101cece79b058abd916119081e23 100644 --- a/tests/test_sprint73_baseline_comparison.py +++ b/tests/test_sprint73_baseline_comparison.py @@ -31,13 +31,13 @@ from typing import Any, Optional import pytest -from picarones.core.baseline_comparison import ( +from picarones.measurements.baseline_comparison import ( compute_corpus_difficulty_percentile, compute_engine_baseline, ) -from picarones.core.narrative.detectors import detect_engine_off_baseline -from picarones.core.narrative.facts import FactImportance, FactType -from picarones.core.narrative.renderer import render_fact +from picarones.measurements.narrative.detectors import detect_engine_off_baseline +from picarones.core.facts import FactImportance, FactType +from picarones.measurements.narrative.renderer import render_fact # ────────────────────────────────────────────────────────────────────────── diff --git a/tests/test_sprint75_taxonomy_cooccurrence.py b/tests/test_sprint75_taxonomy_cooccurrence.py index 5f45422c7154895c90dd0bdfbcd9b70ee7617e1a..9c7b457f542dd893a7c1e49325611e074e7b6427 100644 --- a/tests/test_sprint75_taxonomy_cooccurrence.py +++ b/tests/test_sprint75_taxonomy_cooccurrence.py @@ -26,7 +26,7 @@ from pathlib import Path import pytest -from picarones.core.taxonomy_cooccurrence import ( +from picarones.measurements.taxonomy_cooccurrence import ( compute_taxonomy_cooccurrence, ) from picarones.report.taxonomy_cooccurrence_render import ( diff --git a/tests/test_sprint76_taxonomy_intra_doc.py b/tests/test_sprint76_taxonomy_intra_doc.py index d7b9c82bb3ef7f7111bc2ef81a35e93ff48ac42a..0c957d51373abc1f2975f157d7f60f8bdd312a3d 100644 --- a/tests/test_sprint76_taxonomy_intra_doc.py +++ b/tests/test_sprint76_taxonomy_intra_doc.py @@ -26,7 +26,7 @@ from pathlib import Path import pytest -from picarones.core.taxonomy_intra_doc import ( +from picarones.measurements.taxonomy_intra_doc import ( compute_taxonomy_position_heatmap, ) from picarones.report.taxonomy_intra_doc_render import ( diff --git a/tests/test_sprint77_taxonomy_comparison.py b/tests/test_sprint77_taxonomy_comparison.py index 9e8525adde5f818fb842b02ce1c956f382000c4a..b793b69a979313f60a8bd3f891f05377d1874e9d 100644 --- a/tests/test_sprint77_taxonomy_comparison.py +++ b/tests/test_sprint77_taxonomy_comparison.py @@ -23,7 +23,7 @@ from __future__ import annotations import json from pathlib import Path -from picarones.core.taxonomy_comparison import ( +from picarones.measurements.taxonomy_comparison import ( RECOVERABILITY, compare_taxonomies, ) @@ -91,7 +91,7 @@ class TestCompare: def test_recoverability_constant_complete(self) -> None: # Sanité : RECOVERABILITY couvre toutes les classes du module - from picarones.core.taxonomy import ERROR_CLASSES + from picarones.measurements.taxonomy import ERROR_CLASSES for cls in ERROR_CLASSES: assert cls in RECOVERABILITY diff --git a/tests/test_sprint78_equivalence_profile.py b/tests/test_sprint78_equivalence_profile.py index 30456577fa56b1277b0eb394c0d5debaac3151e4..2d5f1491896c4e7220c296d90278f1c35039b35b 100644 --- a/tests/test_sprint78_equivalence_profile.py +++ b/tests/test_sprint78_equivalence_profile.py @@ -23,7 +23,7 @@ Couvre : from __future__ import annotations -from picarones.core.equivalence_profile import ( +from picarones.measurements.equivalence_profile import ( BUILTIN_EQUIVALENCES, EquivalenceRule, apply_selected_equivalences, diff --git a/tests/test_sprint79_cost_projection.py b/tests/test_sprint79_cost_projection.py index 0fd6b5319b819bccbaead7821be7ca18369ef918..e9a47c16ec3967ef998bf4670aafa00100e45ca4 100644 --- a/tests/test_sprint79_cost_projection.py +++ b/tests/test_sprint79_cost_projection.py @@ -23,7 +23,7 @@ from __future__ import annotations import pytest -from picarones.core.cost_projection import ( +from picarones.measurements.cost_projection import ( ProjectedCost, cost_gap_table, project_all_engines, @@ -31,7 +31,7 @@ from picarones.core.cost_projection import ( project_cost_total, project_engine, ) -from picarones.core.pricing import EngineCost +from picarones.measurements.pricing import EngineCost def _ec(name: str, cost_1k: float | None, co2_1k: float | None = None, diff --git a/tests/test_sprint7_advanced_report.py b/tests/test_sprint7_advanced_report.py index 522faed50528b4625d0f36effe4daf78aac810ee..cb2c1c284a71eb0af1ede1a4d88ee936e659f637 100644 --- a/tests/test_sprint7_advanced_report.py +++ b/tests/test_sprint7_advanced_report.py @@ -53,41 +53,41 @@ def html_s7(sample_benchmark_s7): class TestBootstrapCI: def test_returns_tuple_of_two(self): - from picarones.core.statistics import bootstrap_ci + from picarones.measurements.statistics import bootstrap_ci result = bootstrap_ci([0.1, 0.2, 0.3]) assert isinstance(result, tuple) and len(result) == 2 def test_lower_le_upper(self): - from picarones.core.statistics import bootstrap_ci + from picarones.measurements.statistics import bootstrap_ci lo, hi = bootstrap_ci([0.1, 0.2, 0.3, 0.4, 0.5]) assert lo <= hi def test_ci_contains_mean(self): - from picarones.core.statistics import bootstrap_ci + from picarones.measurements.statistics import bootstrap_ci values = [0.1, 0.15, 0.2, 0.12, 0.18, 0.13, 0.17] lo, hi = bootstrap_ci(values) mean = sum(values) / len(values) assert lo <= mean <= hi def test_empty_returns_zeros(self): - from picarones.core.statistics import bootstrap_ci + from picarones.measurements.statistics import bootstrap_ci lo, hi = bootstrap_ci([]) assert lo == 0.0 and hi == 0.0 def test_single_value(self): - from picarones.core.statistics import bootstrap_ci + from picarones.measurements.statistics import bootstrap_ci lo, hi = bootstrap_ci([0.25]) assert lo <= 0.25 <= hi def test_reproducible_with_seed(self): - from picarones.core.statistics import bootstrap_ci + from picarones.measurements.statistics import bootstrap_ci vals = [0.1, 0.2, 0.3, 0.15, 0.25] r1 = bootstrap_ci(vals, seed=1) r2 = bootstrap_ci(vals, seed=1) assert r1 == r2 def test_wider_with_more_variance(self): - from picarones.core.statistics import bootstrap_ci + from picarones.measurements.statistics import bootstrap_ci narrow = [0.10, 0.11, 0.10, 0.11, 0.10] wide = [0.01, 0.50, 0.02, 0.49, 0.01] lo_n, hi_n = bootstrap_ci(narrow, n_iter=500) @@ -101,7 +101,7 @@ class TestBootstrapCI: class TestWilcoxonTest: def test_returns_dict_with_keys(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test r = wilcoxon_test([0.1]*5, [0.1]*5) assert "statistic" in r assert "p_value" in r @@ -109,13 +109,13 @@ class TestWilcoxonTest: assert "interpretation" in r def test_identical_series_not_significant(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test vals = [0.1, 0.2, 0.3, 0.15, 0.05] r = wilcoxon_test(vals, vals) assert not r["significant"] def test_clearly_different_series_significant(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test a = [0.01]*12 b = [0.80]*12 r = wilcoxon_test(a, b) @@ -123,37 +123,37 @@ class TestWilcoxonTest: assert r["p_value"] < 0.05 def test_p_value_in_range(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test a = [0.1, 0.15, 0.2, 0.08] b = [0.2, 0.25, 0.3, 0.18] r = wilcoxon_test(a, b) assert 0.0 <= r["p_value"] <= 1.0 def test_interpretation_is_string(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test r = wilcoxon_test([0.1, 0.2], [0.1, 0.2]) assert isinstance(r["interpretation"], str) and len(r["interpretation"]) > 10 def test_n_pairs_correct(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test r = wilcoxon_test([0.1, 0.2, 0.3], [0.1, 0.2, 0.3]) # tous les diffs = 0, filtrés en mode wilcox assert r["n_pairs"] == 0 def test_mismatched_lengths_raises(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test with pytest.raises(ValueError): wilcoxon_test([0.1, 0.2], [0.1]) def test_w_plus_w_minus_present(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test a = [0.1, 0.2, 0.3, 0.15, 0.25, 0.18, 0.12, 0.22, 0.08, 0.27] b = [0.2, 0.3, 0.4, 0.25, 0.35, 0.28, 0.22, 0.32, 0.18, 0.37] r = wilcoxon_test(a, b) assert "W_plus" in r and "W_minus" in r def test_significant_larger_sample(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test import random rng = random.Random(0) a = [rng.uniform(0.0, 0.05) for _ in range(15)] @@ -162,7 +162,7 @@ class TestWilcoxonTest: assert r["significant"] def test_symmetry(self): - from picarones.core.statistics import wilcoxon_test + from picarones.measurements.statistics import wilcoxon_test a = [0.1, 0.2, 0.3, 0.15, 0.25, 0.18, 0.22, 0.08, 0.27, 0.14] b = [0.2, 0.3, 0.4, 0.25, 0.35, 0.28, 0.32, 0.18, 0.37, 0.24] r_ab = wilcoxon_test(a, b) @@ -177,35 +177,35 @@ class TestWilcoxonTest: class TestPairwiseStats: def test_returns_list(self): - from picarones.core.statistics import compute_pairwise_stats + from picarones.measurements.statistics import compute_pairwise_stats r = compute_pairwise_stats({"A": [0.1, 0.2], "B": [0.3, 0.4]}) assert isinstance(r, list) def test_correct_pair_count_2_engines(self): - from picarones.core.statistics import compute_pairwise_stats + from picarones.measurements.statistics import compute_pairwise_stats r = compute_pairwise_stats({"A": [0.1]*5, "B": [0.2]*5}) assert len(r) == 1 def test_correct_pair_count_3_engines(self): - from picarones.core.statistics import compute_pairwise_stats + from picarones.measurements.statistics import compute_pairwise_stats r = compute_pairwise_stats({ "A": [0.1]*5, "B": [0.2]*5, "C": [0.3]*5 }) assert len(r) == 3 def test_pair_has_engine_names(self): - from picarones.core.statistics import compute_pairwise_stats + from picarones.measurements.statistics import compute_pairwise_stats r = compute_pairwise_stats({"A": [0.1]*5, "B": [0.2]*5}) assert r[0]["engine_a"] in ["A", "B"] assert r[0]["engine_b"] in ["A", "B"] def test_pair_has_p_value(self): - from picarones.core.statistics import compute_pairwise_stats + from picarones.measurements.statistics import compute_pairwise_stats r = compute_pairwise_stats({"A": [0.1]*5, "B": [0.2]*5}) assert "p_value" in r[0] def test_single_engine_returns_empty(self): - from picarones.core.statistics import compute_pairwise_stats + from picarones.measurements.statistics import compute_pairwise_stats r = compute_pairwise_stats({"A": [0.1]*5}) assert r == [] @@ -216,33 +216,33 @@ class TestPairwiseStats: class TestReliabilityCurve: def test_returns_list(self): - from picarones.core.statistics import compute_reliability_curve + from picarones.measurements.statistics import compute_reliability_curve r = compute_reliability_curve([0.1, 0.2, 0.3]) assert isinstance(r, list) def test_correct_number_of_steps(self): - from picarones.core.statistics import compute_reliability_curve + from picarones.measurements.statistics import compute_reliability_curve r = compute_reliability_curve([0.1]*10, steps=5) assert len(r) == 5 def test_pct_docs_increases(self): - from picarones.core.statistics import compute_reliability_curve + from picarones.measurements.statistics import compute_reliability_curve r = compute_reliability_curve([0.1, 0.2, 0.3, 0.4, 0.5], steps=5) pcts = [p["pct_docs"] for p in r] assert pcts == sorted(pcts) def test_mean_cer_increases(self): - from picarones.core.statistics import compute_reliability_curve + from picarones.measurements.statistics import compute_reliability_curve r = compute_reliability_curve([0.05, 0.10, 0.20, 0.30, 0.50], steps=5) cers = [p["mean_cer"] for p in r] assert cers[0] <= cers[-1] def test_empty_returns_empty(self): - from picarones.core.statistics import compute_reliability_curve + from picarones.measurements.statistics import compute_reliability_curve assert compute_reliability_curve([]) == [] def test_last_point_includes_all(self): - from picarones.core.statistics import compute_reliability_curve + from picarones.measurements.statistics import compute_reliability_curve vals = [0.1, 0.2, 0.3] r = compute_reliability_curve(vals, steps=4) last = r[-1] @@ -250,7 +250,7 @@ class TestReliabilityCurve: assert last["mean_cer"] == pytest.approx(expected, rel=1e-4) def test_each_point_has_required_keys(self): - from picarones.core.statistics import compute_reliability_curve + from picarones.measurements.statistics import compute_reliability_curve r = compute_reliability_curve([0.1, 0.2, 0.3], steps=3) for p in r: assert "pct_docs" in p and "mean_cer" in p @@ -262,47 +262,47 @@ class TestReliabilityCurve: class TestVennData: def test_venn2_type(self): - from picarones.core.statistics import compute_venn_data + from picarones.measurements.statistics import compute_venn_data r = compute_venn_data({"A": {"e1","e2"}, "B": {"e2","e3"}}) assert r["type"] == "venn2" def test_venn3_type(self): - from picarones.core.statistics import compute_venn_data + from picarones.measurements.statistics import compute_venn_data r = compute_venn_data({"A": {"e1"}, "B": {"e2"}, "C": {"e3"}}) assert r["type"] == "venn3" def test_venn2_counts_correct(self): - from picarones.core.statistics import compute_venn_data + from picarones.measurements.statistics import compute_venn_data r = compute_venn_data({"A": {"e1","e2","e3"}, "B": {"e2","e3","e4"}}) assert r["only_a"] == 1 assert r["only_b"] == 1 assert r["both"] == 2 def test_venn2_disjoint(self): - from picarones.core.statistics import compute_venn_data + from picarones.measurements.statistics import compute_venn_data r = compute_venn_data({"A": {"e1"}, "B": {"e2"}}) assert r["both"] == 0 assert r["only_a"] == 1 assert r["only_b"] == 1 def test_venn2_subset(self): - from picarones.core.statistics import compute_venn_data + from picarones.measurements.statistics import compute_venn_data r = compute_venn_data({"A": {"e1","e2"}, "B": {"e1","e2","e3"}}) assert r["only_a"] == 0 def test_venn3_abc_count(self): - from picarones.core.statistics import compute_venn_data + from picarones.measurements.statistics import compute_venn_data shared = {"e1","e2"} r = compute_venn_data({"A": shared, "B": shared, "C": shared}) assert r["abc"] == 2 def test_empty_returns_empty(self): - from picarones.core.statistics import compute_venn_data + from picarones.measurements.statistics import compute_venn_data r = compute_venn_data({}) assert r == {} def test_labels_present(self): - from picarones.core.statistics import compute_venn_data + from picarones.measurements.statistics import compute_venn_data r = compute_venn_data({"moteur_a": {"e1"}, "moteur_b": {"e2"}}) assert r["label_a"] == "moteur_a" assert r["label_b"] == "moteur_b" @@ -324,17 +324,17 @@ class TestErrorClustering: ] def test_returns_list(self): - from picarones.core.statistics import cluster_errors + from picarones.measurements.statistics import cluster_errors result = cluster_errors(self._sample_data()) assert isinstance(result, list) def test_max_clusters_respected(self): - from picarones.core.statistics import cluster_errors + from picarones.measurements.statistics import cluster_errors result = cluster_errors(self._sample_data(), max_clusters=3) assert len(result) <= 3 def test_cluster_has_required_keys(self): - from picarones.core.statistics import cluster_errors + from picarones.measurements.statistics import cluster_errors result = cluster_errors(self._sample_data()) if result: c = result[0] @@ -344,7 +344,7 @@ class TestErrorClustering: assert hasattr(c, "examples") def test_as_dict_method(self): - from picarones.core.statistics import cluster_errors + from picarones.measurements.statistics import cluster_errors result = cluster_errors(self._sample_data()) if result: d = result[0].as_dict() @@ -354,24 +354,24 @@ class TestErrorClustering: assert "examples" in d def test_sorted_by_count_descending(self): - from picarones.core.statistics import cluster_errors + from picarones.measurements.statistics import cluster_errors result = cluster_errors(self._sample_data()) if len(result) >= 2: assert result[0].count >= result[1].count def test_examples_capped_at_5(self): - from picarones.core.statistics import cluster_errors + from picarones.measurements.statistics import cluster_errors result = cluster_errors(self._sample_data()) for c in result: assert len(c.as_dict()["examples"]) <= 5 def test_empty_data_returns_empty(self): - from picarones.core.statistics import cluster_errors + from picarones.measurements.statistics import cluster_errors result = cluster_errors([]) assert result == [] def test_cluster_id_unique(self): - from picarones.core.statistics import cluster_errors + from picarones.measurements.statistics import cluster_errors result = cluster_errors(self._sample_data()) ids = [c.cluster_id for c in result] assert len(ids) == len(set(ids)) @@ -392,12 +392,12 @@ class TestCorrelationMatrix: ] def test_returns_dict_with_labels_and_matrix(self): - from picarones.core.statistics import compute_correlation_matrix + from picarones.measurements.statistics import compute_correlation_matrix r = compute_correlation_matrix(self._sample_metrics()) assert "labels" in r and "matrix" in r def test_matrix_is_square(self): - from picarones.core.statistics import compute_correlation_matrix + from picarones.measurements.statistics import compute_correlation_matrix r = compute_correlation_matrix(self._sample_metrics()) n = len(r["labels"]) assert len(r["matrix"]) == n @@ -405,13 +405,13 @@ class TestCorrelationMatrix: assert len(row) == n def test_diagonal_is_one(self): - from picarones.core.statistics import compute_correlation_matrix + from picarones.measurements.statistics import compute_correlation_matrix r = compute_correlation_matrix(self._sample_metrics()) for i in range(len(r["labels"])): assert r["matrix"][i][i] == pytest.approx(1.0) def test_cer_quality_negatively_correlated(self): - from picarones.core.statistics import compute_correlation_matrix + from picarones.measurements.statistics import compute_correlation_matrix r = compute_correlation_matrix(self._sample_metrics()) labels = r["labels"] if "cer" in labels and "quality_score" in labels: @@ -420,7 +420,7 @@ class TestCorrelationMatrix: assert r["matrix"][i][j] < 0 # plus la qualité est bonne, plus le CER est bas def test_symmetric_matrix(self): - from picarones.core.statistics import compute_correlation_matrix + from picarones.measurements.statistics import compute_correlation_matrix r = compute_correlation_matrix(self._sample_metrics()) n = len(r["labels"]) for i in range(n): @@ -428,18 +428,18 @@ class TestCorrelationMatrix: assert r["matrix"][i][j] == pytest.approx(r["matrix"][j][i], abs=1e-6) def test_empty_returns_empty(self): - from picarones.core.statistics import compute_correlation_matrix + from picarones.measurements.statistics import compute_correlation_matrix r = compute_correlation_matrix([]) assert r == {"labels": [], "matrix": []} def test_custom_metric_keys(self): - from picarones.core.statistics import compute_correlation_matrix + from picarones.measurements.statistics import compute_correlation_matrix data = [{"a": 1.0, "b": 2.0, "c": 3.0}] * 5 r = compute_correlation_matrix(data, metric_keys=["a", "b"]) assert r["labels"] == ["a", "b"] def test_values_in_range(self): - from picarones.core.statistics import compute_correlation_matrix + from picarones.measurements.statistics import compute_correlation_matrix r = compute_correlation_matrix(self._sample_metrics()) for row in r["matrix"]: for v in row: @@ -452,60 +452,60 @@ class TestCorrelationMatrix: class TestDifficultyScore: def test_returns_difficulty_score(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score ds = compute_difficulty_score("doc1", "maiſtre Froiſſart", [0.1, 0.2, 0.3]) - from picarones.core.difficulty import DifficultyScore + from picarones.measurements.difficulty import DifficultyScore assert isinstance(ds, DifficultyScore) def test_score_in_range(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score ds = compute_difficulty_score("doc1", "hello world", [0.1, 0.2]) assert 0.0 <= ds.score <= 1.0 def test_more_variance_higher_score(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score low_var = compute_difficulty_score("doc1", "hello", [0.1, 0.1, 0.1]) high_var = compute_difficulty_score("doc1", "hello", [0.0, 0.5, 1.0]) assert high_var.score > low_var.score def test_bad_quality_image_harder(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score good_img = compute_difficulty_score("doc1", "hello", [0.1], image_quality_score=0.9) bad_img = compute_difficulty_score("doc1", "hello", [0.1], image_quality_score=0.1) assert bad_img.score > good_img.score def test_special_chars_increase_difficulty(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score plain = compute_difficulty_score("doc1", "hello world plain text", [0.1]) heritage = compute_difficulty_score("doc1", "maiſtre Froiſſart ꝑ &", [0.1]) assert heritage.score > plain.score def test_components_present(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score ds = compute_difficulty_score("doc1", "text", [0.1, 0.2]) assert hasattr(ds, "variance_component") assert hasattr(ds, "quality_component") assert hasattr(ds, "density_component") def test_as_dict_has_doc_id(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score ds = compute_difficulty_score("folio_001", "text", [0.1]) d = ds.as_dict() assert d["doc_id"] == "folio_001" def test_as_dict_rounded(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score ds = compute_difficulty_score("doc1", "text", [0.1]) d = ds.as_dict() assert isinstance(d["score"], float) def test_no_engines_gives_low_variance(self): - from picarones.core.difficulty import compute_difficulty_score + from picarones.measurements.difficulty import compute_difficulty_score ds = compute_difficulty_score("doc1", "text", []) assert ds.cer_variance == 0.0 def test_difficulty_label(self): - from picarones.core.difficulty import difficulty_label + from picarones.measurements.difficulty import difficulty_label assert difficulty_label(0.1) == "Facile" assert difficulty_label(0.35) == "Modéré" assert difficulty_label(0.6) == "Difficile" @@ -518,7 +518,7 @@ class TestDifficultyScore: class TestAllDifficulties: def test_returns_dict(self): - from picarones.core.difficulty import compute_all_difficulties + from picarones.measurements.difficulty import compute_all_difficulties r = compute_all_difficulties( ["doc1", "doc2"], {"doc1": "hello", "doc2": "world"}, @@ -527,7 +527,7 @@ class TestAllDifficulties: assert isinstance(r, dict) def test_all_docs_present(self): - from picarones.core.difficulty import compute_all_difficulties + from picarones.measurements.difficulty import compute_all_difficulties r = compute_all_difficulties( ["d1", "d2", "d3"], {"d1": "a", "d2": "b", "d3": "c"}, @@ -536,7 +536,7 @@ class TestAllDifficulties: assert set(r.keys()) == {"d1", "d2", "d3"} def test_scores_in_range(self): - from picarones.core.difficulty import compute_all_difficulties + from picarones.measurements.difficulty import compute_all_difficulties r = compute_all_difficulties( ["d1", "d2"], {"d1": "maiſtre Jean", "d2": "simple text"}, @@ -546,7 +546,7 @@ class TestAllDifficulties: assert 0.0 <= ds.score <= 1.0 def test_with_image_quality(self): - from picarones.core.difficulty import compute_all_difficulties + from picarones.measurements.difficulty import compute_all_difficulties r = compute_all_difficulties( ["d1"], {"d1": "text"}, @@ -558,12 +558,12 @@ class TestAllDifficulties: assert r["d1"].quality_component > 0.5 def test_empty_corpus(self): - from picarones.core.difficulty import compute_all_difficulties + from picarones.measurements.difficulty import compute_all_difficulties r = compute_all_difficulties([], {}, {}) assert r == {} def test_missing_gt_handled(self): - from picarones.core.difficulty import compute_all_difficulties + from picarones.measurements.difficulty import compute_all_difficulties r = compute_all_difficulties( ["d1"], {}, # GT manquante diff --git a/tests/test_sprint80_lexical_modernization.py b/tests/test_sprint80_lexical_modernization.py index 15e32d7e68e76f45cdb7d237867c0f34d92c644d..89d18aab72cdad1cd9cb0a48668ca15d703844d5 100644 --- a/tests/test_sprint80_lexical_modernization.py +++ b/tests/test_sprint80_lexical_modernization.py @@ -27,7 +27,7 @@ from __future__ import annotations import json from pathlib import Path -from picarones.core.lexical_modernization import ( +from picarones.measurements.lexical_modernization import ( aggregate_lexical_modernization, compute_lexical_modernization, top_modernized_tokens, diff --git a/tests/test_sprint81_robustness_projection.py b/tests/test_sprint81_robustness_projection.py index 1c40c7435dcb03a962946e19404729e3d9c34707..c6da5e8919e6f921fd047b044b6b402836f8ef6c 100644 --- a/tests/test_sprint81_robustness_projection.py +++ b/tests/test_sprint81_robustness_projection.py @@ -24,7 +24,7 @@ from __future__ import annotations import pytest -from picarones.core.robustness_projection import ( +from picarones.measurements.robustness_projection import ( _extract_quality_value, _interpolate_cer, aggregate_projection_per_engine, diff --git a/tests/test_sprint82_levers.py b/tests/test_sprint82_levers.py index c0a25fdf6adfffd69fd3ee171fc10b52cf5aa79c..bc240335c0ca52f7a981edf70191c685789e8a72 100644 --- a/tests/test_sprint82_levers.py +++ b/tests/test_sprint82_levers.py @@ -19,7 +19,7 @@ import json import re from pathlib import Path -from picarones.core.levers import ( +from picarones.measurements.levers import ( Lever, LeverImportance, LeverType, diff --git a/tests/test_sprint83_reliability.py b/tests/test_sprint83_reliability.py index 847b2ebb10655170d9974dc58deacb2f519ab09a..937c1b72d5be302377dd57e889fb2c81de5a125d 100644 --- a/tests/test_sprint83_reliability.py +++ b/tests/test_sprint83_reliability.py @@ -29,7 +29,7 @@ from __future__ import annotations import pytest -from picarones.core.reliability import ( +from picarones.measurements.reliability import ( _aligned_char_pairs, cohen_kappa, compute_iaa, diff --git a/tests/test_sprint84_searchability.py b/tests/test_sprint84_searchability.py index 4f574fdcc7aaafce451d5bba2ae815b7f1bc8bb7..bf3486a83bba4a525290a245c4f5fa4d522b2725 100644 --- a/tests/test_sprint84_searchability.py +++ b/tests/test_sprint84_searchability.py @@ -23,7 +23,7 @@ from __future__ import annotations import pytest -from picarones.core.searchability import ( +from picarones.measurements.searchability import ( compute_searchability, levenshtein_distance, searchability_recall_metric, diff --git a/tests/test_sprint85_numerical_sequences.py b/tests/test_sprint85_numerical_sequences.py index ebda43afbc1a2ca641a7233463044bd6dd7d6505..709d1864f1e55f80520f37f54bca29b087dd48e1 100644 --- a/tests/test_sprint85_numerical_sequences.py +++ b/tests/test_sprint85_numerical_sequences.py @@ -16,7 +16,7 @@ Couvre : from __future__ import annotations -from picarones.core.numerical_sequences import ( +from picarones.measurements.numerical_sequences import ( CATEGORIES, _detect_currencies, _detect_foliations, diff --git a/tests/test_sprint86_aii5_html.py b/tests/test_sprint86_aii5_html.py index 0ecc4899a3107e567a434ccb3e5994a53a9d1687..f62cd8c5739ec61e0d8b81d1db5bdc2c387d637e 100644 --- a/tests/test_sprint86_aii5_html.py +++ b/tests/test_sprint86_aii5_html.py @@ -18,11 +18,11 @@ from __future__ import annotations import json from pathlib import Path -from picarones.core.numerical_sequences_runner import ( +from picarones.measurements.numerical_sequences_runner import ( aggregate_numerical_sequence_metrics, compute_numerical_sequence_metrics_adaptive, ) -from picarones.core.metrics import MetricsResult +from picarones.measurements.metrics import MetricsResult from picarones.core.results import DocumentResult, EngineReport @@ -32,7 +32,7 @@ def _stub_metrics() -> MetricsResult: wer=0.0, wer_normalized=0.0, mer=0.0, wil=0.0, reference_length=0, hypothesis_length=0, ) -from picarones.core.searchability_runner import ( +from picarones.measurements.searchability_runner import ( aggregate_searchability_metrics, compute_searchability_metrics, ) diff --git a/tests/test_sprint87_readability_html.py b/tests/test_sprint87_readability_html.py index 9b53e7e4bdb8b5ffe7888f96e33cf7adbc165282..2b37cff13ad2926e674f1280dc382fd6cb00b61e 100644 --- a/tests/test_sprint87_readability_html.py +++ b/tests/test_sprint87_readability_html.py @@ -16,8 +16,8 @@ from __future__ import annotations import json from pathlib import Path -from picarones.core.metrics import MetricsResult -from picarones.core.readability_runner import ( +from picarones.measurements.metrics import MetricsResult +from picarones.measurements.readability_runner import ( aggregate_readability_metrics, compute_readability_metrics, ) diff --git a/tests/test_sprint88_robustness_projection_html.py b/tests/test_sprint88_robustness_projection_html.py index 8267f4fccff46fecb994e2787a4d6d01d6be5335..8dd9d461ac6d477390f45712c19dc4e0a440ecad 100644 --- a/tests/test_sprint88_robustness_projection_html.py +++ b/tests/test_sprint88_robustness_projection_html.py @@ -20,7 +20,7 @@ from __future__ import annotations import json from pathlib import Path -from picarones.core.robustness_projection import ( +from picarones.measurements.robustness_projection import ( aggregate_projection_per_engine, project_robustness_on_corpus, ) diff --git a/tests/test_sprint89_specialization.py b/tests/test_sprint89_specialization.py index 78f826c2105bf5fe0390f13529346b55ac91d8fc..586fae5a0b7db494e59092375c6e00a5455792c4 100644 --- a/tests/test_sprint89_specialization.py +++ b/tests/test_sprint89_specialization.py @@ -15,7 +15,7 @@ from __future__ import annotations import json from pathlib import Path -from picarones.core.specialization import ( +from picarones.measurements.specialization import ( DEFAULT_THRESHOLDS, classify_specialization, compute_specialization_matrix, diff --git a/tests/test_sprint8_escriptorium_gallica.py b/tests/test_sprint8_escriptorium_gallica.py index c3629f67274193689be3211a1fb5632151b30388..2b34364e956e0ce16810059ca131fdf3fe41e7b7 100644 --- a/tests/test_sprint8_escriptorium_gallica.py +++ b/tests/test_sprint8_escriptorium_gallica.py @@ -32,54 +32,54 @@ if TYPE_CHECKING: class TestEScriptoriumClient: def test_import_module(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient assert EScriptoriumClient is not None def test_init_attributes(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://escriptorium.example.org", token="tok123", timeout=60) assert client.base_url == "https://escriptorium.example.org" assert client.token == "tok123" assert client.timeout == 60 def test_base_url_trailing_slash_stripped(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://escriptorium.example.org/", token="tok") assert not client.base_url.endswith("/") def test_headers_contain_token(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="mytoken") headers = client._headers() assert "Token mytoken" in headers.get("Authorization", "") def test_headers_contain_accept_json(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") headers = client._headers() assert "application/json" in headers.get("Accept", "") def test_test_connection_success(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") with patch.object(client, "_get", return_value={"results": [], "count": 0}): assert client.test_connection() is True def test_test_connection_failure(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="bad") with patch.object(client, "_get", side_effect=RuntimeError("403")): assert client.test_connection() is False def test_list_projects_empty(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") with patch.object(client, "_paginate", return_value=[]): projects = client.list_projects() assert projects == [] def test_list_projects_parses_items(self): - from picarones.importers.escriptorium import EScriptoriumClient, EScriptoriumProject + from picarones.extras.importers.escriptorium import EScriptoriumClient, EScriptoriumProject client = EScriptoriumClient("https://example.org", token="tok") mock_data = [ {"pk": 1, "name": "Projet Test", "slug": "projet-test", @@ -94,7 +94,7 @@ class TestEScriptoriumClient: assert projects[0].document_count == 5 def test_list_documents_with_project_filter(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") with patch.object(client, "_paginate", return_value=[]) as mock_pag: client.list_documents(project_pk=42) @@ -102,7 +102,7 @@ class TestEScriptoriumClient: assert call_kwargs[0][1]["project"] == 42 def test_list_parts_returns_list(self): - from picarones.importers.escriptorium import EScriptoriumClient, EScriptoriumPart + from picarones.extras.importers.escriptorium import EScriptoriumClient, EScriptoriumPart client = EScriptoriumClient("https://example.org", token="tok") mock_data = [ {"pk": 10, "title": "f. 1r", "image": "https://example.org/img/1.jpg", "order": 0}, @@ -115,7 +115,7 @@ class TestEScriptoriumClient: assert parts[0].pk == 10 def test_escriptorium_project_as_dict(self): - from picarones.importers.escriptorium import EScriptoriumProject + from picarones.extras.importers.escriptorium import EScriptoriumProject p = EScriptoriumProject(pk=1, name="Test", slug="test", owner="user", document_count=3) d = p.as_dict() assert d["pk"] == 1 @@ -130,25 +130,25 @@ class TestEScriptoriumClient: class TestEScriptoriumConnect: def test_connect_success(self): - from picarones.importers.escriptorium import connect_escriptorium, EScriptoriumClient + from picarones.extras.importers.escriptorium import connect_escriptorium, EScriptoriumClient with patch.object(EScriptoriumClient, "test_connection", return_value=True): client = connect_escriptorium("https://example.org", token="tok") assert isinstance(client, EScriptoriumClient) def test_connect_failure_raises(self): - from picarones.importers.escriptorium import connect_escriptorium, EScriptoriumClient + from picarones.extras.importers.escriptorium import connect_escriptorium, EScriptoriumClient with patch.object(EScriptoriumClient, "test_connection", return_value=False): with pytest.raises(RuntimeError, match="Impossible de se connecter"): connect_escriptorium("https://example.org", token="bad") def test_connect_returns_client_with_correct_url(self): - from picarones.importers.escriptorium import connect_escriptorium, EScriptoriumClient + from picarones.extras.importers.escriptorium import connect_escriptorium, EScriptoriumClient with patch.object(EScriptoriumClient, "test_connection", return_value=True): client = connect_escriptorium("https://myinstance.org", token="tok") assert "myinstance.org" in client.base_url def test_connect_timeout_passed(self): - from picarones.importers.escriptorium import connect_escriptorium, EScriptoriumClient + from picarones.extras.importers.escriptorium import connect_escriptorium, EScriptoriumClient with patch.object(EScriptoriumClient, "test_connection", return_value=True): client = connect_escriptorium("https://example.org", token="tok", timeout=120) assert client.timeout == 120 @@ -162,7 +162,7 @@ class TestEScriptoriumExport: def _make_benchmark(self, engine_name: str = "tesseract") -> "BenchmarkResult": from picarones.core.results import BenchmarkResult, EngineReport, DocumentResult - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult metrics = MetricsResult(cer=0.05, wer=0.10, cer_nfc=0.05, cer_caseless=0.04, cer_diplomatic=0.04, wer_normalized=0.09, mer=0.09, wil=0.05, @@ -189,14 +189,14 @@ class TestEScriptoriumExport: ) def test_export_unknown_engine_raises(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") bm = self._make_benchmark("tesseract") with pytest.raises(ValueError, match="unknown_engine"): client.export_benchmark_as_layer(bm, doc_pk=1, engine_name="unknown_engine") def test_export_returns_count(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") bm = self._make_benchmark("tesseract") with patch.object(client, "_post", return_value={}): @@ -206,7 +206,7 @@ class TestEScriptoriumExport: assert count == 1 def test_export_layer_name_default(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") bm = self._make_benchmark("tesseract") calls = [] @@ -215,7 +215,7 @@ class TestEScriptoriumExport: assert calls[0]["name"] == "picarones_tesseract" def test_export_custom_layer_name(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") bm = self._make_benchmark("tesseract") calls = [] @@ -226,9 +226,9 @@ class TestEScriptoriumExport: assert calls[0]["name"] == "my_layer" def test_export_skips_error_docs(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient from picarones.core.results import BenchmarkResult, EngineReport, DocumentResult - from picarones.core.metrics import MetricsResult + from picarones.measurements.metrics import MetricsResult metrics = MetricsResult(cer=0.1, wer=0.2, cer_nfc=0.1, cer_caseless=0.1, cer_diplomatic=0.1, wer_normalized=0.2, mer=0.2, wil=0.1, reference_length=50, hypothesis_length=50) @@ -244,7 +244,7 @@ class TestEScriptoriumExport: assert count == 1 # seul le doc sans erreur est exporté def test_export_with_part_mapping(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") bm = self._make_benchmark("tesseract") calls = [] @@ -256,7 +256,7 @@ class TestEScriptoriumExport: assert "999" in calls[0] def test_export_post_error_is_logged_not_raised(self): - from picarones.importers.escriptorium import EScriptoriumClient + from picarones.extras.importers.escriptorium import EScriptoriumClient client = EScriptoriumClient("https://example.org", token="tok") bm = self._make_benchmark("tesseract") with patch.object(client, "_post", side_effect=RuntimeError("500")): @@ -264,7 +264,7 @@ class TestEScriptoriumExport: assert count == 0 def test_document_result_as_dict_used(self): - from picarones.importers.escriptorium import EScriptoriumDocument + from picarones.extras.importers.escriptorium import EScriptoriumDocument d = EScriptoriumDocument(pk=42, name="Doc", project="1", part_count=10, transcription_layers=["manual", "auto"]) d_dict = d.as_dict() @@ -279,22 +279,22 @@ class TestEScriptoriumExport: class TestGallicaRecord: def test_import_module(self): - from picarones.importers.gallica import GallicaRecord + from picarones.extras.importers.gallica import GallicaRecord assert GallicaRecord is not None def test_ark_property(self): - from picarones.importers.gallica import GallicaRecord + from picarones.extras.importers.gallica import GallicaRecord r = GallicaRecord(ark="12148/btv1b8453561w", title="Test") assert "12148/btv1b8453561w" in r.url def test_manifest_url(self): - from picarones.importers.gallica import GallicaRecord + from picarones.extras.importers.gallica import GallicaRecord r = GallicaRecord(ark="12148/btv1b8453561w", title="Test") assert "manifest.json" in r.manifest_url assert "12148/btv1b8453561w" in r.manifest_url def test_as_dict_keys(self): - from picarones.importers.gallica import GallicaRecord + from picarones.extras.importers.gallica import GallicaRecord r = GallicaRecord(ark="12148/btv1b8453561w", title="Froissart", creator="Froissart") d = r.as_dict() assert "ark" in d @@ -303,12 +303,12 @@ class TestGallicaRecord: assert "url" in d def test_has_ocr_default_false(self): - from picarones.importers.gallica import GallicaRecord + from picarones.extras.importers.gallica import GallicaRecord r = GallicaRecord(ark="12148/xxx", title="Test") assert r.has_ocr is False def test_has_ocr_true(self): - from picarones.importers.gallica import GallicaRecord + from picarones.extras.importers.gallica import GallicaRecord r = GallicaRecord(ark="12148/xxx", title="Test", has_ocr=True) assert r.has_ocr is True @@ -320,31 +320,31 @@ class TestGallicaRecord: class TestGallicaClient: def test_import_module(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient assert GallicaClient is not None def test_init_defaults(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() assert client.timeout == 30 assert client.delay >= 0 def test_search_returns_list(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient(delay_between_requests=0) with patch.object(client, "_fetch_url", side_effect=RuntimeError("network")): results = client.search(title="Froissart", max_results=5) assert isinstance(results, list) def test_search_empty_on_network_error(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient(delay_between_requests=0) with patch.object(client, "_fetch_url", side_effect=RuntimeError("timeout")): results = client.search(title="test") assert results == [] def test_get_ocr_text_returns_string(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient(delay_between_requests=0) with patch.object(client, "_fetch_url", return_value=b"Froissart transcription"): text = client.get_ocr_text("12148/btv1b8453561w", page=1) @@ -352,7 +352,7 @@ class TestGallicaClient: assert "Froissart" in text def test_get_ocr_text_empty_on_html_response(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient(delay_between_requests=0) html = b"Page non disponible" with patch.object(client, "_fetch_url", return_value=html): @@ -360,14 +360,14 @@ class TestGallicaClient: assert text == "" def test_get_ocr_text_empty_on_error(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient(delay_between_requests=0) with patch.object(client, "_fetch_url", side_effect=RuntimeError("404")): text = client.get_ocr_text("12148/xxx", page=99) assert text == "" def test_get_metadata_returns_dict(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient(delay_between_requests=0) xml_bytes = b""" @@ -401,13 +401,13 @@ class TestGallicaClient: assert records == [] def test_parse_sru_invalid_xml_returns_empty(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient(delay_between_requests=0) records = client._parse_sru_response(b"not xml at all !!!", max_results=10) assert records == [] def test_client_has_delay_attribute(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient(delay_between_requests=0.1) assert client.delay == 0.1 @@ -419,46 +419,46 @@ class TestGallicaClient: class TestGallicaSearchQuery: def test_build_query_title(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() query = client._build_sru_query(title="Froissart") assert "Froissart" in query assert "dc.title" in query def test_build_query_author(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() query = client._build_sru_query(author="Froissart") assert "dc.creator" in query def test_build_query_date_range(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() query = client._build_sru_query(date_from=1380, date_to=1420) assert "1380" in query assert "1420" in query def test_build_query_date_from_only(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() query = client._build_sru_query(date_from=1400) assert "1400" in query assert ">=" in query def test_build_query_ark(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() query = client._build_sru_query(ark="12148/btv1b8453561w") assert "12148/btv1b8453561w" in query def test_build_query_empty_returns_default(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() query = client._build_sru_query() assert len(query) > 0 def test_build_query_combined(self): - from picarones.importers.gallica import GallicaClient + from picarones.extras.importers.gallica import GallicaClient client = GallicaClient() query = client._build_sru_query(title="Froissart", author="Jean", date_from=1380) assert "Froissart" in query @@ -466,7 +466,7 @@ class TestGallicaSearchQuery: assert "1380" in query def test_search_gallica_function(self): - from picarones.importers.gallica import search_gallica, GallicaClient + from picarones.extras.importers.gallica import search_gallica, GallicaClient with patch.object(GallicaClient, "search", return_value=[]): results = search_gallica(title="test") assert isinstance(results, list) @@ -479,18 +479,18 @@ class TestGallicaSearchQuery: class TestGallicaOCR: def test_ocr_url_format(self): - from picarones.importers import gallica as g + from picarones.extras.importers import gallica as g url = g._OCR_BRUT_TPL.format(ark="12148/btv1b8453561w", page=3) assert "12148/btv1b8453561w" in url assert "f3" in url assert "texteBrut" in url def test_import_gallica_document_function_exists(self): - from picarones.importers.gallica import import_gallica_document + from picarones.extras.importers.gallica import import_gallica_document assert callable(import_gallica_document) def test_gallica_base_url(self): - from picarones.importers import gallica as g + from picarones.extras.importers import gallica as g assert "gallica.bnf.fr" in g._GALLICA_BASE def test_ark_normalization_in_import(self): @@ -502,13 +502,13 @@ class TestGallicaOCR: assert m.group(1) == "12148/btv1b8453561w" def test_iiif_manifest_url_pattern(self): - from picarones.importers import gallica as g + from picarones.extras.importers import gallica as g url = g._IIIF_MANIFEST_TPL.format(ark="12148/btv1b8453561w") assert "manifest.json" in url assert "12148/btv1b8453561w" in url def test_gallica_record_url_structure(self): - from picarones.importers.gallica import GallicaRecord + from picarones.extras.importers.gallica import GallicaRecord r = GallicaRecord(ark="12148/btv1b8453561w", title="Test") assert r.url.startswith("https://gallica.bnf.fr") assert "12148/btv1b8453561w" in r.url @@ -521,19 +521,19 @@ class TestGallicaOCR: class TestImportersInit: def test_escriptorium_client_exported(self): - from picarones.importers import EScriptoriumClient + from picarones.extras.importers import EScriptoriumClient assert EScriptoriumClient is not None def test_gallica_client_exported(self): - from picarones.importers import GallicaClient + from picarones.extras.importers import GallicaClient assert GallicaClient is not None def test_search_gallica_exported(self): - from picarones.importers import search_gallica + from picarones.extras.importers import search_gallica assert callable(search_gallica) def test_connect_escriptorium_exported(self): - from picarones.importers import connect_escriptorium + from picarones.extras.importers import connect_escriptorium assert callable(connect_escriptorium) diff --git a/tests/test_sprint8_longitudinal_robustness.py b/tests/test_sprint8_longitudinal_robustness.py index 076dbe0606e2c861278395737666105fc363950d..6357095deb94370f59947cb4ea2302aaa6ed7c0f 100644 --- a/tests/test_sprint8_longitudinal_robustness.py +++ b/tests/test_sprint8_longitudinal_robustness.py @@ -29,11 +29,11 @@ class TestBenchmarkHistory: @pytest.fixture def db(self): - from picarones.core.history import BenchmarkHistory + from picarones.measurements.history import BenchmarkHistory return BenchmarkHistory(":memory:") def test_import_module(self): - from picarones.core.history import BenchmarkHistory + from picarones.measurements.history import BenchmarkHistory assert BenchmarkHistory is not None def test_init_in_memory(self, db): @@ -142,11 +142,11 @@ class TestBenchmarkHistory: class TestHistoryEntry: def test_import(self): - from picarones.core.history import HistoryEntry + from picarones.measurements.history import HistoryEntry assert HistoryEntry is not None def test_cer_percent(self): - from picarones.core.history import HistoryEntry + from picarones.measurements.history import HistoryEntry entry = HistoryEntry( run_id="r1", timestamp="2025-01-01T00:00:00+00:00", corpus_name="C", engine_name="tesseract", @@ -155,12 +155,12 @@ class TestHistoryEntry: assert abs(entry.cer_percent - 12.0) < 0.01 def test_cer_percent_none(self): - from picarones.core.history import HistoryEntry + from picarones.measurements.history import HistoryEntry entry = HistoryEntry("r", "2025", "C", "e", None, None, 0) assert entry.cer_percent is None def test_as_dict_keys(self): - from picarones.core.history import HistoryEntry + from picarones.measurements.history import HistoryEntry entry = HistoryEntry("r1", "2025-01-01", "C", "tesseract", 0.10, 0.18, 5) d = entry.as_dict() assert "run_id" in d @@ -168,14 +168,14 @@ class TestHistoryEntry: assert "engine_name" in d def test_as_dict_metadata(self): - from picarones.core.history import HistoryEntry + from picarones.measurements.history import HistoryEntry entry = HistoryEntry("r1", "2025-01-01", "C", "tesseract", 0.10, 0.18, 5, metadata={"key": "value"}) d = entry.as_dict() assert d["metadata"] == {"key": "value"} def test_query_result_is_history_entry(self): - from picarones.core.history import BenchmarkHistory, HistoryEntry + from picarones.measurements.history import BenchmarkHistory, HistoryEntry db = BenchmarkHistory(":memory:") db.record_single("r1", "C", "tesseract", 0.10, 0.18, 5) entries = db.query() @@ -190,7 +190,7 @@ class TestRegressionResult: @pytest.fixture def db_with_runs(self): - from picarones.core.history import BenchmarkHistory + from picarones.measurements.history import BenchmarkHistory db = BenchmarkHistory(":memory:") db.record_single("r1", "C", "tesseract", 0.12, 0.20, 10, timestamp="2025-01-01T00:00:00+00:00") db.record_single("r2", "C", "tesseract", 0.15, 0.25, 10, timestamp="2025-06-01T00:00:00+00:00") @@ -212,7 +212,7 @@ class TestRegressionResult: assert result.current_cer is not None def test_detect_no_regression(self): - from picarones.core.history import BenchmarkHistory + from picarones.measurements.history import BenchmarkHistory db = BenchmarkHistory(":memory:") # CER diminue = amélioration = pas de régression db.record_single("r1", "C", "tesseract", 0.15, 0.25, 5, timestamp="2025-01-01T00:00:00+00:00") @@ -222,14 +222,14 @@ class TestRegressionResult: assert result.is_regression is False def test_detect_regression_none_if_single_run(self): - from picarones.core.history import BenchmarkHistory + from picarones.measurements.history import BenchmarkHistory db = BenchmarkHistory(":memory:") db.record_single("r1", "C", "tesseract", 0.12, 0.20, 5) result = db.detect_regression("tesseract") assert result is None def test_detect_all_regressions(self): - from picarones.core.history import BenchmarkHistory + from picarones.measurements.history import BenchmarkHistory db = BenchmarkHistory(":memory:") db.record_single("r1", "C", "tesseract", 0.10, 0.18, 5, timestamp="2025-01-01T00:00:00+00:00") db.record_single("r2", "C", "tesseract", 0.20, 0.35, 5, timestamp="2025-06-01T00:00:00+00:00") @@ -244,7 +244,7 @@ class TestRegressionResult: assert "engine_name" in d def test_regression_threshold_respected(self): - from picarones.core.history import BenchmarkHistory + from picarones.measurements.history import BenchmarkHistory db = BenchmarkHistory(":memory:") db.record_single("r1", "C", "tesseract", 0.100, 0.18, 5, timestamp="2025-01-01T00:00:00+00:00") db.record_single("r2", "C", "tesseract", 0.105, 0.19, 5, timestamp="2025-06-01T00:00:00+00:00") @@ -264,27 +264,27 @@ class TestRegressionResult: class TestGenerateDemoHistory: def test_generate_fills_db(self): - from picarones.core.history import BenchmarkHistory, generate_demo_history + from picarones.measurements.history import BenchmarkHistory, generate_demo_history db = BenchmarkHistory(":memory:") generate_demo_history(db, n_runs=5) assert db.count() > 0 def test_generate_creates_multiple_engines(self): - from picarones.core.history import BenchmarkHistory, generate_demo_history + from picarones.measurements.history import BenchmarkHistory, generate_demo_history db = BenchmarkHistory(":memory:") generate_demo_history(db, n_runs=4) engines = db.list_engines() assert len(engines) >= 2 def test_generate_n_runs(self): - from picarones.core.history import BenchmarkHistory, generate_demo_history + from picarones.measurements.history import BenchmarkHistory, generate_demo_history db = BenchmarkHistory(":memory:") generate_demo_history(db, n_runs=8) # 8 runs × 3 moteurs = 24 entrées assert db.count() == 8 * 3 def test_cer_values_in_range(self): - from picarones.core.history import BenchmarkHistory, generate_demo_history + from picarones.measurements.history import BenchmarkHistory, generate_demo_history db = BenchmarkHistory(":memory:") generate_demo_history(db, n_runs=5) entries = db.query() @@ -294,7 +294,7 @@ class TestGenerateDemoHistory: def test_regression_detectable_in_demo(self): """La démo inclut une régression simulée au run 5 (tesseract).""" - from picarones.core.history import BenchmarkHistory, generate_demo_history + from picarones.measurements.history import BenchmarkHistory, generate_demo_history db = BenchmarkHistory(":memory:") generate_demo_history(db, n_runs=8, seed=42) # Vérifier que l'historique a été créé @@ -311,33 +311,33 @@ class TestGenerateDemoHistory: class TestDegradationLevels: def test_import_constants(self): - from picarones.core.robustness import DEGRADATION_LEVELS, ALL_DEGRADATION_TYPES + from picarones.measurements.robustness import DEGRADATION_LEVELS, ALL_DEGRADATION_TYPES assert len(DEGRADATION_LEVELS) > 0 assert len(ALL_DEGRADATION_TYPES) > 0 def test_all_types_in_levels(self): - from picarones.core.robustness import DEGRADATION_LEVELS, ALL_DEGRADATION_TYPES + from picarones.measurements.robustness import DEGRADATION_LEVELS, ALL_DEGRADATION_TYPES for t in ALL_DEGRADATION_TYPES: assert t in DEGRADATION_LEVELS def test_noise_levels(self): - from picarones.core.robustness import DEGRADATION_LEVELS + from picarones.measurements.robustness import DEGRADATION_LEVELS levels = DEGRADATION_LEVELS["noise"] assert len(levels) >= 2 assert 0 in levels # niveau original def test_blur_levels(self): - from picarones.core.robustness import DEGRADATION_LEVELS + from picarones.measurements.robustness import DEGRADATION_LEVELS levels = DEGRADATION_LEVELS["blur"] assert 0 in levels def test_resolution_levels_include_1(self): - from picarones.core.robustness import DEGRADATION_LEVELS + from picarones.measurements.robustness import DEGRADATION_LEVELS levels = DEGRADATION_LEVELS["resolution"] assert 1.0 in levels # résolution originale def test_labels_match_levels(self): - from picarones.core.robustness import DEGRADATION_LEVELS, DEGRADATION_LABELS + from picarones.measurements.robustness import DEGRADATION_LEVELS, DEGRADATION_LABELS for dtype in DEGRADATION_LEVELS: if dtype in DEGRADATION_LABELS: assert len(DEGRADATION_LABELS[dtype]) == len(DEGRADATION_LEVELS[dtype]) @@ -355,60 +355,60 @@ class TestDegradationFunctions: return _make_placeholder_png(40, 30) def test_degrade_image_bytes_imports(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes assert callable(degrade_image_bytes) def test_degrade_noise_returns_bytes(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "noise", 0) assert isinstance(result, bytes) assert len(result) > 0 def test_degrade_blur_returns_bytes(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "blur", 0) assert isinstance(result, bytes) def test_degrade_rotation_returns_bytes(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "rotation", 0) assert isinstance(result, bytes) def test_degrade_resolution_returns_bytes(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "resolution", 1.0) assert isinstance(result, bytes) def test_degrade_binarization_returns_bytes(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "binarization", 0) assert isinstance(result, bytes) def test_degrade_noise_level_5(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "noise", 5) assert isinstance(result, bytes) def test_degrade_blur_level_2(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "blur", 2) assert isinstance(result, bytes) def test_degrade_resolution_half(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "resolution", 0.5) assert isinstance(result, bytes) def test_degrade_rotation_10_degrees(self): - from picarones.core.robustness import degrade_image_bytes + from picarones.measurements.robustness import degrade_image_bytes png = self._make_png() result = degrade_image_bytes(png, "rotation", 10) assert isinstance(result, bytes) @@ -421,11 +421,11 @@ class TestDegradationFunctions: class TestDegradationCurve: def test_import(self): - from picarones.core.robustness import DegradationCurve + from picarones.measurements.robustness import DegradationCurve assert DegradationCurve is not None def test_as_dict_keys(self): - from picarones.core.robustness import DegradationCurve + from picarones.measurements.robustness import DegradationCurve curve = DegradationCurve( engine_name="tesseract", degradation_type="noise", @@ -440,7 +440,7 @@ class TestDegradationCurve: assert "cer_values" in d def test_critical_threshold(self): - from picarones.core.robustness import DegradationCurve + from picarones.measurements.robustness import DegradationCurve curve = DegradationCurve( engine_name="tesseract", degradation_type="noise", @@ -453,7 +453,7 @@ class TestDegradationCurve: assert curve.critical_threshold_level == 15 def test_none_cer_allowed(self): - from picarones.core.robustness import DegradationCurve + from picarones.measurements.robustness import DegradationCurve curve = DegradationCurve( engine_name="e", degradation_type="blur", @@ -464,17 +464,17 @@ class TestDegradationCurve: assert curve.cer_values[0] is None def test_default_cer_threshold(self): - from picarones.core.robustness import DegradationCurve + from picarones.measurements.robustness import DegradationCurve curve = DegradationCurve("e", "noise", [0], ["o"], [0.1]) assert curve.cer_threshold == 0.20 def test_engine_name_preserved(self): - from picarones.core.robustness import DegradationCurve + from picarones.measurements.robustness import DegradationCurve curve = DegradationCurve("pero_ocr", "blur", [0, 1], ["o", "r=1"], [0.05, 0.08]) assert curve.engine_name == "pero_ocr" def test_as_dict_roundtrip(self): - from picarones.core.robustness import DegradationCurve + from picarones.measurements.robustness import DegradationCurve curve = DegradationCurve( engine_name="tesseract", degradation_type="rotation", @@ -495,11 +495,11 @@ class TestDegradationCurve: class TestRobustnessReport: def test_import(self): - from picarones.core.robustness import RobustnessReport + from picarones.measurements.robustness import RobustnessReport assert RobustnessReport is not None def test_get_curves_for_engine(self): - from picarones.core.robustness import RobustnessReport, DegradationCurve + from picarones.measurements.robustness import RobustnessReport, DegradationCurve c1 = DegradationCurve("tesseract", "noise", [0, 5], ["o", "σ=5"], [0.10, 0.15]) c2 = DegradationCurve("pero_ocr", "noise", [0, 5], ["o", "σ=5"], [0.07, 0.10]) report = RobustnessReport(["tesseract", "pero_ocr"], "C", ["noise"], [c1, c2]) @@ -508,7 +508,7 @@ class TestRobustnessReport: assert tess_curves[0].engine_name == "tesseract" def test_get_curves_for_type(self): - from picarones.core.robustness import RobustnessReport, DegradationCurve + from picarones.measurements.robustness import RobustnessReport, DegradationCurve c1 = DegradationCurve("tesseract", "noise", [0, 5], ["o", "σ=5"], [0.10, 0.15]) c2 = DegradationCurve("tesseract", "blur", [0, 2], ["o", "r=2"], [0.10, 0.14]) report = RobustnessReport(["tesseract"], "C", ["noise", "blur"], [c1, c2]) @@ -517,7 +517,7 @@ class TestRobustnessReport: assert noise_curves[0].degradation_type == "noise" def test_as_dict_keys(self): - from picarones.core.robustness import RobustnessReport + from picarones.measurements.robustness import RobustnessReport report = RobustnessReport(["tesseract"], "C", ["noise"], []) d = report.as_dict() assert "engine_names" in d @@ -525,7 +525,7 @@ class TestRobustnessReport: assert "summary" in d def test_as_dict_json_serializable(self): - from picarones.core.robustness import RobustnessReport, DegradationCurve + from picarones.measurements.robustness import RobustnessReport, DegradationCurve c = DegradationCurve("e", "noise", [0, 5], ["o", "n5"], [0.1, 0.2]) report = RobustnessReport(["e"], "C", ["noise"], [c]) d = report.as_dict() @@ -534,18 +534,18 @@ class TestRobustnessReport: assert len(json_str) > 0 def test_summary_populated(self): - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report report = generate_demo_robustness_report(engine_names=["tesseract"], seed=1) assert isinstance(report.summary, dict) assert len(report.summary) > 0 def test_corpus_name_preserved(self): - from picarones.core.robustness import RobustnessReport + from picarones.measurements.robustness import RobustnessReport report = RobustnessReport(["e"], "Mon Corpus", ["noise"], []) assert report.corpus_name == "Mon Corpus" def test_engine_names_list(self): - from picarones.core.robustness import RobustnessReport + from picarones.measurements.robustness import RobustnessReport report = RobustnessReport(["tesseract", "pero_ocr"], "C", [], []) assert "tesseract" in report.engine_names assert "pero_ocr" in report.engine_names @@ -558,17 +558,17 @@ class TestRobustnessReport: class TestRobustnessAnalyzer: def test_import(self): - from picarones.core.robustness import RobustnessAnalyzer + from picarones.measurements.robustness import RobustnessAnalyzer assert RobustnessAnalyzer is not None def test_init_single_engine(self): - from picarones.core.robustness import RobustnessAnalyzer + from picarones.measurements.robustness import RobustnessAnalyzer mock_engine = type("E", (), {"name": "tesseract"})() analyzer = RobustnessAnalyzer(mock_engine) assert len(analyzer.engines) == 1 def test_init_list_engines(self): - from picarones.core.robustness import RobustnessAnalyzer + from picarones.measurements.robustness import RobustnessAnalyzer engines = [ type("E", (), {"name": "tesseract"})(), type("E", (), {"name": "pero_ocr"})(), @@ -577,33 +577,33 @@ class TestRobustnessAnalyzer: assert len(analyzer.engines) == 2 def test_default_degradation_types(self): - from picarones.core.robustness import RobustnessAnalyzer, ALL_DEGRADATION_TYPES + from picarones.measurements.robustness import RobustnessAnalyzer, ALL_DEGRADATION_TYPES e = type("E", (), {"name": "e"})() analyzer = RobustnessAnalyzer(e) assert set(analyzer.degradation_types) == set(ALL_DEGRADATION_TYPES) def test_custom_degradation_types(self): - from picarones.core.robustness import RobustnessAnalyzer + from picarones.measurements.robustness import RobustnessAnalyzer e = type("E", (), {"name": "e"})() analyzer = RobustnessAnalyzer(e, degradation_types=["noise", "blur"]) assert analyzer.degradation_types == ["noise", "blur"] def test_find_critical_level_found(self): - from picarones.core.robustness import RobustnessAnalyzer + from picarones.measurements.robustness import RobustnessAnalyzer levels = [0, 5, 15, 30] cer_values = [0.10, 0.15, 0.22, 0.35] critical = RobustnessAnalyzer._find_critical_level(levels, cer_values, 0.20) assert critical == 15 def test_find_critical_level_none(self): - from picarones.core.robustness import RobustnessAnalyzer + from picarones.measurements.robustness import RobustnessAnalyzer levels = [0, 5, 15] cer_values = [0.05, 0.10, 0.15] critical = RobustnessAnalyzer._find_critical_level(levels, cer_values, 0.20) assert critical is None def test_build_summary(self): - from picarones.core.robustness import RobustnessAnalyzer, DegradationCurve + from picarones.measurements.robustness import RobustnessAnalyzer, DegradationCurve curves = [ DegradationCurve("tesseract", "noise", [0, 5], ["o", "n5"], [0.10, 0.20]), DegradationCurve("pero_ocr", "noise", [0, 5], ["o", "n5"], [0.07, 0.12]), @@ -620,33 +620,33 @@ class TestRobustnessAnalyzer: class TestGenerateDemoRobustness: def test_import(self): - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report assert callable(generate_demo_robustness_report) def test_returns_report(self): - from picarones.core.robustness import generate_demo_robustness_report, RobustnessReport + from picarones.measurements.robustness import generate_demo_robustness_report, RobustnessReport report = generate_demo_robustness_report() assert isinstance(report, RobustnessReport) def test_default_engines(self): - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report report = generate_demo_robustness_report() assert "tesseract" in report.engine_names assert "pero_ocr" in report.engine_names def test_custom_engines(self): - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report report = generate_demo_robustness_report(engine_names=["moteur_custom"]) assert "moteur_custom" in report.engine_names def test_all_degradation_types_present(self): - from picarones.core.robustness import generate_demo_robustness_report, ALL_DEGRADATION_TYPES + from picarones.measurements.robustness import generate_demo_robustness_report, ALL_DEGRADATION_TYPES report = generate_demo_robustness_report() types_in_report = {c.degradation_type for c in report.curves} assert types_in_report == set(ALL_DEGRADATION_TYPES) def test_cer_values_in_range(self): - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report report = generate_demo_robustness_report(seed=99) for curve in report.curves: for cer in curve.cer_values: @@ -655,7 +655,7 @@ class TestGenerateDemoRobustness: def test_cer_increases_with_degradation(self): """Pour la plupart des types, le CER doit augmenter avec le niveau de dégradation.""" - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report report = generate_demo_robustness_report(seed=42) for curve in report.curves: valid = [c for c in curve.cer_values if c is not None] @@ -667,18 +667,18 @@ class TestGenerateDemoRobustness: ) def test_reproducible_with_seed(self): - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report r1 = generate_demo_robustness_report(seed=7) r2 = generate_demo_robustness_report(seed=7) assert r1.curves[0].cer_values == r2.curves[0].cer_values def test_summary_contains_most_robust(self): - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report report = generate_demo_robustness_report() assert any("most_robust" in k for k in report.summary) def test_json_serializable(self): - from picarones.core.robustness import generate_demo_robustness_report + from picarones.measurements.robustness import generate_demo_robustness_report report = generate_demo_robustness_report() d = report.as_dict() json_str = json.dumps(d, ensure_ascii=False) diff --git a/tests/test_sprint90_engine_unstable.py b/tests/test_sprint90_engine_unstable.py index 885c58db44a3cadd0d23376de2ce6b50a8a9032e..6a22c3f3c9ac90f0b59ea3e6601f01bbe5b77b5b 100644 --- a/tests/test_sprint90_engine_unstable.py +++ b/tests/test_sprint90_engine_unstable.py @@ -21,9 +21,9 @@ import json import re from pathlib import Path -from picarones.core.narrative import build_synthesis -from picarones.core.narrative.detectors import detect_engine_unstable -from picarones.core.narrative.facts import FactImportance, FactType +from picarones.measurements.narrative import build_synthesis +from picarones.measurements.narrative.detectors import detect_engine_unstable +from picarones.core.facts import FactImportance, FactType from picarones.report.multirun_stability_render import ( build_multirun_stability_html, ) @@ -47,7 +47,7 @@ class TestFactType: assert FactType.ENGINE_UNSTABLE.value == "engine_unstable" def test_in_arbiter_fallback_order(self) -> None: - from picarones.core.narrative.arbiter import _FALLBACK_TYPE_ORDER + from picarones.measurements.narrative.arbiter import _FALLBACK_TYPE_ORDER assert FactType.ENGINE_UNSTABLE in _FALLBACK_TYPE_ORDER diff --git a/tests/test_sprint91_throughput.py b/tests/test_sprint91_throughput.py index 542d78863d61cf819724c308259c67ce9d40c92d..7eead5f344f574e47470bad09dded200dc855ece 100644 --- a/tests/test_sprint91_throughput.py +++ b/tests/test_sprint91_throughput.py @@ -20,11 +20,11 @@ from pathlib import Path import pytest -from picarones.core.marginal_cost import ( +from picarones.measurements.marginal_cost import ( compute_marginal_cost, compute_marginal_cost_matrix, ) -from picarones.core.throughput import ( +from picarones.measurements.throughput import ( aggregate_effective_throughput, compute_effective_throughput, ) diff --git a/tests/test_sprint92_longitudinal.py b/tests/test_sprint92_longitudinal.py index 997c720f3ae83c40af805634e21652138337a058..89b0d5811a927fd0ceed09827e9e18d4607f352a 100644 --- a/tests/test_sprint92_longitudinal.py +++ b/tests/test_sprint92_longitudinal.py @@ -24,15 +24,15 @@ from pathlib import Path import pytest -from picarones.core.longitudinal import ( +from picarones.measurements.longitudinal import ( compute_corpus_longitudinal, compute_engine_longitudinal, compute_linear_trend, detect_change_point, ) -from picarones.core.narrative import build_synthesis -from picarones.core.narrative.detectors import detect_regression_in_history -from picarones.core.narrative.facts import FactImportance, FactType +from picarones.measurements.narrative import build_synthesis +from picarones.measurements.narrative.detectors import detect_regression_in_history +from picarones.core.facts import FactImportance, FactType from picarones.report.longitudinal_render import build_longitudinal_html diff --git a/tests/test_sprint93_image_predictive.py b/tests/test_sprint93_image_predictive.py index 3cdb7e9889bbb0de18939f00d2b92dc8c1c3fffe..74e636f9e6d8bd023340be5a61510487c21df52d 100644 --- a/tests/test_sprint93_image_predictive.py +++ b/tests/test_sprint93_image_predictive.py @@ -29,7 +29,7 @@ from pathlib import Path import pytest -from picarones.core.image_predictive import ( +from picarones.measurements.image_predictive import ( DEFAULT_COMPLEXITY_WEIGHTS, aggregate_corpus_predictive, compute_corpus_homogeneity, diff --git a/tests/test_sprint94_error_absorption.py b/tests/test_sprint94_error_absorption.py index c6176e7d50ce5775334ff774d64223d7319ab91c..270f6acec881ae94ff2d710bcf24fcf4a99db5bc 100644 --- a/tests/test_sprint94_error_absorption.py +++ b/tests/test_sprint94_error_absorption.py @@ -25,7 +25,7 @@ from pathlib import Path import pytest -from picarones.core.error_absorption import ( +from picarones.measurements.error_absorption import ( aggregate_error_absorption, compute_error_absorption, ) diff --git a/tests/test_sprint96_incremental_comparison.py b/tests/test_sprint96_incremental_comparison.py index 1ed3f02ed94abe0e20bb99cd91c55400a1ad3aae..3e3625a8cdf3c540912dd0caf66c61ddc0dde5d3 100644 --- a/tests/test_sprint96_incremental_comparison.py +++ b/tests/test_sprint96_incremental_comparison.py @@ -27,7 +27,7 @@ from pathlib import Path import pytest -from picarones.core.incremental_comparison import ( +from picarones.measurements.incremental_comparison import ( PipelineRun, compare_isolated_effect, ) diff --git a/tests/test_sprint97_module_policy.py b/tests/test_sprint97_module_policy.py index 968e8f454dd53f47c816dd99121b52185b7a9751..c34c724417e49a48211650043e84f8dd7407309a 100644 --- a/tests/test_sprint97_module_policy.py +++ b/tests/test_sprint97_module_policy.py @@ -28,7 +28,7 @@ from __future__ import annotations import json from pathlib import Path -from picarones.core.module_policy import ( +from picarones.measurements.module_policy import ( AuditCheck, AuditResult, ModuleManifest,