From 26455a7616746c957a962b4478a49a2ec4fa68e8 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Mon, 22 Jun 2026 21:18:00 +0200 Subject: [PATCH] =?UTF-8?q?fix(observatoire):=20barre=20renseign=C3=A9e=20?= =?UTF-8?q?=3D=20positifs=20parmi=20les=20march=C3=A9s=20renseign=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dénominateur = marchés non-null ; numérateur = marchés positifs (regex). Label affiché : "parmi les N marchés renseignés". Co-Authored-By: Claude Sonnet 4.6 --- src/figures.py | 12 +++++++----- tests/test_figures.py | 23 +++++++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/figures.py b/src/figures.py index 87faba4..e96946a 100644 --- a/src/figures.py +++ b/src/figures.py @@ -739,9 +739,10 @@ def compute_considerations_stats(lff: pl.LazyFrame) -> dict[str, tuple[int, int] """Part des marchés (uid distincts) ayant au moins une considération. Renvoie pour chaque clé (sociales, environnementales) : - - (count, pct) : marchés avec au moins une considération (regex) - - (count, pct) suffixé _renseignees : marchés avec une valeur non nulle - Dénominateur = tous les uid distincts. Colonne absente -> (0, 0). + - (count_pos, pct) : positifs / tous les uid distincts + - (count_ren, pct) suffixé _renseignees : non-null uid comme dénominateur, + positifs comme numérateur (= part de positifs parmi les marchés renseignés) + Colonne absente -> (0, 0). """ names = lff.collect_schema().names() present = {key: col for key, col in CONSIDERATIONS_COLUMNS.items() if col in names} @@ -768,8 +769,9 @@ def compute_considerations_stats(lff: pl.LazyFrame) -> dict[str, tuple[int, int] for key, col in present.items(): count_pos = agg.filter(pl.col(col).str.contains(CONSIDERATIONS_REGEX)).height count_ren = agg.filter(pl.col(col).is_not_null()).height + pct_pos_ren = round(100 * count_pos / count_ren) if count_ren > 0 else 0 stats[key] = (count_pos, round(100 * count_pos / total)) - stats[f"{key}_renseignees"] = (count_ren, round(100 * count_ren / total)) + stats[f"{key}_renseignees"] = (count_ren, pct_pos_ren) return stats @@ -816,7 +818,7 @@ def get_considerations_card_content(lff: pl.LazyFrame) -> html.Div: className="d-flex justify-content-end mt-1", children=[ html.Span( - f"{format_number(count_ren)} marchés (renseignée)", + f"parmi les {format_number(count_ren)} marchés renseignés", className="text-muted", ), ], diff --git a/tests/test_figures.py b/tests/test_figures.py index 3ffb42b..0ccc82a 100644 --- a/tests/test_figures.py +++ b/tests/test_figures.py @@ -42,9 +42,10 @@ def test_compute_considerations_stats_basic(): # 4 marchés au total. Social : u1, u3 -> 2/4 = 50%. Env : u2 -> 1/4 = 25%. assert stats["sociales"] == (2, 50) assert stats["environnementales"] == (1, 25) - # Renseignées : social u1/u2/u3/u4 non null -> 4/4=100%. Env : u1/u2/u4 non null -> 3/4=75%. - assert stats["sociales_renseignees"] == (4, 100) - assert stats["environnementales_renseignees"] == (3, 75) + # Renseignées : dénominateur = non-null ; numérateur = positifs. + # Social : 4 non-null, 2 positifs -> (4, 50%). Env : 3 non-null, 1 positif -> (3, 33%). + assert stats["sociales_renseignees"] == (4, 50) + assert stats["environnementales_renseignees"] == (3, 33) def test_compute_considerations_stats_dedup_per_uid(): @@ -76,8 +77,9 @@ def test_compute_considerations_stats_dedup_per_uid(): # 2 marchés distincts. Social : u1 -> 1/2 = 50%. assert stats["sociales"] == (1, 50) assert stats["environnementales"] == (0, 0) - assert stats["sociales_renseignees"] == (2, 100) - assert stats["environnementales_renseignees"] == (2, 100) + # Social : 2 non-null, 1 positif -> (2, 50%). Env : 2 non-null, 0 positif -> (2, 0%). + assert stats["sociales_renseignees"] == (2, 50) + assert stats["environnementales_renseignees"] == (2, 0) def test_compute_considerations_stats_missing_column(): @@ -95,7 +97,8 @@ def test_compute_considerations_stats_missing_column(): # Colonne env absente -> (0, 0) sans exception. Social : 1/2 = 50%. assert stats["sociales"] == (1, 50) assert stats["environnementales"] == (0, 0) - assert stats["sociales_renseignees"] == (2, 100) + # Social : 2 non-null, 1 positif -> (2, 50%). Env absente -> (0, 0). + assert stats["sociales_renseignees"] == (2, 50) assert stats["environnementales_renseignees"] == (0, 0) @@ -164,13 +167,13 @@ def test_get_considerations_card_content_returns_four_progress_bars(): assert social_pos.value == 50 assert social_pos.color == "#CC6677" assert social_pos.style["color"] == "white" - # Sociales renseignées : u1 + u2 non null -> 2/2 = 100% - assert social_ren.value == 100 + # Sociales renseignées : 2 non-null, 1 positif (u1) -> 50% + assert social_ren.value == 50 assert social_ren.color == "#E5B2BB" # Environnementales positives : u2 -> 1/2 = 50% assert env_pos.value == 50 assert env_pos.color == "#117733" assert env_pos.style["color"] == "white" - # Environnementales renseignées : u1 (Sans objet) + u2 -> 2/2 = 100% - assert env_ren.value == 100 + # Environnementales renseignées : 2 non-null (u1 "Sans objet", u2), 1 positif (u2) -> 50% + assert env_ren.value == 50 assert env_ren.color == "#88BB99"