Recherche dans les tableaux insensible aux accents #79
This commit is contained in:
+25
-5
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user