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("", 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("", 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()