arm-core/api/src/domain/statement_names.py

52 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Совпадение наименований строки формы со справочными (05-domain-rules §11.9, спека «Совпадение наименований»)."""
import difflib
import re
from collections.abc import Iterable
__all__ = ["names_match", "normalize", "section_number"]
_NON_ALNUM = re.compile(r"[\W_]+")
_TOTAL_PREFIX = "итого по разделу"
_MIN_CONTAINED = 5
_MIN_RATIO = 0.8
_ROMAN_LATIN = str.maketrans({"і": "i", "х": "x"}) # кириллические «і», «х» -> латиница
_ROMAN_HEAD = re.compile(r"[ivx]+")
def normalize(text: str) -> str:
"""Нижний регистр, `ё` -> `е`, всё кроме букв и цифр — пробел, схлопывание, `strip`."""
return _NON_ALNUM.sub(" ", text.lower().replace("ё", "е")).strip()
def section_number(normalized: str) -> str | None:
"""Номер раздела «Итого по разделу …» (строчными буквами i, v, x) из нормализованного имени; остаток слова —
хвост OCR («v1» -> «v») — отбрасывается. Имя не «итого по разделу» или номер не выделен — `None`."""
if not normalized.startswith(_TOTAL_PREFIX):
return None
words = normalized[len(_TOTAL_PREFIX) :].split()
if not words:
return None
head = _ROMAN_HEAD.match(words[0].translate(_ROMAN_LATIN))
return head.group() if head else None
def _match_pair(a: str, b: str) -> bool:
if a.startswith(_TOTAL_PREFIX) or b.startswith(_TOTAL_PREFIX):
na, nb = section_number(a), section_number(b)
return a == b if na is None or nb is None else na == nb
if a == b:
return True
short, long_ = sorted((a, b), key=len)
if len(short) >= _MIN_CONTAINED and short in long_:
return True
return difflib.SequenceMatcher(None, a, b).ratio() >= _MIN_RATIO
def names_match(recognized: str | None, references: Iterable[str]) -> bool:
"""Наименование совпало хотя бы с одним справочным; пустое имя или нет справочных — `False`."""
name = normalize(recognized or "")
if not name:
return False
return any(ref and _match_pair(name, ref) for ref in map(normalize, references))