Files
colibre/tests/test_figures.py
T
Colin Maudry 19e181734c feat(observatoire): ajouter barres 'valeur renseignée' dans tuile considérations
Affiche 4 barres groupées par type (sociales/env) : une pour les marchés
avec au moins une considération, une pour ceux qui ont une valeur renseignée
(non nulle, y compris 'Sans objet').

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 18:55:38 +02:00

177 lines
5.8 KiB
Python

import polars as pl
def _make_lff(rows):
return pl.LazyFrame(rows)
def test_compute_considerations_stats_basic():
from src.figures import compute_considerations_stats
lff = _make_lff(
[
# uid u1 : social oui (Clause), env non (Sans objet)
{
"uid": "u1",
"considerationsSociales": "Clause sociale",
"considerationsEnvironnementales": "Sans objet",
},
# uid u2 : social non (Sans objet), env oui (Critère)
{
"uid": "u2",
"considerationsSociales": "Sans objet",
"considerationsEnvironnementales": "Critère environnemental",
},
# uid u3 : social oui (Marché réservé compte), env null
{
"uid": "u3",
"considerationsSociales": "Marché réservé",
"considerationsEnvironnementales": None,
},
# uid u4 : aucune considération
{
"uid": "u4",
"considerationsSociales": "Pas de considération sociale",
"considerationsEnvironnementales": "Sans objet",
},
]
)
stats = compute_considerations_stats(lff)
# 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)
def test_compute_considerations_stats_dedup_per_uid():
from src.figures import compute_considerations_stats
lff = _make_lff(
[
# uid u1 présent 2 fois (2 titulaires) -> compté une seule fois
{
"uid": "u1",
"considerationsSociales": "Clause sociale",
"considerationsEnvironnementales": "Sans objet",
},
{
"uid": "u1",
"considerationsSociales": "Clause sociale",
"considerationsEnvironnementales": "Sans objet",
},
{
"uid": "u2",
"considerationsSociales": "Sans objet",
"considerationsEnvironnementales": "Sans objet",
},
]
)
stats = compute_considerations_stats(lff)
# 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)
def test_compute_considerations_stats_missing_column():
from src.figures import compute_considerations_stats
lff = _make_lff(
[
{"uid": "u1", "considerationsSociales": "Clause sociale"},
{"uid": "u2", "considerationsSociales": "Sans objet"},
]
)
stats = compute_considerations_stats(lff)
# 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)
assert stats["environnementales_renseignees"] == (0, 0)
def test_compute_considerations_stats_empty():
from src.figures import compute_considerations_stats
lff = pl.LazyFrame(
{
"uid": pl.Series([], dtype=pl.String),
"considerationsSociales": pl.Series([], dtype=pl.String),
"considerationsEnvironnementales": pl.Series([], dtype=pl.String),
}
)
stats = compute_considerations_stats(lff)
assert stats["sociales"] == (0, 0)
assert stats["environnementales"] == (0, 0)
assert stats["sociales_renseignees"] == (0, 0)
assert stats["environnementales_renseignees"] == (0, 0)
def test_get_considerations_card_content_returns_four_progress_bars():
import dash_bootstrap_components as dbc
from dash import html
from src.figures import get_considerations_card_content
lff = pl.LazyFrame(
[
{
"uid": "u1",
"considerationsSociales": "Clause sociale",
"considerationsEnvironnementales": "Sans objet",
},
{
"uid": "u2",
"considerationsSociales": "Sans objet",
"considerationsEnvironnementales": "Critère environnemental",
},
]
)
div = get_considerations_card_content(lff)
assert isinstance(div, html.Div)
def find_progress(component, found):
if isinstance(component, dbc.Progress):
found.append(component)
children = getattr(component, "children", None)
if isinstance(children, (list, tuple)):
for c in children:
find_progress(c, found)
elif children is not None and not isinstance(children, str):
find_progress(children, found)
return found
all_bars = find_progress(div, [])
inner_bars = [b for b in all_bars if getattr(b, "bar", False)]
# 4 barres internes : 2 positives + 2 renseignées
assert len(inner_bars) == 4
social_pos, social_ren, env_pos, env_ren = inner_bars
# Sociales positives : u1 -> 1/2 = 50%
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
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
assert env_ren.color == "#88BB99"