184 lines
8.7 KiB
Python
184 lines
8.7 KiB
Python
"""
|
||
Сервис для работы с 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)
|
||
}
|