init
This commit is contained in:
0
python_parser/core/__init__.py
Normal file
0
python_parser/core/__init__.py
Normal file
45
python_parser/core/models.py
Normal file
45
python_parser/core/models.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Доменные модели
|
||||
"""
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict, Any
|
||||
import pandas as pd
|
||||
|
||||
|
||||
@dataclass
|
||||
class UploadRequest:
|
||||
"""Запрос на загрузку отчета"""
|
||||
report_type: str
|
||||
file_content: bytes
|
||||
file_name: str
|
||||
parse_params: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class UploadResult:
|
||||
"""Результат загрузки отчета"""
|
||||
success: bool
|
||||
message: str
|
||||
object_id: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class DataRequest:
|
||||
"""Запрос на получение данных"""
|
||||
report_type: str
|
||||
get_params: Dict[str, Any]
|
||||
|
||||
|
||||
@dataclass
|
||||
class DataResult:
|
||||
"""Результат получения данных"""
|
||||
success: bool
|
||||
data: Optional[Dict[str, Any]] = None
|
||||
message: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class ParsedData:
|
||||
"""Распарсенные данные"""
|
||||
dataframe: pd.DataFrame
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
49
python_parser/core/ports.py
Normal file
49
python_parser/core/ports.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
Порты (интерфейсы) для hexagonal architecture
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
import pandas as pd
|
||||
|
||||
|
||||
class ParserPort(ABC):
|
||||
"""Интерфейс для парсеров"""
|
||||
|
||||
@abstractmethod
|
||||
def parse(self, file_path: str, params: dict) -> pd.DataFrame:
|
||||
"""Парсинг файла и возврат DataFrame"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_value(self, df: pd.DataFrame, params: dict):
|
||||
"""Получение значения из DataFrame по параметрам"""
|
||||
pass
|
||||
|
||||
# @abstractmethod
|
||||
# def get_schema(self) -> dict:
|
||||
# """Возвращает схему входных параметров для парсера"""
|
||||
# pass
|
||||
|
||||
|
||||
class StoragePort(ABC):
|
||||
"""Интерфейс для хранилища данных"""
|
||||
|
||||
@abstractmethod
|
||||
def save_dataframe(self, df: pd.DataFrame, object_id: str) -> bool:
|
||||
"""Сохранение DataFrame"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def load_dataframe(self, object_id: str) -> Optional[pd.DataFrame]:
|
||||
"""Загрузка DataFrame"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete_object(self, object_id: str) -> bool:
|
||||
"""Удаление объекта"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def object_exists(self, object_id: str) -> bool:
|
||||
"""Проверка существования объекта"""
|
||||
pass
|
||||
120
python_parser/core/services.py
Normal file
120
python_parser/core/services.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Сервисы (бизнес-логика)
|
||||
"""
|
||||
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 {}
|
||||
df = 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(df, object_id):
|
||||
return UploadResult(
|
||||
success=True,
|
||||
message="Отчет успешно загружен",
|
||||
object_id=object_id
|
||||
)
|
||||
else:
|
||||
return UploadResult(
|
||||
success=False,
|
||||
message="Ошибка при сохранении в хранилище"
|
||||
)
|
||||
|
||||
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}' не найден"
|
||||
)
|
||||
|
||||
# Загружаем DataFrame из хранилища
|
||||
df = self.storage.load_dataframe(object_id)
|
||||
if df is None:
|
||||
return DataResult(
|
||||
success=False,
|
||||
message="Ошибка при загрузке данных из хранилища"
|
||||
)
|
||||
|
||||
# Получаем парсер
|
||||
parser = get_parser(request.report_type)
|
||||
|
||||
# Получаем значение
|
||||
value = parser.get_value(df, request.get_params)
|
||||
|
||||
# Формируем результат
|
||||
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)}"
|
||||
)
|
||||
Reference in New Issue
Block a user