319 lines
12 KiB
Python
319 lines
12 KiB
Python
"""
|
||
Эндпоинты для сводки ПМ
|
||
"""
|
||
import logging
|
||
from fastapi import APIRouter, File, UploadFile, HTTPException, status
|
||
from fastapi.responses import JSONResponse
|
||
|
||
from adapters.storage import MinIOStorageAdapter
|
||
from adapters.parsers import SvodkaPMParser
|
||
from core.models import UploadRequest, DataRequest
|
||
from core.services import ReportService
|
||
from core.async_services import AsyncReportService
|
||
from app.schemas import (
|
||
UploadResponse, UploadErrorResponse,
|
||
SvodkaPMTotalOGsRequest, SvodkaPMSingleOGRequest
|
||
)
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Создаем роутер для сводки ПМ
|
||
router = APIRouter()
|
||
|
||
|
||
def get_report_service() -> ReportService:
|
||
"""Получение экземпляра сервиса отчетов"""
|
||
storage_adapter = MinIOStorageAdapter()
|
||
return ReportService(storage_adapter)
|
||
|
||
|
||
def get_async_report_service() -> AsyncReportService:
|
||
"""Получение экземпляра асинхронного сервиса отчетов"""
|
||
from core.services import ReportService
|
||
storage_adapter = MinIOStorageAdapter()
|
||
report_service = ReportService(storage_adapter)
|
||
return AsyncReportService(report_service)
|
||
|
||
|
||
@router.post("/svodka_pm/upload-zip", tags=[SvodkaPMParser.name],
|
||
summary="Загрузка файлов сводок ПМ одним ZIP-архивом",
|
||
response_model=UploadResponse,
|
||
responses={
|
||
400: {"model": UploadErrorResponse, "description": "Неверный формат архива или файлов"},
|
||
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
|
||
},)
|
||
async def upload_svodka_pm_zip(
|
||
zip_file: UploadFile = File(..., description="ZIP архив с Excel файлами (.zip)")
|
||
):
|
||
"""Загрузка файлов сводок ПМ (факта и плана) по всем ОГ в **одном ZIP-архиве**
|
||
|
||
### Поддерживаемые форматы:
|
||
- **ZIP архивы** с файлами сводок ПМ
|
||
|
||
### Структура данных:
|
||
- Обрабатывает ZIP архивы с файлами по ОГ (svodka_fact_SNPZ.xlsx, svodka_plan_SNPZ.xlsx и т.д.)
|
||
- Извлекает данные по кодам строк и колонкам
|
||
- Возвращает агрегированные данные по ОГ
|
||
|
||
### Пример использования:
|
||
1. Подготовьте ZIP архив с файлами сводок ПМ
|
||
2. Загрузите архив через этот эндпоинт
|
||
3. Используйте полученный `object_id` для запросов данных
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Проверяем тип файла - только ZIP архивы
|
||
if not zip_file.filename.endswith('.zip'):
|
||
return JSONResponse(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
content=UploadErrorResponse(
|
||
message="Файл должен быть ZIP архивом",
|
||
error_code="INVALID_FILE_TYPE",
|
||
details={
|
||
"expected_formats": [".zip"],
|
||
"received_format": zip_file.filename.split('.')[-1] if '.' in zip_file.filename else "unknown"
|
||
}
|
||
).model_dump()
|
||
)
|
||
|
||
# Читаем содержимое файла
|
||
file_content = await zip_file.read()
|
||
|
||
# Создаем запрос
|
||
request = UploadRequest(
|
||
report_type='svodka_pm',
|
||
file_content=file_content,
|
||
file_name=zip_file.filename
|
||
)
|
||
# Загружаем отчет
|
||
result = report_service.upload_report(request)
|
||
if result.success:
|
||
return UploadResponse(
|
||
success=True,
|
||
message=result.message,
|
||
object_id=result.object_id
|
||
)
|
||
else:
|
||
return JSONResponse(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
content=UploadErrorResponse(
|
||
message=result.message,
|
||
error_code="ERR_UPLOAD"
|
||
).model_dump(),
|
||
)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
return JSONResponse(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
content=UploadErrorResponse(
|
||
message=f"Внутренняя ошибка сервера: {str(e)}",
|
||
error_code="INTERNAL_SERVER_ERROR"
|
||
).model_dump()
|
||
)
|
||
|
||
|
||
@router.post("/svodka_pm/get_single_og", tags=[SvodkaPMParser.name],
|
||
summary="Получение данных по одному ОГ")
|
||
async def get_svodka_pm_single_og(
|
||
request_data: SvodkaPMSingleOGRequest
|
||
):
|
||
"""Получение данных из сводок ПМ (факта и плана) по одному ОГ
|
||
|
||
### Структура параметров:
|
||
- `id`: **Идентификатор МА** для запрашиваемого ОГ (обязательный)
|
||
- `codes`: **Массив кодов** выбираемых строк (обязательный)
|
||
- `columns`: **Массив названий** выбираемых столбцов (обязательный)
|
||
- `search`: **Опциональный параметр** для фильтрации ("Итого" или null)
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"id": "SNPZ",
|
||
"codes": [78, 79],
|
||
"columns": ["ПП", "СЭБ"]
|
||
}
|
||
```
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Создаем запрос
|
||
request_dict = request_data.model_dump()
|
||
request_dict['mode'] = 'single_og'
|
||
request = DataRequest(
|
||
report_type='svodka_pm',
|
||
get_params=request_dict
|
||
)
|
||
|
||
# Получаем данные
|
||
result = report_service.get_data(request)
|
||
|
||
if result.success:
|
||
return {
|
||
"success": True,
|
||
"data": result.data
|
||
}
|
||
else:
|
||
raise HTTPException(status_code=404, detail=result.message)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@router.post("/svodka_pm/get_total_ogs", tags=[SvodkaPMParser.name],
|
||
summary="Получение данных по всем ОГ")
|
||
async def get_svodka_pm_total_ogs(
|
||
request_data: SvodkaPMTotalOGsRequest
|
||
):
|
||
"""Получение данных из сводок ПМ (факта и плана) по всем ОГ
|
||
|
||
### Структура параметров:
|
||
- `codes`: **Массив кодов** выбираемых строк (обязательный)
|
||
- `columns`: **Массив названий** выбираемых столбцов (обязательный)
|
||
- `search`: **Опциональный параметр** для фильтрации ("Итого" или null)
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"codes": [78, 79],
|
||
"columns": ["ПП", "СЭБ"]
|
||
}
|
||
```
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Создаем запрос
|
||
request_dict = request_data.model_dump()
|
||
request_dict['mode'] = 'total_ogs'
|
||
request = DataRequest(
|
||
report_type='svodka_pm',
|
||
get_params=request_dict
|
||
)
|
||
|
||
# Получаем данные
|
||
result = report_service.get_data(request)
|
||
|
||
if result.success:
|
||
return {
|
||
"success": True,
|
||
"data": result.data
|
||
}
|
||
else:
|
||
raise HTTPException(status_code=404, detail=result.message)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@router.post("/svodka_pm/get_data", tags=[SvodkaPMParser.name])
|
||
async def get_svodka_pm_data(
|
||
request_data: dict
|
||
):
|
||
"""Получение данных из сводок ПМ (факта и плана)
|
||
|
||
### Структура параметров:
|
||
- `indicator_id`: **ID индикатора** для поиска (обязательный)
|
||
- `code`: **Код строки** для поиска (обязательный)
|
||
- `search_value`: **Опциональное значение** для поиска
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"indicator_id": "SNPZ",
|
||
"code": 78,
|
||
"search_value": "Итого"
|
||
}
|
||
```
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Создаем запрос
|
||
request = DataRequest(
|
||
report_type='svodka_pm',
|
||
get_params=request_data
|
||
)
|
||
|
||
# Получаем данные
|
||
result = report_service.get_data(request)
|
||
|
||
if result.success:
|
||
return {
|
||
"success": True,
|
||
"data": result.data
|
||
}
|
||
else:
|
||
raise HTTPException(status_code=404, detail=result.message)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@router.post("/async/svodka_pm/upload-zip", tags=[SvodkaPMParser.name],
|
||
summary="Асинхронная загрузка файлов сводок ПМ одним ZIP-архивом",
|
||
response_model=UploadResponse,
|
||
responses={
|
||
400: {"model": UploadErrorResponse, "description": "Неверный формат архива или файлов"},
|
||
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
|
||
},)
|
||
async def async_upload_svodka_pm_zip(
|
||
zip_file: UploadFile = File(..., description="ZIP архив с Excel файлами (.zip)")
|
||
):
|
||
"""Асинхронная загрузка файлов сводок ПМ (факта и плана) по всем ОГ в **одном ZIP-архиве**"""
|
||
async_service = get_async_report_service()
|
||
try:
|
||
if not zip_file.filename.endswith('.zip'):
|
||
return JSONResponse(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
content=UploadErrorResponse(
|
||
message="Файл должен быть ZIP архивом",
|
||
error_code="INVALID_FILE_TYPE",
|
||
details={
|
||
"expected_formats": [".zip"],
|
||
"received_format": zip_file.filename.split('.')[-1] if '.' in zip_file.filename else "unknown"
|
||
}
|
||
).model_dump()
|
||
)
|
||
file_content = await zip_file.read()
|
||
# Создаем запрос
|
||
request = UploadRequest(
|
||
report_type='svodka_pm',
|
||
file_content=file_content,
|
||
file_name=zip_file.filename
|
||
)
|
||
# Загружаем отчет асинхронно
|
||
result = await async_service.upload_report_async(request)
|
||
|
||
if result.success:
|
||
return UploadResponse(
|
||
success=True,
|
||
message=result.message,
|
||
object_id=result.object_id
|
||
)
|
||
else:
|
||
return JSONResponse(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
content=UploadErrorResponse(
|
||
message=result.message,
|
||
error_code="UPLOAD_FAILED"
|
||
).model_dump()
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при асинхронной загрузке сводки ПМ: {str(e)}")
|
||
return JSONResponse(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
content=UploadErrorResponse(
|
||
message=f"Внутренняя ошибка сервера: {str(e)}",
|
||
error_code="INTERNAL_ERROR"
|
||
).model_dump()
|
||
) |