""" Сервис для работы с Excel файлами. """ import pandas as pd from pathlib import Path from typing import List, Set, Dict, Optional class ExcelProcessor: def __init__(self, excel_file_path: str): self.excel_file_path = Path(excel_file_path) self.cadastral_numbers: Set[str] = set() self.df: Optional[pd.DataFrame] = None self.cadastral_column: Optional[str] = None def load_excel_file(self) -> bool: """Загружает Excel файл и находит колонку с кадастровыми номерами""" try: if not self.excel_file_path.exists(): print(f"Excel файл {self.excel_file_path} не найден") return False # Пробуем разные форматы Excel файлов try: self.df = pd.read_excel(self.excel_file_path, engine='openpyxl') except: self.df = pd.read_excel(self.excel_file_path, engine='xlrd') # Ищем колонку с кадастровыми номерами self.cadastral_column = self._find_cadastral_column() if self.cadastral_column: # Извлекаем кадастровые номера self._extract_cadastral_numbers() print(f"Загружено {len(self.cadastral_numbers)} кадастровых номеров из Excel") return True else: print("Не найдена колонка с кадастровыми номерами в Excel файле") return False except Exception as e: print(f"Ошибка при загрузке Excel файла: {e}") return False def _find_cadastral_column(self) -> Optional[str]: """Находит колонку с кадастровыми номерами""" if self.df is None: return None # Ключевые слова для поиска колонки keywords = ['кадастр', 'номер', 'cadastral', 'number', 'кн', 'cad'] for column in self.df.columns: column_str = str(column).lower() if any(keyword in column_str for keyword in keywords): return column # Если не найдено по названию, ищем по содержимому for column in self.df.columns: # Проверяем первые несколько значений в колонке sample_values = self.df[column].dropna().head(5) cadastral_count = 0 for value in sample_values: if self._is_cadastral_number_format(str(value)): cadastral_count += 1 # Если больше половины значений похожи на кадастровые номера if cadastral_count >= len(sample_values) * 0.5: return column return None def _extract_cadastral_numbers(self): """Извлекает кадастровые номера из найденной колонки""" if self.df is None or self.cadastral_column is None: return cadastral_numbers = set() for value in self.df[self.cadastral_column].dropna(): value_str = str(value).strip() if self._is_cadastral_number_format(value_str): cadastral_numbers.add(value_str) self.cadastral_numbers = cadastral_numbers def _is_cadastral_number_format(self, text: str) -> bool: """Проверяет, соответствует ли текст формату кадастрового номера""" import re pattern = r'^\d{1,2}:\d{1,2}:\d{6,8}:\d{1,5}$' return bool(re.match(pattern, text)) def update_excel_with_cadastral_records(self, cadastral_records: List, output_file_path: str = None) -> bool: """Обновляет Excel файл информацией о найденных кадастровых объектах""" try: from models.cadastral_record import CadastralRecord if self.df is None or self.cadastral_column is None: print("Excel файл не загружен или не найдена колонка с номерами") return False # Создаем карты для быстрого поиска # номер -> запись объекта number_to_record = {} for record in cadastral_records: # Основной номер number_to_record[record.main_number] = record # Все остальные номера тоже ведут к тому же объекту for number in record.other_numbers: number_to_record[number] = record # Добавляем новые колонки self.df['Найден в файле'] = '' self.df['Основной номер объекта'] = '' self.df['Дополнительные номера'] = '' self.df['Кадастровый блок'] = '' # Заполняем информацию для каждой строки matched_count = 0 for index, row in self.df.iterrows(): cadastral_number = str(row[self.cadastral_column]).strip() if cadastral_number in number_to_record: record = number_to_record[cadastral_number] matched_count += 1 # 1. Имя файла file_name = Path(record.file_path).name self.df.at[index, 'Найден в файле'] = file_name # 2. Основной номер объекта self.df.at[index, 'Основной номер объекта'] = record.main_number # 3. Дополнительные номера в объекте (без основного) other_numbers_sorted = sorted(record.other_numbers) if other_numbers_sorted: self.df.at[index, 'Дополнительные номера'] = ', '.join(other_numbers_sorted) else: self.df.at[index, 'Дополнительные номера'] = '' # 4. Кадастровый блок (от основного номера) cadastral_block = self._extract_cadastral_block(record.main_number) if cadastral_block: self.df.at[index, 'Кадастровый блок'] = cadastral_block # Сохраняем обновленный файл output_path = output_file_path or str(self.excel_file_path.with_suffix('.updated.xlsx')) self.df.to_excel(output_path, index=False, engine='openpyxl') print(f"Обновленный Excel файл сохранен: {output_path}") print(f"Найдено совпадений: {matched_count} из {len(self.cadastral_numbers)}") return True except Exception as e: print(f"Ошибка при обновлении Excel файла: {e}") return False def _extract_cadastral_block(self, cadastral_number: str) -> str: """Извлекает кадастровый блок из номера (первые три части)""" try: parts = cadastral_number.split(':') if len(parts) >= 3: return ':'.join(parts[:3]) return cadastral_number except: return cadastral_number def get_cadastral_numbers(self) -> Set[str]: """Возвращает множество кадастровых номеров из Excel""" return self.cadastral_numbers.copy() def get_statistics(self) -> Dict[str, any]: """Возвращает статистику по Excel файлу""" if self.df is None: return {} return { 'total_rows': len(self.df), 'cadastral_column': self.cadastral_column, 'cadastral_numbers_count': len(self.cadastral_numbers), 'columns': list(self.df.columns) }