60 lines
2.8 KiB
Python
60 lines
2.8 KiB
Python
"""Base forms of inflected Slovak names, used only without the model.
|
|
|
|
CRZP matches whole words, so "Jána Ptáka" cannot be found as written. Without
|
|
the model only the surname is searched, in a few candidate nominatives (most
|
|
likely first); the caller keeps the first candidate whose rows really contain
|
|
the asked person, because the row filter compares the stems of all asked
|
|
words (first name included). A wrong guess therefore finds nothing.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from tools.crzp.source.parse import fold
|
|
|
|
# ending -> nominative replacements in order of preference (lowercase).
|
|
_RULES = [
|
|
("ovej", ["ová"]), ("ovou", ["ová"]), ("ovú", ["ová"]), # Novákovej -> Nováková
|
|
("skej", ["ská"]), ("ckej", ["cká"]), ("skou", ["ská"]), # Hronskej -> Hronská
|
|
("ského", ["ský"]), ("ckého", ["cký"]), ("ého", ["ý"]), ("ému", ["ý"]), ("ým", ["ý"]), # Malého -> Malý
|
|
("ovi", ["", "a"]), ("om", ["", "a"]), ("ou", ["a"]), # Ptákovi -> Pták, Plevom -> Pleva
|
|
("a", [""]), ("u", ["a", ""]), ("y", ["a"]), ("e", ["a", ""]), # Ptáka -> Pták, Galajdu -> Galajda
|
|
]
|
|
MAX_CANDIDATES = 3
|
|
|
|
|
|
def word_forms(word: str) -> list[str]:
|
|
"""Candidate nominatives of one name word, most likely first, the word itself last."""
|
|
low, forms = word.lower(), []
|
|
for ending, replacements in _RULES:
|
|
if low.endswith(ending) and len(low) - len(ending) >= 2:
|
|
for replacement in replacements:
|
|
base = low[: len(low) - len(ending)] + replacement
|
|
if replacement == "" and len(base) >= 3 and base[-1] in "kcrl" and base[-2] not in "aeiouyáéíóúýô":
|
|
forms += [base[:-1] + "e" + base[-1], base[:-1] + "o" + base[-1]] # Hládka -> Hládek, Pavla -> Pavol
|
|
forms.append(base)
|
|
break
|
|
forms.append(low)
|
|
seen, result = set(), []
|
|
for form in forms:
|
|
if len(form) >= 2 and fold(form) not in seen:
|
|
seen.add(fold(form))
|
|
result.append(form[:1].upper() + form[1:] if word[:1].isupper() else form)
|
|
return result
|
|
|
|
|
|
def surname_variants(name: str) -> list[str]:
|
|
"""Search candidates for the surname of an inflected name: "Daniela Hládka" -> ["Hládek", ...]."""
|
|
words = name.split()
|
|
return word_forms(words[-1])[:MAX_CANDIDATES] if words else []
|
|
|
|
|
|
def name_variants(name: str) -> list[str]:
|
|
"""Search texts for an inflected name, most likely first: the likeliest first
|
|
name with each surname candidate, then each surname candidate alone (the
|
|
first name is still checked in the rows, e.g. "Pavla" matches "Pavol")."""
|
|
words = name.split()
|
|
surnames = surname_variants(name)
|
|
if len(words) < 2:
|
|
return surnames
|
|
first = word_forms(words[0])[0]
|
|
return list(dict.fromkeys([f"{first} {s}" for s in surnames] + surnames))
|