muryshev's picture
init
57cf043
raw
history blame
21.8 kB
from typing import List, TypeAlias
import re
import shutil
import os
Hierarchy: TypeAlias = dict[str, str]
class HierarchyParser:
def __init__(self):
self.appendix = False
self.content_flag = False
self.preface = False
self.exclude_pattern = r'^Приложение [А-Я]'
self.patterns = [
r'^\d+\.?\s', # Соответствует "1.", "2.", и т.д.
r'^\d+\.\d+\.?\s', # Соответствует "1.1.", "2.1.", и т.д.
r'^\d+\.\d+\.\d+\.?\s', # Соответствует "1.1.1", "2.1.1", и т.д.
r'^\d+\.\d+\.\d+\.\d+\.?\s', # Соответствует "1.1.1.1", "2.1.1.1", и т.д.
r'^\d+\.\d+\.\d+\.\d+\.\d+\.?\s', # Соответствует "1.1.1.1.1", "2.1.1.1.1", и т.д.
r'^\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.?\s', # Соответствует "1.1.1.1.1.1", "2.1.1.1.1.1", и т.д.
r'^\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.?\s', # Соответствует "1.1.1.1.1.1.1", и т.д.
]
self.russian_alphabet = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя"
self.english_alphabet = "ABCDEFGHIJKabcdefghijk"
self.table_name = ''
def __init_parameters(self):
"""Сбросить найденные параграфы"""
self._hierarchy = {}
self.duplicate_marker_list = []
self.duplicate_marker_count, self.count, self.count_appendix = 0, 1, 1
self.appendix = False
self.letter_ = None
self.content_flag = False
self.preface = False
self.table_name = ''
def _processing_without_paragraph(self, text: str, unique_doc_number):
"""
Обрабатывает ':' если в начале нет нумерации параграфа.
Args:
text: Текст
"""
last_key = list(self._hierarchy.keys())[-1]
split_key = last_key.split('_')
if len(split_key) != 2:
last_lvl = split_key[1]
pattern_text_last = split_key[2].replace(' ', '')
key = f"{unique_doc_number}_{last_lvl}_{pattern_text_last}"
if len(split_key) == 3:
self._hierarchy[f'{key}_PartLevel{self.count}'] = f"{self._hierarchy[key]} {text}"
else:
ch = split_key[3]
self._hierarchy[f'{key}_{ch}_PartLeveL{self.count}'] = f"{self._hierarchy[key]} {text}"
else:
self._hierarchy[f'{unique_doc_number}_{text.replace("_", " ")}--DoublePoint0'] = text
def _processing_special_markers(self, text, pattern_text, unique_doc_number):
"""
Обрабатывает маркеры 'а)', 'б)' или '-' в строке.
Args:
text: Текст
pattern_text: Паттерн для парсинга и кодирования
"""
last_key = list(self._hierarchy.keys())[-1]
split_key = last_key.split('_')
if len(split_key) != 2:
last_lvl = split_key[1]
pattern_text_last = split_key[2]
key = f'{unique_doc_number}_{last_lvl}_{pattern_text_last}'
if pattern_text is not None:
pattern_text = pattern_text.replace(')', '')
self._hierarchy[key + f'_PartLevel{pattern_text[0]}'] = f'{self._hierarchy[key]} {text}'
else:
if len(split_key) == 3:
self._hierarchy[key + f'_PartLevel{self.count}'] = f'{self._hierarchy[key]} {text}'
else:
ch = split_key[3]
if ch.replace('PartLevel', '')[-1] in self.russian_alphabet or ch.replace('PartLevel', '')[-1] in self.english_alphabet:
if len(split_key) == 4:
self.count = 1
self._hierarchy[key + f'_{ch}_PartLeveL{self.count}'] = f'{self._hierarchy[key]} {text}'
elif re.search(r"<\d>", last_key):
self._hierarchy[key + f'_{ch}_PartLeveL{self.count}'] = f'{self._hierarchy[f"{key}_{ch}"]} {text}'
elif 'Table' in ch:
self._hierarchy[f'{key}_PartLevel{self.count}'] = f'{self._hierarchy[key]} {text}'
else:
try:
if int(ch.replace('PartLevel', '')) + 1 != self.count:
self._hierarchy[f'{key}_PartLevel{int(ch[-1]) + 1}'] = f'{self._hierarchy[key]} {text}'
else:
self._hierarchy[f'{key}_PartLevel{self.count}'] = f'{self._hierarchy[key]} {text}'
except ValueError:
self._hierarchy[f'{key}_PartLevel{self.count}'] = f'{self._hierarchy[key]} {text}'
else:
split_key = last_key.split('^')
self._hierarchy[f'{split_key[0]}^PartLevel{self.count}^UniqueNumber{self.unique_count}'] = f"{self._hierarchy[f'{split_key[0]}']} {text}"
def _processing_appendix(self, text, level, pattern_text, unique_doc_number):
"""
Обрабатывает маркеры 'Приложение' и все, что находится внутри приложения
Args:
text: Текст
level: Уровень параграфа
pattern_text: Паттерн для парсинга и кодирования
"""
if pattern_text is not None:
pattern_text = pattern_text.replace(')', '')
if level == 13:
self.letter_ = pattern_text[-1]
self.count = 1
self.count_appendix = 1
if level == 13 or level is None and pattern_text is None:
if level is None:
last_key = list(self._hierarchy.keys())[-1]
split_key = last_key.split('_')
if len(split_key) == 4:
self._hierarchy[f'{last_key}_PartLevel{self.count_appendix}'] = text
self.count_appendix += 1
elif len(split_key) == 5:
self._hierarchy[f'{"_".join(split_key[:-1])}_PartLevel{self.count_appendix}'] = text
self.count_appendix += 1
else:
self._hierarchy[f'{unique_doc_number}_Приложение{self.letter_}{self.count}'] = text
self.count_appendix = 1
else:
self._hierarchy[f'{unique_doc_number}_Приложение{self.letter_}{self.count}'] = text
self.count_appendix = 1
self.count += 1
elif level is not None and pattern_text is not None and level != 11:
key = f'{unique_doc_number}_Приложение{self.letter_}{self.count}_Level{level}_PatternText{pattern_text}'
self._hierarchy[key] = text
self.count += 1
self.count_appendix = 1
elif level == 10:
last_key = list(self._hierarchy.keys())[-1]
split_key = last_key.split('_')
key = f'{last_key}_PartLevel{self.count_appendix}'
if len(split_key) != 2:
self._hierarchy[key] = f"{self._hierarchy[last_key]} {text}"
else:
self._hierarchy[key] = text
self.count_appendix += 1
elif level == 11:
last_key = list(self._hierarchy.keys())[-1]
split_key = last_key.split('_')
appendix = split_key[1]
if len(split_key) == 2:
key = f'{last_key}_PartLevel{self.count_appendix}'
self._hierarchy[key] = f"{self._hierarchy[last_key]} {text}"
elif len(split_key) != 3:
last_lvl = split_key[2]
pattern_text_last = split_key[3]
key = f'{unique_doc_number}_{appendix}_{last_lvl}_{pattern_text_last}'
self._hierarchy[f'{key}_PartLevel{self.count_appendix}'] = f"{self._hierarchy[key]} {text}"
else:
key = f'{unique_doc_number}_{appendix}'
try:
self._hierarchy[f'{key}_PartLevel{self.count_appendix}'] = f"{self._hierarchy[f'{key}_PartLevel1']} {text}"
except KeyError:
print('asdfasdf')
self._hierarchy[f'{key}_PartLevel{self.count_appendix}'] = text
self.count_appendix += 1
def _get_pattern(self, text):
"""
Метод находит паттерны в документе для дальнейшей обработки в соответствии с паттерном
Args:
text: Текст.
Returns:
Код паттерна или None, Паттерн или None
Notes:
0-7 это разделы 1, 1.1., 1.1.1. и т.д
10 это паттерн для поиска ':' в конце предложения
11 это паттерн для поиска а), б) или строк начинающихся с '-', ''
12 это паттерн для поиска наименования таблиц 'Таблица 1', 'Таблица 2' в начале строки
13 это паттерн для поиска 'Приложение А', 'Приложение Б' в начале строки
14 это паттерн для поиска <1>, <2> в начале строки
"""
for i, pattern in enumerate(self.patterns):
pattern = re.match(pattern, text)
if pattern:
self.preface = False
return i, pattern.group(0).replace(' ', '').replace('\xa0', '')
if re.match(r'^- ', text) or re.match(r'^– ', text):
return 11, None
if re.match(r'^ ', text):
return 11, None
if re.match(r'\d\)', text):
return 11, None
if re.match(r'\w\)', text):
pattern = re.match(r'\w\)', text)
return 11, pattern.group(0).replace('\xa0', '')
if re.match(r"<\d>", text):
pattern = re.match(r"<\d>", text)
return 14, pattern.group(0).replace('\xa0', '')
if re.search(r':$', text) or re.search(r':$', text):
return 10, None
if re.findall(r'^Таблица \d+\.?', text):
pattern = re.match(r'^Таблица \d+\.?', text)
return 12, pattern.group(0).replace('\xa0', '')
if re.match(self.exclude_pattern, text):
pattern = re.match(self.exclude_pattern, text)
# if pattern.regs[0][1] + 2 < len(text):
# return None, None
self.appendix = True
return 13, pattern.group(0).replace('\xa0', '')
if re.match(r'^Содержание', text):
self.content_flag = True
return 15, None
if re.match(r'^Предисловие', text):
self.preface = True
return 16, None
return None, None
def _find_duplicate_marker(self, level, pattern_text):
"""
Метод находит одинаковые параграфы в документе. И присваивает дубликату порядковый номер
Args:
level: Уровень параграфа
pattern_text: Паттерн для парсинга и кодирования
Returns:
Название нового параграфа если дубликат был или название старого если не было дубликата
"""
if pattern_text is not None:
pattern_text = pattern_text.replace(' ', '')
if pattern_text in self.duplicate_marker_list and pattern_text[0] not in self.russian_alphabet:
if level == 0:
self.duplicate_marker_count += 1
pattern_text = f'{pattern_text}:Duplicate{self.duplicate_marker_count}'
self.duplicate_marker_list.append(pattern_text)
return pattern_text
def __find_last_paragraph(self, section):
for paragraph_ind in range(section.Paragraphs.Count):
paragraph = section.Paragraphs[paragraph_ind]
if paragraph.Text == '':
continue
if paragraph.ListText != '':
text = f'{paragraph.ListText} {paragraph.Text}'
else:
text = paragraph.Text
level_paragraph, pattern_text = self._get_pattern(text)
if pattern_text and 350 < paragraph_ind < 490 and level_paragraph < 2:
pattern_text_zxc = paragraph.Text
try:
return pattern_text_zxc
except:
return None
def parse_table(self, doc: List, unique_doc_number):
self.__init_parameters()
flag = True
for text in doc:
text = text.strip() # удаляет пробелы в начале и конце текста параграфа.
text = text.replace('------', '').replace('--', '').replace('\u000b', ' ').replace('\t', ' ')
text = text.replace('_', ' ').replace('\u0007', ' ').replace(' ', ' ').replace('', '-')
text = text.replace(' ', ' ')
if not text:
continue
if re.match(r'^Т\d?\d$', text) or re.match(r'^T\d?\d$', text):
try:
last_key = list(self._hierarchy.keys())[-1]
last_text = self._hierarchy[last_key]
if re.search(r': \d?\d?: $', last_text) or re.search(r': \d?\d?:$', last_text) or last_text == '':
self._hierarchy.pop(last_key, None)
except IndexError:
pass
self.table_name = text
flag = True
elif flag:
self._hierarchy[f'{unique_doc_number}_Table{self.table_name}_String{text}'] = ''
flag = False
else:
last_key = list(self._hierarchy.keys())[-1]
if text in self._hierarchy[last_key]:
continue
self._hierarchy[last_key] = f'{self._hierarchy[last_key]} {text}'
try:
last_key = list(self._hierarchy.keys())[-1]
last_text = self._hierarchy[last_key]
if re.search(r': \d?\d?: $', last_text) or re.search(r': \d?\d?:$', last_text):
self._hierarchy.pop(last_key, None)
except IndexError:
pass
def parse(self, doc: List, unique_doc_number, stop_appendix_list):
self.__init_parameters()
name_paragraph = None
flag = True
flag_appendix_stop = False
self.unique_count = 0
for text in doc:
text = text.strip() # удаляет пробелы в начале и конце текста параграфа.
text = text.replace('------', '').replace('--', '').replace('\u000b', ' ').replace('\t', ' ').replace('\ufeff', '')
text = text.replace('_', ' ').replace('\u0007', ' ').replace(' ', ' ').replace('', '-').replace('', '')
if not text:
continue
if flag:
self._hierarchy[f'{unique_doc_number}_{text.replace("_", " ")}'] = f'{text}'
flag = False
continue
level_paragraph, pattern_text = self._get_pattern(text) # чтобы определить уровень текущего параграфа.
if self.preface and not self.content_flag:
if level_paragraph == 16:
self._hierarchy[f'{unique_doc_number}_Предисловие'] = f'{text}'
continue
else:
self._hierarchy[f'{unique_doc_number}_Предисловие:^PatternText{pattern_text}'] = f'{self._hierarchy[f"{unique_doc_number}_Предисловие"]} {text}'
continue
if self.content_flag:
self.preface = False
if level_paragraph == 15:
self._hierarchy[f'{unique_doc_number}_Содержание'] = f'{text}'
continue
elif level_paragraph is not None:
if level_paragraph <= 9 and '1' in pattern_text and not re.findall(r'\d+$', text):
self.content_flag = False
else:
self._hierarchy[
f'{unique_doc_number}_Содержание'] = f'{self._hierarchy[f"{unique_doc_number}_Содержание"]} {text}'
if level_paragraph == 13:
self.appendix = False
continue
elif text not in self._hierarchy[f'{unique_doc_number}_Содержание']:
self._hierarchy[f'{unique_doc_number}_Содержание'] = f'{self._hierarchy[f"{unique_doc_number}_Содержание"]} {text}'
continue
else:
self.content_flag = False
if self.appendix and not self.content_flag:
if level_paragraph == 13:
if pattern_text in stop_appendix_list or pattern_text[-1] in stop_appendix_list:
flag_appendix_stop = True
continue
else:
flag_appendix_stop = False
if not flag_appendix_stop:
self._processing_appendix(text, level_paragraph, pattern_text, unique_doc_number)
continue
# TODO Проверить работоспособность и перенести в случае чего обратно перед условием про appendix
pattern_text = self._find_duplicate_marker(level_paragraph, pattern_text)
if level_paragraph is not None:
if level_paragraph <= 9: # Пункты 1. 1.1. 2.1 и так далее до 1.1.1.1.1.1.1
name_paragraph = text
key = f'{unique_doc_number}_Level{level_paragraph}_PatternText{pattern_text}'
self._hierarchy[key] = text
self.count = 1
elif level_paragraph == 10:
self._processing_without_paragraph(text, unique_doc_number)
self.unique_count += 1
self.count = 1
elif level_paragraph == 11:
self._processing_special_markers(text, pattern_text, unique_doc_number)
self.count += 1
elif level_paragraph == 14:
if name_paragraph is not None:
level_, pattern_text_ = self._get_pattern(name_paragraph)
self._hierarchy[f'{unique_doc_number}_Level{level_}_PatternText{pattern_text_}_PartLevel{pattern_text}'] = f'{text}'
else:
new_text = text.replace(f'{pattern_text}', '')
self._hierarchy[f'{unique_doc_number}_{new_text}'] = f'{text}'
self.count = 1
elif level_paragraph == 12: # Обработка Таблиц
last_key = list(self._hierarchy.keys())[-self.count]
split_key = last_key.split('_')
last_lvl = split_key[1]
numer_table = pattern_text.replace('Таблица', '').replace('.', '')
if len(split_key) != 2:
pattern_text_last = last_key.split('_')[2]
key = f'{unique_doc_number}_{last_lvl}_{pattern_text_last}_Table{numer_table}'
else:
pattern_text_last = last_key.split('_')[1]
key = f'{unique_doc_number}_{pattern_text_last}_Table{numer_table}'
self._hierarchy[key] = text
self.count = 1
else:
last_key = list(self._hierarchy.keys())[-1]
if name_paragraph is not None:
split_key = last_key.split('_')
if 'Table' in split_key[-1]:
level_paragraph, pattern_text = self._get_pattern(name_paragraph)
self._hierarchy[f'{unique_doc_number}_Level{level_paragraph}_PatternText{pattern_text}_PartLevel{self.count}'] = f'{text}'
self.count += 1
else:
self._hierarchy[f'{last_key}'] = f'{self._hierarchy[f"{last_key}"]} {text}'
else:
self._hierarchy[f'{last_key}'] = f'{self._hierarchy[last_key]} {text}'
def hierarchy(self):
"""Вернуть иерархию документа"""
return self._hierarchy
@staticmethod
def clear_tmp(root_path):
# Проверяем, существует ли папка
if not os.path.isdir(root_path):
raise FileNotFoundError(f"Папка {root_path} не найдена.")
# Перебираем все файлы и папки внутри указанной директории
for item in os.listdir(root_path):
item_path = os.path.join(root_path, item)
# Удаляем папки и их содержимое рекурсивно
if os.path.isdir(item_path):
shutil.rmtree(item_path)
else:
# Удаляем файлы
os.remove(item_path)