Files
colibre/docs/superpowers/specs/2026-06-22-observatoire-considerations-card-design.md
2026-06-22 18:09:05 +02:00

4.2 KiB

Tuile « Considérations sociales et environnementales » — Observatoire

Objectif

Ajouter dans /observatoire une tuile (card) qui visualise, à l'aide de barres de progression « plus ou moins remplies », la part des marchés publics filtrés qui comportent au moins une considération sociale et la part qui comportent au moins une considération environnementale.

La tuile s'insère juste après la tuile « Type d'achat », avec le même style que les autres cards.

Données

Colonnes concernées (type String, valeurs libres potentiellement composées) :

  • considerationsSociales
  • considerationsEnvironnementales

Exemples de valeurs : Sans objet, Clause sociale, Critère social, Marché réservé, Clause environnementale, Critère environnemental, Pas de considération sociale, null, ou des combinaisons (Critère social, Clause sociale).

Définition « au moins une considération »

Un marché compte comme ayant une considération si la valeur de la colonne contient l'un des mots-clés (insensible à la casse) :

  • Clause
  • Critère
  • Marché réservé

Regex utilisée : (?i)Clause|Critère|Marché réservé.

Conséquence (validée avec l'utilisateur) : Marché réservé compte comme considération sociale. Les valeurs Sans objet, Pas de considération… et null ne contiennent aucun de ces mots-clés et ne comptent donc pas.

Calcul du pourcentage

  • Dédoublonnage par uid : un marché est compté une seule fois même s'il apparaît sur plusieurs lignes (plusieurs titulaires). On prend la première valeur de chaque colonne par uid.
  • Dénominateur : tous les marchés filtrés (y compris Sans objet et non renseignés) — validé avec l'utilisateur.
  • Numérateur : nombre de marchés (uid distincts) dont la valeur de colonne satisfait la regex.
  • pourcentage = round(100 * numérateur / dénominateur) ; si dénominateur = 0, pourcentage = 0.

Composant visuel

Nouvelle fonction get_considerations_card_content(lff: pl.LazyFrame) dans src/figures.py, renvoyant un html.Div contenant deux barres dbc.Progress empilées :

Considération Couleur (px.colors.qualitative.Safe) Valeur RGB
Sociales index 1 (rouge) rgb(204, 102, 119)
Environnementales index 3 (vert) rgb(17, 119, 51)

Chaque barre :

  • dbc.Progress(value=pourcentage, label=f"{pourcentage} %", style={"backgroundColor": <couleur>})
  • précédée d'un libellé (Sociales / Environnementales) et suivie du nombre de marchés concernés (N marchés), formaté avec format_number.

Robustesse (colonne absente)

tests/test.parquet peut ne pas contenir ces colonnes. La fonction vérifie la présence de chaque colonne via lff.collect_schema().names() ; si une colonne manque, son pourcentage et son compte valent 0 (pas d'exception), à l'image de get_distance_histogram.

Intégration

Dans src/pages/observatoire.py, fonction _compute_dashboard_children :

donut_marche_type = make_donut(lff, "type", per_uid=True, nulls="?")
cards.append(make_card(title="Type d'achat", ...))

# NOUVEAU
considerations = get_considerations_card_content(lff)
cards.append(
    make_card(
        title="Considérations sociales et environnementales",
        subtitle="part des marchés concernés",
        fig=considerations,
    )
)

make_card utilise ses dimensions par défaut (lg=6, xl=4), comme la tuile « Type d'achat ».

Import à ajouter : get_considerations_card_content depuis src.figures.

Tests

  • Test unitaire de get_considerations_card_content sur un petit LazyFrame construit en mémoire couvrant : valeur avec considération, Sans objet, null, Marché réservé, doublon de uid. Vérifier les pourcentages attendus.
  • Cas colonne absente → 0 % sans exception.

Hors périmètre (YAGNI)

  • Pas de tooltip détaillé sur les types de considérations.
  • Pas de graphe de répartition par type (clause vs critère).
  • Pas de nouveau filtre (les filtres existants social/env restent inchangés).