From 495fe153b1b285b59c2d171cf789b0d943b71734 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Mon, 13 Jul 2026 09:44:24 +0200 Subject: [PATCH] feat(grid): export_dataframe scopable par base_where_sql/base_params (#41) --- src/utils/grid.py | 17 ++++++++++++++--- tests/test_grid.py | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/utils/grid.py b/src/utils/grid.py index 5dc79a7..8a4748a 100644 --- a/src/utils/grid.py +++ b/src/utils/grid.py @@ -68,17 +68,28 @@ def fetch_grid_page( return rows, total, total_unique -def export_dataframe(filter_model, sort_model, hidden_columns) -> pl.DataFrame: +def export_dataframe( + filter_model, + sort_model, + hidden_columns, + base_where_sql: str = "TRUE", + base_params: tuple = (), +) -> pl.DataFrame: """Renvoie les lignes filtrées/triées pour l'export Excel. Colonnes masquées exclues, valeurs brutes (non post-traitées HTML). + `base_where_sql`/`base_params` scopent l'export à un sous-ensemble (ex. un + acheteur/titulaire) — combinés en `(base) AND (filtre)`, comme + `fetch_grid_page`. Par défaut `TRUE` → comportement inchangé pour tableau.py. """ ast = filtermodel_to_ast(filter_model, schema) - filter_sql, params = ast_to_sql(ast, schema) + filter_sql, filter_params = ast_to_sql(ast, schema) + where_sql = f"({base_where_sql}) AND ({filter_sql})" + params = [*base_params, *filter_params] order_by = sort_model_to_sql(sort_model, schema) or None visible = [c for c in schema.names() if c not in set(hidden_columns or [])] return query_marches( - where_sql=filter_sql, + where_sql=where_sql, params=params, columns=visible, order_by=order_by, diff --git a/tests/test_grid.py b/tests/test_grid.py index bbed6d7..38ecc00 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -92,6 +92,26 @@ def test_export_dataframe_applies_filter(): assert df.height == 0 +def test_export_dataframe_applies_base_scope(): + """base_where_sql restreint l'export à un sous-ensemble (ex. un acheteur), + en plus du filterModel — utilisé par acheteur.py/titulaire.py (#41).""" + # Récupère un acheteur_id présent dans le jeu de test. + unscoped = export_dataframe(None, None, hidden_columns=[]) + assert unscoped.height > 0 + an_acheteur = unscoped["acheteur_id"][0] + + scoped = export_dataframe( + None, + None, + hidden_columns=[], + base_where_sql="acheteur_id = ?", + base_params=(an_acheteur,), + ) + assert scoped.height > 0 + assert scoped.height <= unscoped.height + assert set(scoped["acheteur_id"].to_list()) == {an_acheteur} + + def test_get_rows_tableau_tracks_search_once_per_filter_not_per_scroll_block(): """Régression revue finale #41 : AG Grid envoie une getRowsRequest par bloc de défilement infini, avec le même filterModel tant que le filtre ne