Recherche dans les tableaux insensible aux accents #79

This commit is contained in:
Colin Maudry
2026-06-22 17:46:31 +02:00
parent 966d193ec4
commit 992f6d4f93
2 changed files with 52 additions and 5 deletions
+25 -5
View File
@@ -4,6 +4,7 @@ import uuid
import polars as pl import polars as pl
from dash import no_update from dash import no_update
from polars import selectors as cs from polars import selectors as cs
from unidecode import unidecode
from src.db import count_marches, count_unique_marches, query_marches, schema from src.db import count_marches, count_unique_marches, query_marches, schema
from src.utils import logger from src.utils import logger
@@ -215,6 +216,25 @@ def format_values(dff: pl.DataFrame) -> pl.DataFrame:
return dff return dff
_ACCENT_REPLACEMENTS = [
("[éèêëÉÈÊË]", "e"),
("[àâäÀÂÄ]", "a"),
("[ùûüÙÛÜ]", "u"),
("[îïÎÏ]", "i"),
("[ôöÔÖ]", "o"),
("[çÇ]", "c"),
("[ñÑ]", "n"),
("[æÆ]", "ae"),
("[œŒ]", "oe"),
]
def _deaccent_col(expr: pl.Expr) -> pl.Expr:
for pattern, replacement in _ACCENT_REPLACEMENTS:
expr = expr.str.replace_all(pattern, replacement)
return expr
def filter_table_data(lff: pl.LazyFrame, filter_query: str) -> pl.LazyFrame: def filter_table_data(lff: pl.LazyFrame, filter_query: str) -> pl.LazyFrame:
_schema = lff.collect_schema() _schema = lff.collect_schema()
filtering_expressions = filter_query.split(" && ") filtering_expressions = filter_query.split(" && ")
@@ -256,17 +276,17 @@ def filter_table_data(lff: pl.LazyFrame, filter_query: str) -> pl.LazyFrame:
elif operator == "contains": elif operator == "contains":
if col_type in ["String", "Date"] and isinstance(filter_value, str): if col_type in ["String", "Date"] and isinstance(filter_value, str):
filter_value = filter_value.strip('"') filter_value = filter_value.strip('"')
normalized_value = unidecode(filter_value)
col_expr = _deaccent_col(pl.col(col_name))
if filter_value.endswith("*"): if filter_value.endswith("*"):
lff = lff.filter( lff = lff.filter(
pl.col(col_name).str.starts_with(filter_value[:-1]) col_expr.str.starts_with(normalized_value[:-1])
) )
elif filter_value.startswith("*"): elif filter_value.startswith("*"):
lff = lff.filter( lff = lff.filter(col_expr.str.ends_with(normalized_value[1:]))
pl.col(col_name).str.ends_with(filter_value[1:])
)
else: else:
lff = lff.filter( lff = lff.filter(
pl.col(col_name).str.contains("(?i)" + filter_value) col_expr.str.contains("(?i)" + normalized_value)
) )
elif col_type.startswith("Int") or col_type.startswith("Float"): elif col_type.startswith("Int") or col_type.startswith("Float"):
lff = lff.filter(pl.col(col_name) == filter_value) lff = lff.filter(pl.col(col_name) == filter_value)
+27
View File
@@ -44,6 +44,33 @@ def test_filter_table_data_does_not_call_track_search(monkeypatch, sample_lff):
assert result.height == 1 assert result.height == 1
def test_filter_table_data_accent_insensitive():
"""Chercher sans accent doit trouver des valeurs accentuées, et vice versa."""
from src.utils.table import filter_table_data
lff = pl.LazyFrame(
[
{"uid": "1", "acheteur_nom": "Mairie de Nîmes", "objet": "Voirie"},
{"uid": "2", "acheteur_nom": "Commune de Reims", "objet": "Éclairage"},
{"uid": "3", "acheteur_nom": "Ville de Paris", "objet": "Travaux"},
]
)
# Sans accent → trouve la valeur accentuée
result = filter_table_data(lff, "{acheteur_nom} icontains Nimes").collect()
assert result.height == 1
assert result["uid"][0] == "1"
# Avec accent → trouve la valeur accentuée
result = filter_table_data(lff, "{acheteur_nom} icontains Nîmes").collect()
assert result.height == 1
# Sans accent → trouve la valeur avec accent initial (É)
result = filter_table_data(lff, "{objet} icontains eclairage").collect()
assert result.height == 1
assert result["uid"][0] == "2"
def test_normalize_sort_by_handles_empty(): def test_normalize_sort_by_handles_empty():
from src.utils.table import normalize_sort_by from src.utils.table import normalize_sort_by