""" Сервисы (бизнес-логика) """ import tempfile import os from typing import Dict, Type from core.models import UploadRequest, UploadResult, DataRequest, DataResult from core.ports import ParserPort, StoragePort # Глобальный словарь парсеров PARSERS: Dict[str, Type[ParserPort]] = {} def get_parser(report_type: str) -> ParserPort: """Получение парсера по типу отчета""" if report_type not in PARSERS: available_parsers = list(PARSERS.keys()) raise ValueError(f"Неизвестный тип отчета '{report_type}'. Доступные типы: {available_parsers}") return PARSERS[report_type]() class ReportService: """Сервис для работы с отчетами (только S3)""" def __init__(self, storage: StoragePort): self.storage = storage def upload_report(self, request: UploadRequest) -> UploadResult: """Загрузка отчета""" try: # Получаем парсер для данного типа отчета parser = get_parser(request.report_type) # Сохраняем файл во временную директорию suff = "." + str(request.file_name.split('.')[-1]) with tempfile.NamedTemporaryFile(delete=False, suffix=suff) as temp_file: temp_file.write(request.file_content) temp_file_path = temp_file.name try: # Парсим файл parse_params = request.parse_params or {} parse_result = parser.parse(temp_file_path, parse_params) # Генерируем object_id object_id = f"nin_excel_data_{request.report_type}" # Удаляем старый объект, если он существует и хранилище доступно if self.storage.object_exists(object_id): self.storage.delete_object(object_id) print(f"Старый объект удален: {object_id}") # Сохраняем в хранилище if self.storage.save_dataframe(parse_result, object_id): return UploadResult( success=True, message="Отчет успешно загружен", object_id=object_id ) else: return UploadResult( success=False, message="Ошибка при сохранении в хранилище. Возможно, MinIO недоступен." ) finally: # Удаляем временный файл os.unlink(temp_file_path) except Exception as e: return UploadResult( success=False, message=f"Ошибка при обработке отчета: {str(e)}" ) def get_data(self, request: DataRequest) -> DataResult: """Получение данных из отчета""" try: # Генерируем object_id object_id = f"nin_excel_data_{request.report_type}" # Проверяем существование объекта if not self.storage.object_exists(object_id): return DataResult( success=False, message=f"Отчет типа '{request.report_type}' не найден. Возможно, MinIO недоступен или отчет не был загружен." ) # Загружаем данные из хранилища loaded_data = self.storage.load_dataframe(object_id) if loaded_data is None: return DataResult( success=False, message="Ошибка при загрузке данных из хранилища. Возможно, MinIO недоступен." ) # Получаем парсер parser = get_parser(request.report_type) # Устанавливаем данные в парсер для использования в геттерах parser.df = loaded_data print(f"🔍 DEBUG: ReportService.get_data - установлены данные в парсер {request.report_type}") # Проверяем тип загруженных данных if hasattr(loaded_data, 'shape'): # Это DataFrame print(f"🔍 DEBUG: DataFrame shape: {loaded_data.shape}") print(f"🔍 DEBUG: DataFrame columns: {list(loaded_data.columns) if not loaded_data.empty else 'Empty'}") elif isinstance(loaded_data, dict): # Это словарь (для парсера ПМ) print(f"🔍 DEBUG: Словарь с ключами: {list(loaded_data.keys())}") else: print(f"🔍 DEBUG: Неизвестный тип данных: {type(loaded_data)}") # Получаем параметры запроса get_params = request.get_params or {} # Для svodka_ca определяем режим из данных или используем 'fact' по умолчанию if request.report_type == 'svodka_ca': # Извлекаем режим из DataFrame или используем 'fact' по умолчанию if hasattr(parser, 'df') and parser.df is not None and not parser.df.empty: modes_in_df = parser.df['mode'].unique() if 'mode' in parser.df.columns else ['fact'] # Используем первый найденный режим или 'fact' по умолчанию default_mode = modes_in_df[0] if len(modes_in_df) > 0 else 'fact' else: default_mode = 'fact' # Устанавливаем режим в параметры, если он не указан if 'mode' not in get_params: get_params['mode'] = default_mode # Определяем имя геттера if request.report_type == 'svodka_ca': # Для svodka_ca используем геттер get_ca_data getter_name = 'get_ca_data' elif request.report_type == 'svodka_repair_ca': # Для svodka_repair_ca используем геттер get_repair_data getter_name = 'get_repair_data' elif request.report_type == 'statuses_repair_ca': # Для statuses_repair_ca используем геттер get_repair_statuses getter_name = 'get_repair_statuses' elif request.report_type == 'monitoring_tar': # Для monitoring_tar определяем геттер по параметрам if 'mode' in get_params: # Если есть параметр mode, используем get_tar_data getter_name = 'get_tar_data' else: # Если нет параметра mode, используем get_tar_full_data getter_name = 'get_tar_full_data' elif request.report_type == 'monitoring_fuel': # Для monitoring_fuel определяем геттер из параметра mode getter_name = get_params.pop("mode", None) if not getter_name: # Если режим не указан, берем первый доступный available_getters = list(parser.getters.keys()) if available_getters: getter_name = available_getters[0] print(f"⚠️ Режим не указан, используем первый доступный: {getter_name}") else: return DataResult( success=False, message="Парсер не имеет доступных геттеров" ) elif request.report_type == 'svodka_pm': # Для svodka_pm определяем геттер из параметра mode getter_name = get_params.pop("mode", None) if not getter_name: # Если режим не указан, берем первый доступный available_getters = list(parser.getters.keys()) if available_getters: getter_name = available_getters[0] print(f"⚠️ Режим не указан, используем первый доступный: {getter_name}") else: return DataResult( success=False, message="Парсер не имеет доступных геттеров" ) else: # Для других парсеров определяем из параметра mode getter_name = get_params.pop("mode", None) if not getter_name: # Если режим не указан, берем первый доступный available_getters = list(parser.getters.keys()) if available_getters: getter_name = available_getters[0] print(f"⚠️ Режим не указан, используем первый доступный: {getter_name}") else: return DataResult( success=False, message="Парсер не имеет доступных геттеров" ) # Получаем значение через указанный геттер try: value = parser.get_value(getter_name, get_params) except ValueError as e: return DataResult( success=False, message=f"Ошибка параметров: {str(e)}" ) # Формируем результат if value is not None: if hasattr(value, 'to_dict'): result_data = dict(value) else: result_data = {"value": value} return DataResult(success=True, data=result_data) else: return DataResult(success=False, message="Значение не найдено") except Exception as e: return DataResult( success=False, message=f"Ошибка при получении данных: {str(e)}" )