import os from bs4 import BeautifulSoup from components.parser.xml.constants import ( ACTUAL_STATUSES, EXCLUDE_OWNERS, NAME_TAG, OWNER_TAGS, STATUS_TAG, USEFUL_STATUSES, ) from components.parser.xml.structures import ParsedXML class XMLInfoParser: """ Класс для парсинга основной информации из xml файлов. """ def __init__(self, soup: BeautifulSoup, filepath: os.PathLike): """ Инициализация парсера. Args: soup: BeautifulSoup - суп, содержащий xml документ filepath: os.PathLike - путь к файлу """ self.filepath = filepath self.soup = soup def parse(self) -> ParsedXML | None: """ Парсинг основной информации о xml файле. Returns: ParsedXML - информация о xml файле """ status = self._extract_info_value(STATUS_TAG) owner = self._extract_info_recurse(OWNER_TAGS, EXCLUDE_OWNERS) name = self._extract_info_value(NAME_TAG) if (name == '') and (status == '') and (owner == ''): status = ACTUAL_STATUSES[0] owner = '-' name = self.filepath.stem if status not in USEFUL_STATUSES: return None return ParsedXML(status=status, owner=owner, name=name, filename=self.filepath) def _extract_info_value(self, key: str) -> str: """ Извлечение значения из xml по тегу с использованием BeautifulSoup. Args: key: str - тег, по которому будет производиться поиск. Либо название документа, либо статус, либо владелец Returns: str - значение статуса, владельца или названия документа """ # Ищем тег в супе key_tag = self.soup.find(string=key) if not key_tag: return '' # Ищем следующий текстовый тег после ключевого next_tag = key_tag.find_next('w:t') if not next_tag: return '' return next_tag.get_text() def _extract_info_recurse( self, keys: list[str], excluding_values: list[str] | None = None, ) -> str: """ Извлечение значения из xml по нескольким тегам с использованием BeautifulSoup. Args: keys: list[str] - список тегов, по которым будет производиться поиск excluding_values: list[str] | None - список значений, которые нужно исключить из результата Returns: str - значение статуса, владельца или названия документа """ result = [] for key in keys: value = self._extract_info_value(key) if excluding_values and (value in excluding_values): continue if value: result.append(value) return '/'.join(reversed(result))