1745 wystapien w 291 plikach (backend, scripts, mobile). Dominowal jeden ksztalt, ' — ' laczace zdania, wiec poszedl przecinek; myslnik otwierajacy mysl zdjety; zakresy 0–5 i A–Z na dywiz. Podzial komentarz/kod robil skaner stanow, nie regex, zeby nie ruszyc myslnika stojacego w stringu albo w wyrazeniu regularnym. Potwierdzone maszynowo: po wycieciu komentarzy z wersji HEAD i z dysku obie sa identyczne we wszystkich 291 plikach, czyli poza komentarzami nie zmienilo sie nic. 233 pliki .py parsuja sie bez bledu, tsc ma te same 11 bledow w tych samych 3 plikach co przed zmiana. Dwie rzeczy wymagaly poprawki po automacie: wyrownane listy definicji, gdzie myslnik byl separatorem kolumny (37 miejsc, kolumna przywrocona), i trzy przeniesienia mysli do nowej linii, gdzie zostawal wiszacy przecinek na poczatku (poprawione recznie). Bundla nie republikuje: Hermes i tak nie niesie komentarzy, wiec dla uzytkownika to zero zmiany. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
61 lines
1.8 KiB
Python
61 lines
1.8 KiB
Python
"""Normalizacja tekstu używana wszędzie w pipeline'ie matchingu.
|
|
|
|
Cel: dwie formy tej samej nazwy (różne casing, akcenty, interpunkcja, whitespace, „the"/myślniki)
|
|
muszą produkować identyczny string po `normalize`. Trgram index na tych polach robi resztę.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import unicodedata
|
|
|
|
from slugify import slugify as _slugify
|
|
|
|
_NON_ASCII_ALNUM_RE = re.compile(r"[^a-z0-9\s]+")
|
|
_WS_RE = re.compile(r"\s+")
|
|
_LEADING_ARTICLE_RE = re.compile(r"^(the|a|an)\s+", flags=re.IGNORECASE)
|
|
|
|
# Litery których NFKD nie rozkłada na ASCII, mapujemy ręcznie.
|
|
_EXTRA_TRANSLIT = str.maketrans(
|
|
{
|
|
"Ł": "L", "ł": "l",
|
|
"Ø": "O", "ø": "o",
|
|
"Æ": "AE", "æ": "ae",
|
|
"Œ": "OE", "œ": "oe",
|
|
"Þ": "Th", "þ": "th",
|
|
"Ð": "D", "ð": "d",
|
|
"Đ": "D", "đ": "d",
|
|
"ß": "ss",
|
|
"Ħ": "H", "ħ": "h",
|
|
}
|
|
)
|
|
|
|
|
|
def strip_accents(value: str) -> str:
|
|
pre = value.translate(_EXTRA_TRANSLIT)
|
|
nfkd = unicodedata.normalize("NFKD", pre)
|
|
return "".join(c for c in nfkd if not unicodedata.combining(c))
|
|
|
|
|
|
def normalize(value: str | None) -> str:
|
|
"""Lower + strip accents + remove punctuation + collapse whitespace + drop leading article."""
|
|
if not value:
|
|
return ""
|
|
out = strip_accents(value).lower()
|
|
out = _NON_ASCII_ALNUM_RE.sub(" ", out)
|
|
out = _WS_RE.sub(" ", out).strip()
|
|
out = _LEADING_ARTICLE_RE.sub("", out)
|
|
return out
|
|
|
|
|
|
def normalize_person(value: str | None) -> str:
|
|
"""Normalizacja imion: jak `normalize`, plus zwija inicjały „M." → „m"."""
|
|
if not value:
|
|
return ""
|
|
out = normalize(value)
|
|
return _WS_RE.sub(" ", out).strip()
|
|
|
|
|
|
def slugify(value: str | None) -> str:
|
|
if not value:
|
|
return ""
|
|
return _slugify(value, lowercase=True, max_length=200)
|