From 119f77b1e5033910ea629f8441f79cf96c80ebb4 Mon Sep 17 00:00:00 2001 From: Dennis Krivochenko Date: Sat, 18 Jul 2026 23:29:24 +0700 Subject: [PATCH] feat: add document optimization script for batch processing --- search_bot/optimize_documents.py | 298 +++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 search_bot/optimize_documents.py diff --git a/search_bot/optimize_documents.py b/search_bot/optimize_documents.py new file mode 100644 index 0000000..9ec28eb --- /dev/null +++ b/search_bot/optimize_documents.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +""" +Универсальный скрипт для улучшения MD-файлов документов АО «ХК «Сибцем». + +Выполняется на сервере 192.168.1.106. +Применяет паттерны: +1. Удаляет футеры страниц +2. Форматирует блок "УТВЕРЖДАЮ" +3. Выравнивает иерархию заголовков +4. Объединяет таблицы терминов +5. Добавляет разделители между разделами +6. Приводит таблицы к единому виду + +Использование: + python3 /opt/trueconf_bot/search_bot/optimize_documents.py <путь_к_каталогу> + +Пример: + python3 /opt/trueconf_bot/search_bot/optimize_documents.py "/opt/documents_xk/ready_md/АОХКСибцем/07 Служба Вице-президента по экономике и финансам/ДИТ/02 Регламенты деятельности/" +""" + +import os +import re +import sys +import time +import requests +import base64 + +# Конфигурация +FILEBROWSER_URL = "https://smb.dddennnisss.ru" +FILEBROWSER_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJGaWxlQnJvd3NlciBRdWFudHVtIiwiZXhwIjoyMzAyNzM1NzA3LCJpYXQiOjE3ODQzMzU3MDcsImJlbG9uZ3NUbyI6MSwiUGVybWlzc2lvbnMiOnsiYXBpIjp0cnVlLCJhZG1pbiI6dHJ1ZSwibW9kaWZ5Ijp0cnVlLCJzaGFyZSI6dHJ1ZSwicmVhbHRpbWUiOmZhbHNlLCJkZWxldGUiOnRydWUsImNyZWF0ZSI6dHJ1ZSwiZG93bmxvYWQiOnRydWV9fQ.QBKlh3NQJeh4aeMiKLkhg0tF8hxk-Oh2KLsXLSbuvm8" +SOURCE_NAME = "SSD-Storage" + + +def remove_footers(content): + """Удалить футеры страниц.""" + # Удалить "АО «ХК «Сибцем»\nТип документа: ...\nНаименование документа: ..." + content = re.sub( + r'АО «ХК «Сибцем»\nТип документа:.*?\nНаименование документа:.*?\n\n', + '', + content, + flags=re.DOTALL + ) + # Удалить "Стр. X из Y" + content = re.sub(r'Стр\. \d+ из \d+\n\n', '', content) + # Удалить "Дата утверждения: DD.MM.YYYY" + content = re.sub(r'Дата утверждения: \d{2}\.\d{2}\.\d{4}\n\n', '', content) + # Удалить "Ведущее подразделение: ..." + content = re.sub(r'Ведущее подразделение:.*?\n\n', '', content) + return content + + +def fix_headings(content): + """Выровнять иерархию заголовков: все на уровень ##.""" + content = re.sub(r'^(#+)\s+(.+)$', r'## \2', content, flags=re.MULTILINE) + return content + + +def add_dividers(content): + """Добавить разделители между основными разделами.""" + # Разделитель перед Оглавлением + content = re.sub( + r'(\n)(## )(Оглавление)', + r'\1\n---\n\1\2\3', + content + ) + # Разделители перед разделами 1-9 + content = re.sub( + r'(\n)(## )(\s*(?:1\.|2\.|3\.|4\.|5\.|6\.|7\.|8\.|9\.))', + r'\1\n---\n\1\2\3', + content + ) + # Разделители перед Приложениями + content = re.sub( + r'(\n)(## )(Приложение)', + r'\1\n---\n\1\2\3', + content + ) + # Разделитель перед Листом согласований + content = re.sub( + r'(\n)(## )(Лист согласований)', + r'\1\n---\n\1\2\3', + content + ) + return content + + +def fix_tables(content): + """Привести все таблицы к единому виду с thead/tbody.""" + def fix_table(table): + if '' in table and '' in table: + return table + rows = re.findall(r'(.*?)', table, re.DOTALL) + if not rows: + return table + + header = '' + rows[0] + '' + body = '' + for row in rows[1:]: + body += '' + row + '' + body += '' + + return '' + header + body + '
' + + return re.sub(r'.*?
', lambda m: fix_table(m.group(0)), content, flags=re.DOTALL) + + +def merge_terms_tables(content): + """Объединить разбитые таблицы терминов.""" + # Поиск таблиц терминов + terms_pattern = r'(.*?Термины.*?
)' + matches = re.findall(terms_pattern, content, re.DOTALL) + + if len(matches) > 1: + # Объединяем все таблицы терминов в одну + merged = '' + for table in matches: + rows = re.findall(r'(.*?)', table, re.DOTALL) + for row in rows[1:]: # Пропускаем заголовок + merged += row + merged += '
ТерминОпределение
' + content = content.replace(matches[0], merged) + + return content + + +def fix_approval_block(content): + """Форматировать блок УТВЕРЖДАЮ.""" + # Паттерн 1: "УТВЕРЖДАЮ\nПрезидент\n\nО.В. Шарыкин\n\n«XX» месяц год г." + approval_patterns = [ + r'(УТВЕРЖДАУ\nПрезидент\n\nО\.В\. Шарыкин\n\n«\d+» \w+ \d{4} г\.)', + r'(УТВЕРЖДАУ\nПрезидент\n\nО\.В\. Шарыкин\n\n«\d+» \w+ \d{4} г\.)', + ] + + for pattern in approval_patterns: + match = re.search(pattern, content) + if match: + old_text = match.group(0) + new_text = f'''
+УТВЕРЖДАУ
+Президент
+О.В. Шарыкин
+{old_text.split("«")[1].split("г.")[0]} г. +
''' + content = content.replace(old_text, new_text) + break + + return content + + +def upload_to_filebrowser(content, filename): + """Загрузить файл в filebrowser.""" + # Добавить "_improved" перед расширением + if filename.endswith('.md'): + improved_filename = filename[:-3] + '_improved.md' + else: + improved_filename = filename + '_improved' + + session = requests.Session() + session.cookies.set("filebrowser_quantum_jwt", FILEBROWSER_TOKEN, domain="smb.dddennnisss.ru") + + url = f"{FILEBROWSER_URL}/api/resources" + params = {"path": improved_filename, "source": SOURCE_NAME} + + r = session.put(url, params=params, data=content) + + if r.status_code == 200: + print(f"✓ Файл загружен: {improved_filename}") + + # Проверить + r = session.get(f"{FILEBROWSER_URL}/api/resources", params={"path": "/", "source": SOURCE_NAME}) + if r.status_code == 200: + data = r.json() + items = data.get("files", []) + data.get("folders", []) + for item in items: + if improved_filename in item["name"]: + print(f"✓ Размер: {item['size']} байт") + break + else: + print(f"✗ Ошибка загрузки: {r.status_code} - {r.text[:200]}") + + return improved_filename + + +def optimize_document(md_path): + """Основная функция оптимизации документа.""" + start_time = time.time() + + print(f"\n📄 Обработка документа: {md_path.split('/')[-1]}") + print("=" * 60) + + # Шаг 1: Читать MD файл + print("\n📥 Шаг 1: Чтение MD...") + try: + with open(md_path, 'r', encoding='utf-8') as f: + md_content = f.read() + except Exception as e: + print(f"✗ Ошибка при чтении: {e}") + return + + print(f"✓ MD прочитан: {len(md_content)} символов") + + # Шаг 2: Применить паттерны + print("\n🔧 Шаг 2: Применение паттернов...") + + # 2.1 Удалить футеры + content = remove_footers(md_content) + print(" ✓ Футеры удалены") + + # 2.2 Выровнять иерархию заголовков + content = fix_headings(content) + print(" ✓ Иерархия заголовков выровнена") + + # 2.3 Добавить разделители + content = add_dividers(content) + print(" ✓ Разделители добавлены") + + # 2.4 Привести таблицы к единому виду + content = fix_tables(content) + print(" ✓ Таблицы приведены к единому виду") + + # 2.5 Объединить таблицы терминов + content = merge_terms_tables(content) + print(" ✓ Таблицы терминов объединены") + + # 2.6 Форматировать блок УТВЕРЖДАЮ + content = fix_approval_block(content) + print(" ✓ Блок УТВЕРЖДАУ отформатирован") + + # Сохранить улучшенную версию + improved_path = md_path.replace('.md', '_improved.md') + with open(improved_path, "w", encoding="utf-8") as f: + f.write(content) + + print(f"\n📊 Результаты:") + print(f" Исходный размер: {len(md_content)} символов") + print(f" Улучшенный размер: {len(content)} символов") + + # Показать структуру + sections = re.findall(r'^(##)\s+(.+)$', content, re.MULTILINE) + print(f" Разделов: {len(sections)}") + + tables = re.findall(r'', content) + print(f" Таблиц: {len(tables)}") + + # Показать первые 300 символов + print(f"\n📝 Начало файла:") + print(content[:300].replace('\n', '\\n')) + + # Загрузить в filebrowser + print("\n📤 Шаг 3: Загрузка в filebrowser...") + filename = md_path.split('/')[-1] + upload_to_filebrowser(content, filename) + + elapsed = time.time() - start_time + print(f"\n{'=' * 60}") + print(f"✅ Готово! Время выполнения: {elapsed:.1f} сек") + print(f"{'=' * 60}") + + +def process_directory(directory): + """Обработать все MD файлы в каталоге.""" + start_time = time.time() + + print(f"📂 Обработка каталога: {directory}") + print("=" * 60) + + # Найти все MD файлы + md_files = [] + for root, dirs, files in os.walk(directory): + for file in files: + if file.endswith('.md'): + md_files.append(os.path.join(root, file)) + + print(f"Найдено {len(md_files)} MD файлов") + + # Обработать каждый файл + for md_path in md_files: + try: + optimize_document(md_path) + except Exception as e: + print(f"✗ Ошибка при обработке {md_path}: {e}") + + elapsed = time.time() - start_time + print(f"\n{'=' * 60}") + print(f"✅ Обработка каталога завершена! Общее время: {elapsed:.1f} сек") + print(f"{'=' * 60}") + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Использование:") + print(" python3 optimize_documents.py <путь_к_каталогу>") + print("\nПример:") + print(' python3 optimize_documents.py "/opt/documents_xk/ready_md/АОХКСибцем/07 Служба Вице-президента по экономике и финансам/ДИТ/02 Регламенты деятельности/"') + sys.exit(1) + + directory = sys.argv[1] + process_directory(directory)