Files
kadastr14/main.py
2025-08-12 23:53:44 +05:00

401 lines
21 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
import threading
from pathlib import Path
# Импорт наших сервисов
from services.file_seeker import FileSeeker
from services.excel_processor import ExcelProcessor
def copy_matching_files(matching_numbers, cadastral_to_file_map, target_directory):
"""Копирует файлы с совпадающими кадастровыми номерами в целевую директорию"""
import shutil
moved_count = 0
for number in matching_numbers:
if number in cadastral_to_file_map:
source_path = Path(cadastral_to_file_map[number])
if source_path.exists():
# Создаем уникальное имя файла
dest_name = f"{number.replace(':', '_')}_{source_path.name}"
dest_path = target_directory / dest_name
try:
shutil.copy2(source_path, dest_path)
moved_count += 1
print(f"Файл скопирован в matching: {source_path.name} -> {dest_path}")
except Exception as e:
print(f"Ошибка при копировании файла {source_path}: {e}")
return moved_count
def Pars():
"""Основная функция обработки"""
xml_directory = xml_entry.get().strip()
excel_file_path = excel_entry.get().strip()
# Создаем директории вывода рядом с программой
# Для PyInstaller нужно использовать sys.executable вместо __file__
import sys
if getattr(sys, 'frozen', False):
# Если программа скомпилирована PyInstaller
script_dir = Path(sys.executable).parent
else:
# Если запускается как Python скрипт
script_dir = Path(__file__).parent
output_directory = script_dir / "results"
print(f"Создаем директорию результатов: {output_directory}") # Отладочный вывод
output_directory.mkdir(exist_ok=True)
# Создаем две отдельные папки для организации файлов
all_files_dir = output_directory / "all_xml_files" # Все найденные XML файлы
matching_files_dir = output_directory / "matching_cadastral_files" # Файлы с номерами из Excel
all_files_dir.mkdir(exist_ok=True)
matching_files_dir.mkdir(exist_ok=True)
# Проверяем входные данные
if not xml_directory or not os.path.exists(xml_directory):
messagebox.showerror("Ошибка", "Выберите существующую директорию с XML файлами")
return
# Запускаем обработку в отдельном потоке
def process_files():
try:
# Показываем прогресс
progress_window = show_progress_window()
# 1. Инициализируем FileSeeker с первой папкой для всех файлов
update_progress(progress_window, "Поиск XML файлов...", 10)
seeker = FileSeeker(xml_directory, str(all_files_dir))
# 2. Ищем XML файлы
update_progress(progress_window, "Анализ найденных файлов...", 30)
xml_files = seeker.find_xml_files()
if not xml_files:
progress_window.destroy()
messagebox.showwarning("Предупреждение", "XML файлы не найдены в указанной директории")
return
# 3. Обрабатываем файлы
update_progress(progress_window, "Извлечение кадастровых номеров...", 50)
records = seeker.process_files()
if not records:
progress_window.destroy()
messagebox.showwarning("Предупреждение", "Кадастровые номера не найдены в XML файлах")
return
# 4. Создаем карту номер -> файл для быстрого поиска (для обратной совместимости)
cadastral_to_file_map = {}
for record in records:
# Добавляем все номера из объекта в карту
for number in record.all_numbers:
if number not in cadastral_to_file_map: # Берем первый найденный файл
cadastral_to_file_map[number] = record.file_path
# 5. Обработка Excel файла и организация файлов
excel_updated = False
matching_files_moved = 0
if excel_file_path and os.path.exists(excel_file_path):
update_progress(progress_window, "Обработка Excel файла...", 60)
try:
excel_processor = ExcelProcessor(excel_file_path)
if excel_processor.load_excel_file():
excel_numbers = excel_processor.get_cadastral_numbers()
# Находим пересечения между Excel и найденными номерами
# Теперь ищем по всем номерам в объектах, не только по основным
all_found_numbers = seeker.get_all_cadastral_numbers()
matching_numbers = excel_numbers.intersection(all_found_numbers)
if matching_numbers:
# Обновляем Excel файл информацией о найденных объектах
output_excel_path = str(output_directory / "updated_cadastral_list.xlsx")
excel_processor.update_excel_with_cadastral_records(records, output_excel_path)
excel_updated = True
# Копируем файлы с номерами из Excel в отдельную папку
update_progress(progress_window, "Организация файлов из Excel списка...", 70)
matching_files_moved = copy_matching_files(matching_numbers, cadastral_to_file_map, matching_files_dir)
# Копируем ВСЕ найденные файлы в общую папку
update_progress(progress_window, "Копирование всех найденных файлов...", 80)
all_files_moved = seeker.move_files_to_temp(set(cadastral_to_file_map.keys()))
else:
# Если совпадений нет, копируем все найденные файлы только в общую папку
update_progress(progress_window, "Организация всех найденных файлов...", 75)
all_files_moved = seeker.move_files_to_temp(set(cadastral_to_file_map.keys()))
else:
# Если Excel не загрузился, обрабатываем все файлы
update_progress(progress_window, "Организация всех найденных файлов...", 75)
all_files_moved = seeker.move_files_to_temp(set(cadastral_to_file_map.keys()))
except Exception as excel_error:
print(f"Ошибка при обработке Excel: {excel_error}")
update_progress(progress_window, "Организация всех найденных файлов...", 75)
all_files_moved = seeker.move_files_to_temp(set(cadastral_to_file_map.keys()))
else:
# Если Excel файл не указан, копируем все найденные файлы только в общую папку
update_progress(progress_window, "Организация всех найденных файлов...", 75)
all_files_moved = seeker.move_files_to_temp(set(cadastral_to_file_map.keys()))
# 6. Получаем статистику
update_progress(progress_window, "Подготовка результатов...", 85)
stats = seeker.get_statistics()
unique_numbers = seeker.get_unique_cadastral_numbers()
# Подготавливаем информацию о перемещенных файлах для отчета
if 'all_files_moved' in locals():
moved_files_info = all_files_moved
total_moved = len(all_files_moved)
else:
moved_files_info = {}
total_moved = 0
# 7. Создаем отчет
update_progress(progress_window, "Создание отчета...", 95)
create_report(str(output_directory), stats, unique_numbers, moved_files_info, excel_updated, excel_file_path, matching_files_moved if 'matching_files_moved' in locals() else 0)
# Закрываем окно прогресса
progress_window.destroy()
# Показываем результаты
show_results(stats, len(unique_numbers), total_moved, excel_updated, matching_files_moved if 'matching_files_moved' in locals() else 0)
except Exception as e:
if 'progress_window' in locals():
progress_window.destroy()
messagebox.showerror("Ошибка", f"Произошла ошибка при обработке: {str(e)}")
# Запускаем в отдельном потоке
thread = threading.Thread(target=process_files)
thread.daemon = True
thread.start()
def show_progress_window():
"""Создает окно с прогресс-баром"""
progress_window = tk.Toplevel(root)
progress_window.title("Обработка файлов")
progress_window.geometry("400x150")
progress_window.transient(root)
progress_window.grab_set()
# Центрируем окно
progress_window.geometry("+%d+%d" % (root.winfo_rootx() + 50, root.winfo_rooty() + 50))
# Метка с текстом
progress_label = tk.Label(progress_window, text="Инициализация...", font=font)
progress_label.pack(pady=20)
# Прогресс-бар
progress_bar = ttk.Progressbar(progress_window, length=300, mode='determinate')
progress_bar.pack(pady=10)
progress_window.label = progress_label
progress_window.bar = progress_bar
return progress_window
def update_progress(progress_window, text, value):
"""Обновляет прогресс-бар"""
if progress_window and progress_window.winfo_exists():
progress_window.label.config(text=text)
progress_window.bar['value'] = value
progress_window.update()
def create_report(output_directory, stats, unique_numbers, moved_files, excel_updated=False, excel_file_path=None, matching_files_count=0):
"""Создает текстовый отчет о результатах обработки"""
report_path = os.path.join(output_directory, "processing_report.txt")
with open(report_path, 'w', encoding='utf-8') as f:
f.write("=== ОТЧЕТ О ОБРАБОТКЕ XML ФАЙЛОВ ===\n\n")
f.write(f"Общая статистика:\n")
f.write(f"- Всего XML файлов найдено: {stats['total_xml_files']}\n")
f.write(f"- Файлов с кадастровыми номерами: {stats['files_with_numbers']}\n")
f.write(f"- Всего кадастровых объектов: {stats['total_cadastral_objects']}\n")
f.write(f"- Уникальных основных номеров: {stats['unique_main_numbers']}\n")
f.write(f"- Всего номеров во всех объектах: {stats['total_all_numbers']}\n")
f.write(f"- Всего уникальных номеров: {stats['unique_all_numbers']}\n")
f.write(f"- Файлов скопировано в 'all_xml_files': {len(moved_files)}\n")
if matching_files_count > 0:
f.write(f"- Файлов из Excel списка скопировано в 'matching_cadastral_files': {matching_files_count}\n")
f.write("\n📁 Структура результатов:\n")
f.write("- all_xml_files/ - все найденные XML файлы с кадастровыми номерами\n")
f.write("- matching_cadastral_files/ - файлы с номерами из Excel списка\n")
f.write("- processing_report.txt - данный отчет\n")
if excel_updated and excel_file_path:
f.write(f"- updated_cadastral_list.xlsx - обновленный Excel файл\n\n")
f.write(f"Excel обработка:\n")
f.write(f"- Исходный Excel файл: {Path(excel_file_path).name}\n")
f.write(f"- Обновленный файл создан: updated_cadastral_list.xlsx\n")
f.write(f"- Добавлены колонки:\n")
f.write(f"'Найденные файлы' - файлы с кадастровыми номерами\n")
f.write(f"'Смежные кадастровые номера' - другие номера из тех же файлов\n")
f.write(f"'Кадастровый блок' - первые 3 части номера\n\n")
else:
f.write("\n")
f.write("Найденные кадастровые номера:\n")
for number in sorted(unique_numbers):
f.write(f"- {number}\n")
if moved_files:
f.write(f"\nСкопированные файлы:\n")
for number, file_path in moved_files.items():
f.write(f"- {number}: {Path(file_path).name}\n")
def show_results(stats, unique_count, total_moved, excel_updated=False, matching_files_count=0):
"""Показывает окно с результатами обработки"""
excel_info = ""
if excel_updated:
excel_info = ("\n📊 Excel файл обновлен с дополнительной информацией:\n"
"• Файлы с найденными номерами\n"
"• Смежные кадастровые номера из тех же файлов\n"
"• Кадастровые блоки")
matching_info = ""
if matching_files_count > 0:
matching_info = f"\n📋 Файлов из Excel списка: {matching_files_count}"
result_text = (
f"✅ Обработка завершена успешно!\n\n"
f"📊 Результаты:\n"
f"• XML файлов обработано: {stats['total_xml_files']}\n"
f"• Файлов с номерами: {stats['files_with_numbers']}\n"
f"• Уникальных кадастровых номеров: {unique_count}\n"
f"Всего файлов скопировано: {total_moved}"
f"{matching_info}\n\n"
f"📁 Результаты сохранены в папке 'results':\n"
f"• all_xml_files/ - все найденные файлы\n"
f"• matching_cadastral_files/ - файлы из Excel\n"
f"📋 Создан подробный отчет: processing_report.txt"
f"{excel_info}"
)
messagebox.showinfo("Результаты обработки", result_text)
root = tk.Tk()
root.title("Parser14")
font = ("Arial", 12)
root.configure(bg="#B85C38")
root.geometry("600x500")
# Устанавливаем минимальный размер окна
root.minsize(500, 250)
#путь для сохранения файлов
def open_directory(event, entry):
directory_path = filedialog.askdirectory()
if directory_path: # Проверяем что путь выбран
entry.delete(0, tk.END)
entry.insert(0, directory_path)
# Функция для выбора Excel файла
def open_excel_file(event, entry):
file_path = filedialog.askopenfilename(
title="Выберите Excel файл",
filetypes=[("Excel files", "*.xlsx *.xls"), ("All files", "*.*")]
)
if file_path:
entry.delete(0, tk.END)
entry.insert(0, file_path)
# Создаем главный фрейм
main_frame = tk.Frame(root, bg="#B85C38")
main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# Заголовок
title_label = tk.Label(main_frame,
text="🏗️ Парсер кадастровых номеров из XML файлов",
font=("Arial", 14, "bold"),
bg="#B85C38",
fg="white")
title_label.pack(pady=(0, 20))
# первое поле с списком
xml_label = tk.Label(main_frame, text="📁 Директория с XML файлами:", font=font, bg="#B85C38", fg="white")
xml_label.pack(anchor="w", pady=(0, 5))
xml_entry = tk.Entry(main_frame, font=font, width=70)
xml_entry.pack(anchor="w", fill=tk.X, pady=(0, 15))
xml_entry.bind("<Button-1>", lambda event: open_directory(event, xml_entry))
# Поле для Excel файла (опционально)
excel_label = tk.Label(main_frame, text="📊 Excel файл со списком номеров (опционально):", font=font, bg="#B85C38", fg="white")
excel_label.pack(anchor="w", pady=(0, 5))
excel_entry = tk.Entry(main_frame, font=font, width=70)
excel_entry.pack(anchor="w", fill=tk.X, pady=(0, 15))
excel_entry.bind("<Button-1>", lambda event: open_excel_file(event, excel_entry))
# Информация о месте сохранения
info_label = tk.Label(main_frame,
text="💾 Результаты будут сохранены в папке 'results' рядом с программой",
font=("Arial", 10),
bg="#B85C38",
fg="#FFE4B5")
info_label.pack(anchor="w", pady=(0, 20))
# Фрейм для кнопок
button_frame = tk.Frame(main_frame, bg="#B85C38")
button_frame.pack(fill=tk.X)
# кнопка запуска
start_button = tk.Button(button_frame,
text="🚀 Начать обработку",
command=Pars,
font=("Arial", 12, "bold"),
bg="#2E8B57",
fg="white",
padx=20,
pady=10)
start_button.pack(side="right", padx=(10, 0))
# Кнопка помощи
help_button = tk.Button(button_frame,
text=" Справка",
command=lambda: show_help(),
font=font,
bg="#4682B4",
fg="white",
padx=15,
pady=10)
help_button.pack(side="right")
def show_help():
"""Показывает справочную информацию"""
help_text = """
🔍 Инструкция по использованию:
1. Выберите директорию с XML файлами
2. (Опционально) Выберите Excel файл со списком кадастровых номеров
3. Нажмите "Начать обработку"
📋 Программа выполнит:
• Поиск всех XML файлов в указанной директории
• Извлечение кадастровых номеров из файлов
• Организацию файлов по номерам
• Обновление Excel файла (если указан) с дополнительными колонками:
- Найденные файлы (где обнаружен номер)
- Смежные кадастровые номера (из тех же файлов)
- Кадастровый блок (первые 3 части номера)
• Создание подробного отчета
📁 Результаты сохраняются в папке 'results' рядом с программой:
• all_xml_files/ - ВСЕ найденные XML файлы с кадастровыми номерами
• matching_cadastral_files/ - файлы с номерами из Excel списка
• updated_cadastral_list.xlsx - обновленный Excel (если указан)
• processing_report.txt - подробный отчет
⚡ Поддерживаемые форматы номеров: XX:XX:XXXXXXX:XXX
"""
messagebox.showinfo("Справка", help_text)
root.mainloop()