Callback URL → filtres sur la page observatoire #65

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Colin Maudry
2026-03-18 14:55:41 +01:00
parent bf1791635f
commit 3745f6df74
3 changed files with 57 additions and 2 deletions
+32 -2
View File
@@ -1,9 +1,21 @@
import urllib.parse
from datetime import datetime, timedelta from datetime import datetime, timedelta
import dash_bootstrap_components as dbc import dash_bootstrap_components as dbc
import polars as pl import polars as pl
import polars.selectors as cs import polars.selectors as cs
from dash import ALL, Input, Output, State, callback, ctx, dcc, html, register_page from dash import (
ALL,
Input,
Output,
State,
callback,
ctx,
dcc,
html,
no_update,
register_page,
)
from src.figures import ( from src.figures import (
get_barchart_sources, get_barchart_sources,
@@ -107,7 +119,7 @@ def _apply_filters(
layout = [ layout = [
dcc.Store(id="dashboard-filters"), dcc.Store(id="dashboard-filters"),
dcc.Location(id="dashboard_url"), dcc.Location(id="dashboard_url", refresh="callback-nav"),
dbc.Modal( dbc.Modal(
[ [
dbc.ModalHeader(dbc.ModalTitle("Montants")), dbc.ModalHeader(dbc.ModalTitle("Montants")),
@@ -280,6 +292,24 @@ Alors, on fait comment ?
] ]
@callback(
Output("dashboard_acheteur_id", "value"),
Output("dashboard_titulaire_id", "value"),
Output("dashboard_url", "search"),
Input("dashboard_url", "search"),
)
def restore_filters_from_url(search):
if not search:
return no_update, no_update, no_update
params = urllib.parse.parse_qs(search.lstrip("?"))
acheteur_id = params.get("acheteur_id", [None])[0] or no_update
titulaire_id = params.get("titulaire_id", [None])[0] or no_update
return acheteur_id, titulaire_id, ""
@callback( @callback(
Output("cards", "children"), Output("cards", "children"),
Output("btn-download-observatoire", "disabled"), Output("btn-download-observatoire", "disabled"),
+5
View File
@@ -34,6 +34,11 @@ def test_data():
"sourceFile": "test.xml", "sourceFile": "test.xml",
"sourceDataset": "test_dataset", "sourceDataset": "test_dataset",
"datePublicationDonnees": datetime.date(2025, 1, 1), "datePublicationDonnees": datetime.date(2025, 1, 1),
"considerationsSociales": "",
"considerationsEnvironnementales": "",
"type": "Marché",
"acheteur_categorie": "Collectivité",
"titulaire_categorie": "PME",
} }
] ]
path = "tests/test.parquet" path = "tests/test.parquet"
+20
View File
@@ -137,3 +137,23 @@ def test_005_add_links_observatoire_titulaire():
# titulaire_id should NOT contain observatoire link # titulaire_id should NOT contain observatoire link
assert "/observatoire" not in id_value assert "/observatoire" not in id_value
def test_006_observatoire_url_to_input(dash_duo: DashComposite):
from src.app import app
dash_duo.start_server(app)
dash_duo.wait_for_text_to_equal(".logo > h1", "decp.info", timeout=4)
# Navigate to observatoire with acheteur_id query param
dash_duo.wait_for_page(f"{dash_duo.server_url}/observatoire?acheteur_id=a1")
dash_duo.wait_for_element("#dashboard_acheteur_id", timeout=4)
import time
time.sleep(1) # Allow callback chain to complete
acheteur_input = dash_duo.find_element("#dashboard_acheteur_id")
assert acheteur_input.get_attribute("value") == "a1", (
"acheteur_id input should be populated from URL param"
)