import typing as t import pymorphy3 import re import ujson as json import pathlib from transliterate import translit from functools import lru_cache from .config import Keys, Paths from collections import defaultdict CACHE_SIZE = 256 class Matcher: def __init__(self): self.morph = pymorphy3.MorphAnalyzer(lang="ru") self.pairs: dict[str, tuple[float, bool]] = json.loads(pathlib.Path(Paths.PAIRS).read_text()) self.preprocess: dict[str, str] = json.loads(pathlib.Path(Paths.PREPROCESSOR).read_text()) def __call__(self, key: Keys) -> t.Callable: match key: case Keys.DIAG: return self.match_diags case Keys.DOC: return self.match_doctors raise KeyError def match_doctors(self, doc: str, other_doc: str) -> bool: if not doc or not other_doc: return False words: set[str] = self.text_to_words(doc) other_words: set[str] = self.text_to_words(other_doc) return len(words & other_words) / min(len(words), len(other_words), 1) > 0.49 def match_diags(self, raw_diag: str, raw_other_diag: str) -> bool: diag: str = self.preprocess.get(raw_diag, "") other_diag: str = self.preprocess.get(raw_other_diag, "") if not diag or not other_diag: return False if diag == other_diag: return True pair_check: bool | None = self.pairs.get(f"{diag}|{other_diag}", None) if pair_check is not None: return pair_check[1] reverse_pair_check: bool | None = self.pairs.get(f"{other_diag}|{diag}", None) if reverse_pair_check is not None: return reverse_pair_check[1] return True @lru_cache(maxsize=CACHE_SIZE) def text_to_words(self, input_text: str) -> set[str]: text = input_text.lower() text = translit(text, "ru") text = re.sub(r"[^а-я_]+", " ", text).strip() return set([self.morph.parse(word)[0].normal_form for word in text.split()])