From bbbfbbd5088589ed92f78ea0c1bd631a39b390f0 Mon Sep 17 00:00:00 2001 From: Maksim Date: Thu, 4 Sep 2025 17:13:39 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=81=D0=BD=D0=BE=D0=B2=D0=B0=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BB=D0=BE=D0=B3=D0=B3=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python_parser/adapters/parsers/svodka_pm.py | 80 +++++++++++---------- python_parser/app/main.py | 8 +++ 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/python_parser/adapters/parsers/svodka_pm.py b/python_parser/adapters/parsers/svodka_pm.py index bf83bf2..749a688 100644 --- a/python_parser/adapters/parsers/svodka_pm.py +++ b/python_parser/adapters/parsers/svodka_pm.py @@ -6,10 +6,14 @@ import json import zipfile import tempfile import shutil +import logging from typing import Dict, Any, List, Optional from core.ports import ParserPort from adapters.pconfig import SINGLE_OGS, replace_id_in_path, find_header_row, data_to_json +# Настройка логгера для модуля +logger = logging.getLogger(__name__) + class SvodkaPMParser(ParserPort): """Парсер для сводок ПМ (план и факт)""" @@ -51,17 +55,17 @@ class SvodkaPMParser(ParserPort): # Разархивируем файл with zipfile.ZipFile(file_path, 'r') as zip_ref: zip_ref.extractall(temp_dir) - print(f"📦 Архив разархивирован в: {temp_dir}") + logger.info(f"📦 Архив разархивирован в: {temp_dir}") # Посмотрим, что находится в архиве - print(f"🔍 Содержимое архива:") + logger.debug(f"🔍 Содержимое архива:") for root, dirs, files in os.walk(temp_dir): level = root.replace(temp_dir, '').count(os.sep) indent = ' ' * 2 * level - print(f"{indent}{os.path.basename(root)}/") + logger.debug(f"{indent}{os.path.basename(root)}/") subindent = ' ' * 2 * (level + 1) for file in files: - print(f"{subindent}{file}") + logger.debug(f"{subindent}{file}") # Создаем словари для хранения данных как в оригинале df_pm_facts = {} # Словарь с данными факта, ключ - ID ОГ @@ -80,8 +84,8 @@ class SvodkaPMParser(ParserPort): elif 'plan' in file.lower() or 'план' in file.lower(): plan_files.append(full_path) - print(f"📊 Найдено файлов факта: {len(fact_files)}") - print(f"📊 Найдено файлов плана: {len(plan_files)}") + logger.info(f"📊 Найдено файлов факта: {len(fact_files)}") + logger.info(f"📊 Найдено файлов плана: {len(plan_files)}") # Обрабатываем найденные файлы for fact_file in fact_files: @@ -91,9 +95,9 @@ class SvodkaPMParser(ParserPort): if 'svodka_fact_pm_' in filename: og_id = filename.replace('svodka_fact_pm_', '').replace('.xlsx', '').replace('.xlsm', '') if og_id in SINGLE_OGS: - print(f'📊 Загрузка факта: {fact_file} (ОГ: {og_id})') + logger.info(f'📊 Загрузка факта: {fact_file} (ОГ: {og_id})') df_pm_facts[og_id] = self._parse_svodka_pm(fact_file, 'Сводка Нефтепереработка') - print(f"✅ Факт загружен для {og_id}") + logger.info(f"✅ Факт загружен для {og_id}") for plan_file in plan_files: # Извлекаем ID ОГ из имени файла @@ -102,9 +106,9 @@ class SvodkaPMParser(ParserPort): if 'svodka_plan_pm_' in filename: og_id = filename.replace('svodka_plan_pm_', '').replace('.xlsx', '').replace('.xlsm', '') if og_id in SINGLE_OGS: - print(f'📊 Загрузка плана: {plan_file} (ОГ: {og_id})') + logger.info(f'📊 Загрузка плана: {plan_file} (ОГ: {og_id})') df_pm_plans[og_id] = self._parse_svodka_pm(plan_file, 'Сводка Нефтепереработка') - print(f"✅ План загружен для {og_id}") + logger.info(f"✅ План загружен для {og_id}") # Инициализируем None для ОГ, для которых файлы не найдены for og_id in SINGLE_OGS: @@ -123,14 +127,14 @@ class SvodkaPMParser(ParserPort): 'df_pm_plans': df_pm_plans } - print(f"🎯 Обработано ОГ: {len([k for k, v in df_pm_facts.items() if v is not None])} факт, {len([k for k, v in df_pm_plans.items() if v is not None])} план") + logger.info(f"🎯 Обработано ОГ: {len([k for k, v in df_pm_facts.items() if v is not None])} факт, {len([k for k, v in df_pm_plans.items() if v is not None])} план") return result finally: # Удаляем временную директорию shutil.rmtree(temp_dir, ignore_errors=True) - print(f"🗑️ Временная директория удалена: {temp_dir}") + logger.debug(f"🗑️ Временная директория удалена: {temp_dir}") def _parse_svodka_pm(self, file_path: str, sheet_name: str, header_num: Optional[int] = None) -> pd.DataFrame: """Парсинг отчетов одного ОГ для БП, ПП и факта""" @@ -226,19 +230,19 @@ class SvodkaPMParser(ParserPort): def _get_svodka_value(self, df_svodka: pd.DataFrame, og_id: str, code: int, search_value: Optional[str] = None): """Служебная функция для простой выборке по сводке""" - print(f"🔍 DEBUG: Ищем код '{code}' для ОГ '{og_id}' в DataFrame с {len(df_svodka)} строками") - print(f"🔍 DEBUG: Первая строка данных: {df_svodka.iloc[0].tolist()}") - print(f"🔍 DEBUG: Доступные индексы: {list(df_svodka.index)}") - print(f"🔍 DEBUG: Доступные столбцы: {list(df_svodka.columns)}") + logger.debug(f"🔍 DEBUG: Ищем код '{code}' для ОГ '{og_id}' в DataFrame с {len(df_svodka)} строками") + logger.debug(f"🔍 DEBUG: Первая строка данных: {df_svodka.iloc[0].tolist()}") + logger.debug(f"🔍 DEBUG: Доступные индексы: {list(df_svodka.index)}") + logger.debug(f"🔍 DEBUG: Доступные столбцы: {list(df_svodka.columns)}") # Проверяем, есть ли код в индексе if code not in df_svodka.index: - print(f"⚠️ Код '{code}' не найден в индексе") + logger.warning(f"⚠️ Код '{code}' не найден в индексе") return 0 # Получаем позицию строки с кодом code_row_loc = df_svodka.index.get_loc(code) - print(f"🔍 DEBUG: Код '{code}' в позиции {code_row_loc}") + logger.debug(f"🔍 DEBUG: Код '{code}' в позиции {code_row_loc}") # Определяем позиции для поиска if search_value is None: @@ -254,14 +258,14 @@ class SvodkaPMParser(ParserPort): if col_name == search_value: target_positions.append(i) - print(f"🔍 DEBUG: Найдены позиции для '{search_value}': {target_positions[:5]}...") - print(f"🔍 DEBUG: Позиции в первой строке: {target_positions[:5]}...") + logger.debug(f"🔍 DEBUG: Найдены позиции для '{search_value}': {target_positions[:5]}...") + logger.debug(f"🔍 DEBUG: Позиции в первой строке: {target_positions[:5]}...") - print(f"🔍 DEBUG: Ищем столбцы с названием '{search_value}'") - print(f"🔍 DEBUG: Целевые позиции: {target_positions[:10]}...") + logger.debug(f"🔍 DEBUG: Ищем столбцы с названием '{search_value}'") + logger.debug(f"🔍 DEBUG: Целевые позиции: {target_positions[:10]}...") if not target_positions: - print(f"⚠️ Позиции '{search_value}' не найдены") + logger.warning(f"⚠️ Позиции '{search_value}' не найдены") return 0 # Извлекаем значения из найденных позиций @@ -285,7 +289,7 @@ class SvodkaPMParser(ParserPort): # Преобразуем в числовой формат numeric_values = pd.to_numeric(values, errors='coerce') - print(f"🔍 DEBUG: Числовые значения (первые 5): {numeric_values.tolist()[:5]}") + logger.debug(f"🔍 DEBUG: Числовые значения (первые 5): {numeric_values.tolist()[:5]}") # Попробуем альтернативное преобразование try: @@ -301,10 +305,10 @@ class SvodkaPMParser(ParserPort): except (ValueError, TypeError): manual_values.append(0) - print(f"🔍 DEBUG: Ручное преобразование (первые 5): {manual_values[:5]}") + logger.debug(f"🔍 DEBUG: Ручное преобразование (первые 5): {manual_values[:5]}") numeric_values = pd.Series(manual_values) except Exception as e: - print(f"⚠️ Ошибка при ручном преобразовании: {e}") + logger.warning(f"⚠️ Ошибка при ручном преобразовании: {e}") # Используем исходные значения numeric_values = pd.Series([0 if pd.isna(v) or v is None else v for v in values]) @@ -338,7 +342,7 @@ class SvodkaPMParser(ParserPort): # Получаем данные из сохраненных словарей (через self.df) if not hasattr(self, 'df') or self.df is None: - print("❌ Данные не загружены. Сначала загрузите ZIP архив.") + logger.error("❌ Данные не загружены. Сначала загрузите ZIP архив.") return {col: {str(code): None for code in codes} for col in columns} # Извлекаем словари из сохраненных данных @@ -349,10 +353,10 @@ class SvodkaPMParser(ParserPort): fact_df = df_pm_facts.get(og_id) plan_df = df_pm_plans.get(og_id) - print(f"🔍 ===== НАЧАЛО ОБРАБОТКИ ОГ {og_id} =====") - print(f"🔍 Коды: {codes}") - print(f"🔍 Столбцы: {columns}") - print(f"🔍 Получены данные для {og_id}: факт={'✅' if fact_df is not None else '❌'}, план={'✅' if plan_df is not None else '❌'}") + logger.debug(f"🔍 ===== НАЧАЛО ОБРАБОТКИ ОГ {og_id} =====") + logger.debug(f"🔍 Коды: {codes}") + logger.debug(f"🔍 Столбцы: {columns}") + logger.debug(f"🔍 Получены данные для {og_id}: факт={'✅' if fact_df is not None else '❌'}, план={'✅' if plan_df is not None else '❌'}") # Определяем, какие столбцы из какого датафрейма брать for col in columns: @@ -360,24 +364,24 @@ class SvodkaPMParser(ParserPort): if col in ['ПП', 'БП']: if plan_df is None: - print(f"❌ Невозможно обработать '{col}': нет данных плана для {og_id}") + logger.warning(f"❌ Невозможно обработать '{col}': нет данных плана для {og_id}") else: - print(f"🔍 DEBUG: ===== ОБРАБАТЫВАЕМ '{col}' ИЗ ДАННЫХ ПЛАНА =====") + logger.debug(f"🔍 DEBUG: ===== ОБРАБАТЫВАЕМ '{col}' ИЗ ДАННЫХ ПЛАНА =====") for code in codes: - print(f"🔍 DEBUG: --- Код {code} для {col} ---") + logger.debug(f"🔍 DEBUG: --- Код {code} для {col} ---") val = self._get_svodka_value(plan_df, og_id, code, col) col_result[str(code)] = val - print(f"🔍 DEBUG: ===== ЗАВЕРШИЛИ ОБРАБОТКУ '{col}' =====") + logger.debug(f"🔍 DEBUG: ===== ЗАВЕРШИЛИ ОБРАБОТКУ '{col}' =====") elif col in ['ТБ', 'СЭБ', 'НЭБ']: if fact_df is None: - print(f"❌ Невозможно обработать '{col}': нет данных факта для {og_id}") + logger.warning(f"❌ Невозможно обработать '{col}': нет данных факта для {og_id}") else: for code in codes: val = self._get_svodka_value(fact_df, og_id, code, col) col_result[str(code)] = val else: - print(f"⚠️ Неизвестный столбец: '{col}'. Пропускаем.") + logger.warning(f"⚠️ Неизвестный столбец: '{col}'. Пропускаем.") col_result = {str(code): None for code in codes} result[col] = col_result @@ -443,7 +447,7 @@ class SvodkaPMParser(ParserPort): data = self._get_svodka_og(og_id, codes, columns, search) total_result[og_id] = data except Exception as e: - print(f"❌ Ошибка при обработке {og_id}: {e}") + logger.error(f"❌ Ошибка при обработке {og_id}: {e}") total_result[og_id] = None json_result = data_to_json(total_result) diff --git a/python_parser/app/main.py b/python_parser/app/main.py index 161eccb..837f664 100644 --- a/python_parser/app/main.py +++ b/python_parser/app/main.py @@ -1,10 +1,18 @@ import os import multiprocessing import uvicorn +import logging from typing import Dict, List from fastapi import FastAPI, File, UploadFile, HTTPException, status from fastapi.responses import JSONResponse +# Настройка логирования +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(name)s - %(lineno)d - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' +) + from adapters.storage import MinIOStorageAdapter from adapters.parsers import SvodkaPMParser, SvodkaCAParser, MonitoringFuelParser, MonitoringTarParser, SvodkaRepairCAParser, StatusesRepairCAParser, OperSpravkaTechPosParser