Normalization of ä, ö,ü and ß in filters and search of duplicates
This commit is contained in:
+38
-6
@@ -4868,16 +4868,48 @@ def _software_job_filter_values(request: Request) -> dict[str, str]:
|
||||
}
|
||||
|
||||
|
||||
def _normalize_filter_text(value: Any) -> str:
|
||||
"""Normalize user-facing text for accent- and German umlaut-aware search."""
|
||||
import unicodedata
|
||||
|
||||
text = str(value or "").translate(
|
||||
str.maketrans(
|
||||
{
|
||||
"Ä": "Ae",
|
||||
"Ö": "Oe",
|
||||
"Ü": "Ue",
|
||||
"ä": "ae",
|
||||
"ö": "oe",
|
||||
"ü": "ue",
|
||||
"ẞ": "SS",
|
||||
"ß": "ss",
|
||||
}
|
||||
)
|
||||
)
|
||||
return "".join(
|
||||
character
|
||||
for character in unicodedata.normalize("NFKD", text.casefold())
|
||||
if not unicodedata.combining(character)
|
||||
)
|
||||
|
||||
|
||||
def _sql_normalized_filter_text(column: Any):
|
||||
"""Return the SQL equivalent of :func:`_normalize_filter_text`."""
|
||||
expression = func.lower(func.coalesce(cast(column, String), ""))
|
||||
for source, target in (("ä", "ae"), ("ö", "oe"), ("ü", "ue"), ("ß", "ss")):
|
||||
expression = func.replace(expression, source, target)
|
||||
return expression
|
||||
|
||||
|
||||
def _sql_text_contains(column: Any, value: str):
|
||||
"""Case-insensitive literal substring search for SQL text expressions."""
|
||||
"""Literal substring search with umlaut and ASCII spelling equivalence."""
|
||||
escaped = (
|
||||
str(value or "")
|
||||
.casefold()
|
||||
_normalize_filter_text(value)
|
||||
.replace("\\", "\\\\")
|
||||
.replace("%", "\\%")
|
||||
.replace("_", "\\_")
|
||||
)
|
||||
return func.lower(func.coalesce(cast(column, String), "")).like(
|
||||
return _sql_normalized_filter_text(column).like(
|
||||
f"%{escaped}%",
|
||||
escape="\\",
|
||||
)
|
||||
@@ -4894,7 +4926,7 @@ def _translated_filter_codes(
|
||||
The translation dictionaries are cached on the request so one filter
|
||||
request does not open a database session for every possible status value.
|
||||
"""
|
||||
needle = str(search_value or "").strip().casefold()
|
||||
needle = _normalize_filter_text(str(search_value or "").strip())
|
||||
if not needle:
|
||||
return []
|
||||
|
||||
@@ -4920,7 +4952,7 @@ def _translated_filter_codes(
|
||||
for code in codes:
|
||||
key = f"{prefix}.{code}"
|
||||
label = cache["own"].get(key) or cache["en"].get(key) or code
|
||||
if needle in code.casefold() or needle in str(label).casefold():
|
||||
if needle in _normalize_filter_text(code) or needle in _normalize_filter_text(label):
|
||||
matches.append(code)
|
||||
return matches
|
||||
|
||||
|
||||
Reference in New Issue
Block a user