Refactor structure modules pages/compte, petits changements de texte

This commit is contained in:
Colin Maudry
2026-07-02 10:53:01 +02:00
parent 9ff2373ccb
commit cd107a0213
12 changed files with 62 additions and 37 deletions
+2
View File
@@ -16,6 +16,7 @@ build
# Runtime databases (never commit)
users.sqlite
*.sqlite
*.sqlite-*
!tests/*.sqlite
!tests/**/*.sqlite
@@ -23,3 +24,4 @@ users.sqlite
.superpowers/
.codegraph/
.claude
*.duckdb*
+46
View File
@@ -0,0 +1,46 @@
import os
from dash import dcc, html, register_page
from src.figures import get_sources_tables
from src.pages._apropos_shell import apropos_shell
from src.utils.seo import META_CONTENT
register_page(
__name__,
path="/a-propos/donnees",
title="Données | À propos | colibre",
description="Données brutes, qualité des données et sources utilisées par colibre pour consolider les marchés publics français.",
image_url=META_CONTENT["image_url"],
)
def layout(**_):
contenu = html.Div(
[
html.H2("Consommer les données brutes"),
dcc.Markdown(
"""
Vous pouvez consommer les données qui alimentent colibre en les téléchargeant [sur data.gouv.fr](https://www.data.gouv.fr/datasets/donnees-essentielles-de-la-commande-publique-consolidees-format-tabulaire) (Parquet, CSV), pensez à lire la description du jeu de données
Une API REST tabulaire (JSON) est également disponible par abonnement mensuel pour accéder aux mêmes données et alimenter une application.
Documentation interactive : [Swagger UI](/api/v1/swagger). Si cela vous intéresse, [contactez-moi](/a-propos/contact)."""
),
html.H2("Qualité et exhaustivité des données", className="mt-4"),
dcc.Markdown(
"""Les données visibles sur ce site proviennent exclusivement de la publication de données
ouvertes par les acheteurs publics ou en leur nom, régie par
[l'arrêté du 22 décembre 2022](https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000046850496). Leur
qualité est donc principalement liée à la qualité de leur saisie par les agents publics, parfois
peu aidé·es par la qualité des outils à leur disposition.
Je pense que l'analyse de marchés
individuels et le comptage de marchés sur des critères autres que financiers sont plutôt fiables. En revanche, certains montants de marché estimés à des valeurs farfelues ([1 euro](https://colibre.fr/marches/432766947000192025S01301), [1 milliard](https://colibre.fr/marches/2459004280001320210000000271)) faussent les calculs par aggrégation (sommes, moyennes, médianes) et donc la production de statistiques financières fiables. Acheteurs, acheteuses : s'il vous plaît, essayez d'estimer les montants des marchés publics attribués de manière plus précise.
Quant à l'exhaustivité, je consolide toutes les sources de données exploitables que j'ai pu identifier (voir [Sources de données](/a-propos/donnees)). Je tiens à souligner la belle continuité de la publication par la DGFiP des données des marchés publics remontées via le [protocole PES](https://www.collectivites-locales.gouv.fr/finances-locales/le-protocole-dechange-standard-pes). Merci à leurs équipes."""
),
html.H2("Sources de données", className="mt-4"),
get_sources_tables(os.getenv("SOURCE_STATS_CSV_PATH")),
]
)
return apropos_shell("donnees", contenu)
View File
@@ -3,6 +3,7 @@ from dash import Input, Output, State, callback, dcc, html, register_page
from flask_login import current_user
from src.pages._compte_shell import account_guard, account_shell
from src.pages.a_propos.abonnement import abonnement_features
from src.subscriptions import db, plans
from src.utils.frontend import format_date_french
@@ -74,31 +75,7 @@ def _plan_cards(trial_used=False, trial_for=plans.trial_days):
def _explainer():
col_left = dbc.Col(
[
html.H4("Ce que les abonnements financent"),
html.Ul(
[
html.Li("Abonnement Frisbii (solution de paiement) : 50 €"),
html.Li("Serveur Scaleway : 40 €"),
html.Li("Espace de coworking : 250 €"),
html.Li(
[
"Salaire médian (",
html.Span(
"coût employeur",
id="salaire-modal-trigger",
style={
"cursor": "pointer",
"textDecoration": "underline",
"color": "var(--bs-link-color)",
},
),
") : 3 840 €",
]
),
]
),
],
[html.H4("Fonctionnalités réservées aux abonné•es :"), abonnement_features],
md=6,
style={
"borderRight": "1px solid var(--bs-border-color)",
+1 -1
View File
@@ -1,5 +1,5 @@
def test_module_imports_and_registers():
from src.pages import compte_roadmap
from src.pages.compte import roadmap as compte_roadmap
assert callable(compte_roadmap.layout)
assert callable(compte_roadmap.cast_vote)
+1 -1
View File
@@ -11,7 +11,7 @@ import pytest
# enregistrés), elle est ré-enregistrée → "Duplicate callback outputs"
# (salaire-modal, resiliation-modal) qui casse le rendu de TOUTES les pages dans
# la suite Selenium complète. En important src.app ici, la découverte tourne en
# premier et les imports ultérieurs de compte_abonnement sont mis en cache.
# premier et les imports ultérieurs de src.pages.compte.abonnement sont mis en cache.
from src.app import app # noqa: F401, E402
+10 -10
View File
@@ -1,7 +1,7 @@
def test_plan_cards_present_when_no_subscription(monkeypatch):
monkeypatch.setenv("FRISBII_PLAN_SIMPLE", "plan_simple")
monkeypatch.setenv("FRISBII_PLAN_SOUTIEN", "plan_soutien")
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
cards = compte_abonnement._plan_cards(trial_for=lambda key: 2)
text = str(cards)
@@ -13,7 +13,7 @@ def test_plan_cards_present_when_no_subscription(monkeypatch):
def test_plan_cards_no_trial_when_trial_used(monkeypatch):
monkeypatch.setenv("FRISBII_PLAN_SIMPLE", "plan_simple")
monkeypatch.setenv("FRISBII_PLAN_SOUTIEN", "plan_soutien")
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
text = str(compte_abonnement._plan_cards(trial_used=True, trial_for=lambda key: 2))
assert "Sans période d'essai" in text
@@ -21,7 +21,7 @@ def test_plan_cards_no_trial_when_trial_used(monkeypatch):
def test_active_view_shows_cancel(monkeypatch):
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
row = {
"plan": "simple",
@@ -33,7 +33,7 @@ def test_active_view_shows_cancel(monkeypatch):
def test_active_view_trial_banner(monkeypatch):
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
row = {
"plan": "simple",
@@ -47,7 +47,7 @@ def test_subscribe_buttons_disabled_when_tous_abonnes(monkeypatch):
monkeypatch.setenv("FRISBII_PLAN_SIMPLE", "plan_simple")
monkeypatch.setenv("FRISBII_PLAN_SOUTIEN", "plan_soutien")
monkeypatch.setattr("src.utils.TOUS_ABONNES", True)
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
text = str(compte_abonnement._plan_cards(trial_for=lambda key: 2))
assert "btn-secondary disabled" in text
@@ -58,7 +58,7 @@ def test_subscribe_buttons_active_when_flag_off(monkeypatch):
monkeypatch.setenv("FRISBII_PLAN_SIMPLE", "plan_simple")
monkeypatch.setenv("FRISBII_PLAN_SOUTIEN", "plan_soutien")
monkeypatch.setattr("src.utils.TOUS_ABONNES", False)
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
text = str(compte_abonnement._plan_cards(trial_for=lambda key: 2))
assert "btn-primary" in text
@@ -66,7 +66,7 @@ def test_subscribe_buttons_active_when_flag_off(monkeypatch):
def test_banner_present_when_tous_abonnes(monkeypatch):
monkeypatch.setattr("src.utils.TOUS_ABONNES", True)
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
text = str(compte_abonnement._tous_abonnes_banner())
assert "accessibles à tous et toutes" in text
@@ -74,20 +74,20 @@ def test_banner_present_when_tous_abonnes(monkeypatch):
def test_banner_absent_when_flag_off(monkeypatch):
monkeypatch.setattr("src.utils.TOUS_ABONNES", False)
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
assert compte_abonnement._tous_abonnes_banner() is None
def test_show_active_view_true_for_live_statuses(monkeypatch):
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
for status in ("pending", "trial", "active", "cancelled", "expired"):
assert compte_abonnement._show_active_view({"status": status}) is True
def test_show_active_view_false_for_failed_or_none(monkeypatch):
from src.pages import compte_abonnement
from src.pages.compte import abonnement as compte_abonnement
assert compte_abonnement._show_active_view({"status": "failed"}) is False
assert compte_abonnement._show_active_view(None) is False