fix(observatoire): barre renseignée = positifs parmi les marchés renseignés
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 <noreply@anthropic.com>
This commit is contained in:
+7
-5
@@ -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.
|
"""Part des marchés (uid distincts) ayant au moins une considération.
|
||||||
|
|
||||||
Renvoie pour chaque clé (sociales, environnementales) :
|
Renvoie pour chaque clé (sociales, environnementales) :
|
||||||
- (count, pct) : marchés avec au moins une considération (regex)
|
- (count_pos, pct) : positifs / tous les uid distincts
|
||||||
- (count, pct) suffixé _renseignees : marchés avec une valeur non nulle
|
- (count_ren, pct) suffixé _renseignees : non-null uid comme dénominateur,
|
||||||
Dénominateur = tous les uid distincts. Colonne absente -> (0, 0).
|
positifs comme numérateur (= part de positifs parmi les marchés renseignés)
|
||||||
|
Colonne absente -> (0, 0).
|
||||||
"""
|
"""
|
||||||
names = lff.collect_schema().names()
|
names = lff.collect_schema().names()
|
||||||
present = {key: col for key, col in CONSIDERATIONS_COLUMNS.items() if col in 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():
|
for key, col in present.items():
|
||||||
count_pos = agg.filter(pl.col(col).str.contains(CONSIDERATIONS_REGEX)).height
|
count_pos = agg.filter(pl.col(col).str.contains(CONSIDERATIONS_REGEX)).height
|
||||||
count_ren = agg.filter(pl.col(col).is_not_null()).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[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
|
return stats
|
||||||
|
|
||||||
@@ -816,7 +818,7 @@ def get_considerations_card_content(lff: pl.LazyFrame) -> html.Div:
|
|||||||
className="d-flex justify-content-end mt-1",
|
className="d-flex justify-content-end mt-1",
|
||||||
children=[
|
children=[
|
||||||
html.Span(
|
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",
|
className="text-muted",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
+13
-10
@@ -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%.
|
# 4 marchés au total. Social : u1, u3 -> 2/4 = 50%. Env : u2 -> 1/4 = 25%.
|
||||||
assert stats["sociales"] == (2, 50)
|
assert stats["sociales"] == (2, 50)
|
||||||
assert stats["environnementales"] == (1, 25)
|
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%.
|
# Renseignées : dénominateur = non-null ; numérateur = positifs.
|
||||||
assert stats["sociales_renseignees"] == (4, 100)
|
# Social : 4 non-null, 2 positifs -> (4, 50%). Env : 3 non-null, 1 positif -> (3, 33%).
|
||||||
assert stats["environnementales_renseignees"] == (3, 75)
|
assert stats["sociales_renseignees"] == (4, 50)
|
||||||
|
assert stats["environnementales_renseignees"] == (3, 33)
|
||||||
|
|
||||||
|
|
||||||
def test_compute_considerations_stats_dedup_per_uid():
|
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%.
|
# 2 marchés distincts. Social : u1 -> 1/2 = 50%.
|
||||||
assert stats["sociales"] == (1, 50)
|
assert stats["sociales"] == (1, 50)
|
||||||
assert stats["environnementales"] == (0, 0)
|
assert stats["environnementales"] == (0, 0)
|
||||||
assert stats["sociales_renseignees"] == (2, 100)
|
# Social : 2 non-null, 1 positif -> (2, 50%). Env : 2 non-null, 0 positif -> (2, 0%).
|
||||||
assert stats["environnementales_renseignees"] == (2, 100)
|
assert stats["sociales_renseignees"] == (2, 50)
|
||||||
|
assert stats["environnementales_renseignees"] == (2, 0)
|
||||||
|
|
||||||
|
|
||||||
def test_compute_considerations_stats_missing_column():
|
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%.
|
# Colonne env absente -> (0, 0) sans exception. Social : 1/2 = 50%.
|
||||||
assert stats["sociales"] == (1, 50)
|
assert stats["sociales"] == (1, 50)
|
||||||
assert stats["environnementales"] == (0, 0)
|
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)
|
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.value == 50
|
||||||
assert social_pos.color == "#CC6677"
|
assert social_pos.color == "#CC6677"
|
||||||
assert social_pos.style["color"] == "white"
|
assert social_pos.style["color"] == "white"
|
||||||
# Sociales renseignées : u1 + u2 non null -> 2/2 = 100%
|
# Sociales renseignées : 2 non-null, 1 positif (u1) -> 50%
|
||||||
assert social_ren.value == 100
|
assert social_ren.value == 50
|
||||||
assert social_ren.color == "#E5B2BB"
|
assert social_ren.color == "#E5B2BB"
|
||||||
# Environnementales positives : u2 -> 1/2 = 50%
|
# Environnementales positives : u2 -> 1/2 = 50%
|
||||||
assert env_pos.value == 50
|
assert env_pos.value == 50
|
||||||
assert env_pos.color == "#117733"
|
assert env_pos.color == "#117733"
|
||||||
assert env_pos.style["color"] == "white"
|
assert env_pos.style["color"] == "white"
|
||||||
# Environnementales renseignées : u1 (Sans objet) + u2 -> 2/2 = 100%
|
# Environnementales renseignées : 2 non-null (u1 "Sans objet", u2), 1 positif (u2) -> 50%
|
||||||
assert env_ren.value == 100
|
assert env_ren.value == 50
|
||||||
assert env_ren.color == "#88BB99"
|
assert env_ren.color == "#88BB99"
|
||||||
|
|||||||
Reference in New Issue
Block a user