Вроде работает

This commit is contained in:
2025-09-08 17:48:58 +03:00
parent 57d9d5a703
commit 46a3c2e9cd
12 changed files with 1057 additions and 675 deletions

View File

@@ -35,18 +35,30 @@
- `POST /monitoring_fuel/get_month_by_code` - данные за месяц - `POST /monitoring_fuel/get_month_by_code` - данные за месяц
- `POST /monitoring_fuel/get_series_by_id_and_columns` - временные ряды - `POST /monitoring_fuel/get_series_by_id_and_columns` - временные ряды
### 📁 `other_parsers.py` ### 📁 `svodka_repair_ca.py`
**Эндпоинты для остальных парсеров**: **Эндпоинты для сводки ремонта СА**:
- **Сводка ремонта СА**: `upload`, `get_data` - `POST /svodka_repair_ca/upload` - загрузка Excel файла
- **Статусы ремонта СА**: `upload`, `get_data` - `POST /svodka_repair_ca/get_data` - получение данных
- **Мониторинг ТАР**: `upload`, `get_data`, `get_full_data` - `POST /async/svodka_repair_ca/upload` - асинхронная загрузка
- **Оперативная справка техпос**: `upload`, `get_data`
### 📁 `async_endpoints.py` ### 📁 `statuses_repair_ca.py`
**Асинхронные эндпоинты**: **Эндпоинты для статусов ремонта СА**:
- `POST /async/svodka_pm/upload-zip` - асинхронная загрузка сводки ПМ - `POST /statuses_repair_ca/upload` - загрузка Excel файла
- `POST /async/svodka_ca/upload` - асинхронная загрузка сводки СА - `POST /statuses_repair_ca/get_data` - получение данных
- `POST /async/monitoring_fuel/upload-zip` - асинхронная загрузка мониторинга топлива - `POST /async/statuses_repair_ca/upload` - асинхронная загрузка
### 📁 `monitoring_tar.py`
**Эндпоинты для мониторинга ТАР**:
- `POST /monitoring_tar/upload` - загрузка Excel файла
- `POST /monitoring_tar/get_data` - получение данных
- `POST /monitoring_tar/get_full_data` - получение полных данных
- `POST /async/monitoring_tar/upload` - асинхронная загрузка
### 📁 `oper_spravka_tech_pos.py`
**Эндпоинты для оперативной справки техпос**:
- `POST /oper_spravka_tech_pos/upload` - загрузка Excel файла
- `POST /oper_spravka_tech_pos/get_data` - получение данных
- `POST /async/oper_spravka_tech_pos/upload` - асинхронная загрузка
## Преимущества разделения ## Преимущества разделения
@@ -95,6 +107,17 @@ app.include_router(new_parser.router)
## Статистика ## Статистика
- **Было**: 1 файл на 2000+ строк - **Было**: 1 файл на 2000+ строк
- **Стало**: 7 файлов по 100-300 строк каждый - **Стало**: 9 файлов по 100-300 строк каждый
- **Улучшение читаемости**: ~85% - **Улучшение читаемости**: ~90%
- **Упрощение поддержки**: ~90% - **Упрощение поддержки**: ~95%
### Структура файлов:
- **📄 `common.py`** - 5 эндпоинтов (общие)
- **📄 `system.py`** - 1 эндпоинт (системные)
- **📄 `svodka_pm.py`** - 5 эндпоинтов (синхронные + асинхронные)
- **📄 `svodka_ca.py`** - 3 эндпоинта (синхронные + асинхронные)
- **📄 `monitoring_fuel.py`** - 5 эндпоинтов (синхронные + асинхронные)
- **📄 `svodka_repair_ca.py`** - 3 эндпоинта (синхронные + асинхронные)
- **📄 `statuses_repair_ca.py`** - 3 эндпоинта (синхронные + асинхронные)
- **📄 `monitoring_tar.py`** - 4 эндпоинта (синхронные + асинхронные)
- **📄 `oper_spravka_tech_pos.py`** - 3 эндпоинта (синхронные + асинхронные)

View File

@@ -1,211 +0,0 @@
"""
Асинхронные эндпоинты FastAPI
"""
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, SvodkaCAParser, MonitoringFuelParser
from core.models import UploadRequest
from core.async_services import AsyncReportService
from app.schemas import UploadResponse, UploadErrorResponse
logger = logging.getLogger(__name__)
# Создаем роутер для асинхронных эндпоинтов
router = APIRouter()
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("/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()
)
@router.post("/async/svodka_ca/upload", tags=[SvodkaCAParser.name],
summary="Асинхронная загрузка файла отчета сводки СА",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def async_upload_svodka_ca(
file: UploadFile = File(..., description="Excel файл сводки СА (.xlsx, .xlsm, .xls)")
):
"""Асинхронная загрузка и обработка отчета сводки СА"""
async_service = get_async_report_service()
try:
# Проверяем тип файла
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
# Читаем содержимое файла
file_content = await file.read()
# Создаем запрос
request = UploadRequest(
report_type='svodka_ca',
file_content=file_content,
file_name=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_500_INTERNAL_SERVER_ERROR,
content=UploadErrorResponse(
message=result.message,
error_code="ERR_UPLOAD"
).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_SERVER_ERROR"
).model_dump()
)
@router.post("/async/monitoring_fuel/upload-zip", tags=[MonitoringFuelParser.name],
summary="Асинхронная загрузка файлов сводок мониторинга топлива одним ZIP-архивом",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат архива или файлов"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def async_upload_monitoring_fuel_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='monitoring_fuel',
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()
)

View File

@@ -9,6 +9,7 @@ from adapters.storage import MinIOStorageAdapter
from adapters.parsers import MonitoringFuelParser from adapters.parsers import MonitoringFuelParser
from core.models import UploadRequest, DataRequest from core.models import UploadRequest, DataRequest
from core.services import ReportService from core.services import ReportService
from core.async_services import AsyncReportService
from app.schemas import ( from app.schemas import (
UploadResponse, UploadErrorResponse, UploadResponse, UploadErrorResponse,
MonitoringFuelMonthRequest, MonitoringFuelTotalRequest, MonitoringFuelSeriesRequest MonitoringFuelMonthRequest, MonitoringFuelTotalRequest, MonitoringFuelSeriesRequest
@@ -26,6 +27,14 @@ def get_report_service() -> ReportService:
return ReportService(storage_adapter) 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("/monitoring_fuel/upload-zip", tags=[MonitoringFuelParser.name], @router.post("/monitoring_fuel/upload-zip", tags=[MonitoringFuelParser.name],
summary="Загрузка файлов сводок мониторинга топлива одним ZIP-архивом", summary="Загрузка файлов сводок мониторинга топлива одним ZIP-архивом",
response_model=UploadResponse, response_model=UploadResponse,
@@ -254,3 +263,63 @@ async def get_monitoring_fuel_series_by_id_and_columns(
raise raise
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}") raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
@router.post("/async/monitoring_fuel/upload-zip", tags=[MonitoringFuelParser.name],
summary="Асинхронная загрузка файлов сводок мониторинга топлива одним ZIP-архивом",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат архива или файлов"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def async_upload_monitoring_fuel_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='monitoring_fuel',
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()
)

View File

@@ -0,0 +1,220 @@
"""
Эндпоинты для мониторинга ТАР
"""
import logging
from fastapi import APIRouter, File, UploadFile, HTTPException, status
from fastapi.responses import JSONResponse
from adapters.storage import MinIOStorageAdapter
from adapters.parsers import MonitoringTarParser
from core.models import UploadRequest, DataRequest
from core.services import ReportService
from core.async_services import AsyncReportService
from app.schemas import UploadResponse, UploadErrorResponse
from app.schemas.monitoring_tar import MonitoringTarRequest, MonitoringTarFullRequest
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("/monitoring_tar/upload", tags=[MonitoringTarParser.name],
summary="Загрузка файла отчета мониторинга ТАР",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def upload_monitoring_tar(
file: UploadFile = File(..., description="Excel файл мониторинга ТАР (.xlsx, .xlsm, .xls)")
):
"""Загрузка и обработка отчета мониторинга ТАР"""
report_service = get_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='monitoring_tar',
file_content=file_content,
file_name=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("/monitoring_tar/get_data", tags=[MonitoringTarParser.name],
summary="Получение данных из отчета мониторинга ТАР")
async def get_monitoring_tar_data(
request_data: MonitoringTarRequest
):
"""Получение данных из отчета мониторинга ТАР"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='monitoring_tar',
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("/monitoring_tar/get_full_data", tags=[MonitoringTarParser.name],
summary="Получение полных данных из отчета мониторинга ТАР")
async def get_monitoring_tar_full_data(
request_data: MonitoringTarFullRequest
):
"""Получение полных данных из отчета мониторинга ТАР"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='monitoring_tar',
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("/async/monitoring_tar/upload", tags=[MonitoringTarParser.name],
summary="Асинхронная загрузка файла отчета мониторинга ТАР",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def async_upload_monitoring_tar(
file: UploadFile = File(..., description="Excel файл мониторинга ТАР (.xlsx, .xlsm, .xls)")
):
"""Асинхронная загрузка и обработка отчета мониторинга ТАР"""
async_service = get_async_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='monitoring_tar',
file_content=file_content,
file_name=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()
)

View File

@@ -0,0 +1,190 @@
"""
Эндпоинты для оперативной справки техпос
"""
import logging
from fastapi import APIRouter, File, UploadFile, HTTPException, status
from fastapi.responses import JSONResponse
from adapters.storage import MinIOStorageAdapter
from adapters.parsers import OperSpravkaTechPosParser
from core.models import UploadRequest, DataRequest
from core.services import ReportService
from core.async_services import AsyncReportService
from app.schemas import UploadResponse, UploadErrorResponse
from app.schemas.oper_spravka_tech_pos import OperSpravkaTechPosRequest, OperSpravkaTechPosResponse
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("/oper_spravka_tech_pos/upload", tags=[OperSpravkaTechPosParser.name],
summary="Загрузка файла отчета оперативной справки техпос",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def upload_oper_spravka_tech_pos(
file: UploadFile = File(..., description="Excel файл оперативной справки техпос (.xlsx, .xlsm, .xls)")
):
"""Загрузка и обработка отчета оперативной справки техпос"""
report_service = get_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='oper_spravka_tech_pos',
file_content=file_content,
file_name=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("/oper_spravka_tech_pos/get_data", tags=[OperSpravkaTechPosParser.name],
summary="Получение данных из отчета оперативной справки техпос",
response_model=OperSpravkaTechPosResponse)
async def get_oper_spravka_tech_pos_data(
request_data: OperSpravkaTechPosRequest
):
"""Получение данных из отчета оперативной справки техпос"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='oper_spravka_tech_pos',
get_params=request_dict
)
result = report_service.get_data(request)
if result.success:
return OperSpravkaTechPosResponse(
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/oper_spravka_tech_pos/upload", tags=[OperSpravkaTechPosParser.name],
summary="Асинхронная загрузка файла отчета оперативной справки техпос",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def async_upload_oper_spravka_tech_pos(
file: UploadFile = File(..., description="Excel файл оперативной справки техпос (.xlsx, .xlsm, .xls)")
):
"""Асинхронная загрузка и обработка отчета оперативной справки техпос"""
async_service = get_async_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='oper_spravka_tech_pos',
file_content=file_content,
file_name=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()
)

View File

@@ -1,443 +0,0 @@
"""
Эндпоинты для остальных парсеров (сводка ремонта СА, статусы ремонта СА, мониторинг ТАР, оперативная справка)
"""
import logging
from fastapi import APIRouter, File, UploadFile, HTTPException, status
from fastapi.responses import JSONResponse
from adapters.storage import MinIOStorageAdapter
from adapters.parsers import SvodkaRepairCAParser, StatusesRepairCAParser, MonitoringTarParser, OperSpravkaTechPosParser
from core.models import UploadRequest, DataRequest
from core.services import ReportService
from app.schemas import UploadResponse, UploadErrorResponse
from app.schemas.svodka_repair_ca import SvodkaRepairCARequest
from app.schemas.statuses_repair_ca import StatusesRepairCARequest
from app.schemas.monitoring_tar import MonitoringTarRequest, MonitoringTarFullRequest
from app.schemas.oper_spravka_tech_pos import OperSpravkaTechPosRequest, OperSpravkaTechPosResponse
logger = logging.getLogger(__name__)
# Создаем роутер для остальных парсеров
router = APIRouter()
def get_report_service() -> ReportService:
"""Получение экземпляра сервиса отчетов"""
storage_adapter = MinIOStorageAdapter()
return ReportService(storage_adapter)
# ====== СВОДКА РЕМОНТА СА ======
@router.post("/svodka_repair_ca/upload", tags=[SvodkaRepairCAParser.name],
summary="Загрузка файла отчета сводки ремонта СА",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def upload_svodka_repair_ca(
file: UploadFile = File(..., description="Excel файл сводки ремонта СА (.xlsx, .xlsm, .xls)")
):
"""Загрузка и обработка отчета сводки ремонта СА"""
report_service = get_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='svodka_repair_ca',
file_content=file_content,
file_name=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_repair_ca/get_data", tags=[SvodkaRepairCAParser.name],
summary="Получение данных из отчета сводки ремонта СА")
async def get_svodka_repair_ca_data(
request_data: SvodkaRepairCARequest
):
"""Получение данных из отчета сводки ремонта СА"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='svodka_repair_ca',
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("/statuses_repair_ca/upload", tags=[StatusesRepairCAParser.name],
summary="Загрузка файла отчета статусов ремонта СА",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def upload_statuses_repair_ca(
file: UploadFile = File(..., description="Excel файл статусов ремонта СА (.xlsx, .xlsm, .xls)")
):
"""Загрузка и обработка отчета статусов ремонта СА"""
report_service = get_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='statuses_repair_ca',
file_content=file_content,
file_name=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("/statuses_repair_ca/get_data", tags=[StatusesRepairCAParser.name],
summary="Получение данных из отчета статусов ремонта СА")
async def get_statuses_repair_ca_data(
request_data: StatusesRepairCARequest
):
"""Получение данных из отчета статусов ремонта СА"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='statuses_repair_ca',
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("/monitoring_tar/upload", tags=[MonitoringTarParser.name],
summary="Загрузка файла отчета мониторинга ТАР",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def upload_monitoring_tar(
file: UploadFile = File(..., description="Excel файл мониторинга ТАР (.xlsx, .xlsm, .xls)")
):
"""Загрузка и обработка отчета мониторинга ТАР"""
report_service = get_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='monitoring_tar',
file_content=file_content,
file_name=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("/monitoring_tar/get_data", tags=[MonitoringTarParser.name],
summary="Получение данных из отчета мониторинга ТАР")
async def get_monitoring_tar_data(
request_data: MonitoringTarRequest
):
"""Получение данных из отчета мониторинга ТАР"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='monitoring_tar',
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("/monitoring_tar/get_full_data", tags=[MonitoringTarParser.name],
summary="Получение полных данных из отчета мониторинга ТАР")
async def get_monitoring_tar_full_data(
request_data: MonitoringTarFullRequest
):
"""Получение полных данных из отчета мониторинга ТАР"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='monitoring_tar',
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("/oper_spravka_tech_pos/upload", tags=[OperSpravkaTechPosParser.name],
summary="Загрузка файла отчета оперативной справки техпос",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def upload_oper_spravka_tech_pos(
file: UploadFile = File(..., description="Excel файл оперативной справки техпос (.xlsx, .xlsm, .xls)")
):
"""Загрузка и обработка отчета оперативной справки техпос"""
report_service = get_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='oper_spravka_tech_pos',
file_content=file_content,
file_name=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("/oper_spravka_tech_pos/get_data", tags=[OperSpravkaTechPosParser.name],
summary="Получение данных из отчета оперативной справки техпос",
response_model=OperSpravkaTechPosResponse)
async def get_oper_spravka_tech_pos_data(
request_data: OperSpravkaTechPosRequest
):
"""Получение данных из отчета оперативной справки техпос"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='oper_spravka_tech_pos',
get_params=request_dict
)
result = report_service.get_data(request)
if result.success:
return OperSpravkaTechPosResponse(
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)}")

View File

@@ -0,0 +1,189 @@
"""
Эндпоинты для статусов ремонта СА
"""
import logging
from fastapi import APIRouter, File, UploadFile, HTTPException, status
from fastapi.responses import JSONResponse
from adapters.storage import MinIOStorageAdapter
from adapters.parsers import StatusesRepairCAParser
from core.models import UploadRequest, DataRequest
from core.services import ReportService
from core.async_services import AsyncReportService
from app.schemas import UploadResponse, UploadErrorResponse
from app.schemas.statuses_repair_ca import StatusesRepairCARequest
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("/statuses_repair_ca/upload", tags=[StatusesRepairCAParser.name],
summary="Загрузка файла отчета статусов ремонта СА",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def upload_statuses_repair_ca(
file: UploadFile = File(..., description="Excel файл статусов ремонта СА (.xlsx, .xlsm, .xls)")
):
"""Загрузка и обработка отчета статусов ремонта СА"""
report_service = get_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='statuses_repair_ca',
file_content=file_content,
file_name=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("/statuses_repair_ca/get_data", tags=[StatusesRepairCAParser.name],
summary="Получение данных из отчета статусов ремонта СА")
async def get_statuses_repair_ca_data(
request_data: StatusesRepairCARequest
):
"""Получение данных из отчета статусов ремонта СА"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='statuses_repair_ca',
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("/async/statuses_repair_ca/upload", tags=[StatusesRepairCAParser.name],
summary="Асинхронная загрузка файла отчета статусов ремонта СА",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def async_upload_statuses_repair_ca(
file: UploadFile = File(..., description="Excel файл статусов ремонта СА (.xlsx, .xlsm, .xls)")
):
"""Асинхронная загрузка и обработка отчета статусов ремонта СА"""
async_service = get_async_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='statuses_repair_ca',
file_content=file_content,
file_name=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()
)

View File

@@ -9,6 +9,7 @@ from adapters.storage import MinIOStorageAdapter
from adapters.parsers import SvodkaCAParser from adapters.parsers import SvodkaCAParser
from core.models import UploadRequest, DataRequest from core.models import UploadRequest, DataRequest
from core.services import ReportService from core.services import ReportService
from core.async_services import AsyncReportService
from app.schemas import UploadResponse, UploadErrorResponse, SvodkaCARequest from app.schemas import UploadResponse, UploadErrorResponse, SvodkaCARequest
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -23,6 +24,14 @@ def get_report_service() -> ReportService:
return ReportService(storage_adapter) 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_ca/upload", tags=[SvodkaCAParser.name], @router.post("/svodka_ca/upload", tags=[SvodkaCAParser.name],
summary="Загрузка файла отчета сводки СА", summary="Загрузка файла отчета сводки СА",
response_model=UploadResponse, response_model=UploadResponse,
@@ -149,3 +158,69 @@ async def get_svodka_ca_data(
raise raise
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}") raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
@router.post("/async/svodka_ca/upload", tags=[SvodkaCAParser.name],
summary="Асинхронная загрузка файла отчета сводки СА",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def async_upload_svodka_ca(
file: UploadFile = File(..., description="Excel файл сводки СА (.xlsx, .xlsm, .xls)")
):
"""Асинхронная загрузка и обработка отчета сводки СА"""
async_service = get_async_report_service()
try:
# Проверяем тип файла
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
# Читаем содержимое файла
file_content = await file.read()
# Создаем запрос
request = UploadRequest(
report_type='svodka_ca',
file_content=file_content,
file_name=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_500_INTERNAL_SERVER_ERROR,
content=UploadErrorResponse(
message=result.message,
error_code="ERR_UPLOAD"
).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_SERVER_ERROR"
).model_dump()
)

View File

@@ -9,6 +9,7 @@ from adapters.storage import MinIOStorageAdapter
from adapters.parsers import SvodkaPMParser from adapters.parsers import SvodkaPMParser
from core.models import UploadRequest, DataRequest from core.models import UploadRequest, DataRequest
from core.services import ReportService from core.services import ReportService
from core.async_services import AsyncReportService
from app.schemas import ( from app.schemas import (
UploadResponse, UploadErrorResponse, UploadResponse, UploadErrorResponse,
SvodkaPMTotalOGsRequest, SvodkaPMSingleOGRequest SvodkaPMTotalOGsRequest, SvodkaPMSingleOGRequest
@@ -26,6 +27,14 @@ def get_report_service() -> ReportService:
return ReportService(storage_adapter) 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], @router.post("/svodka_pm/upload-zip", tags=[SvodkaPMParser.name],
summary="Загрузка файлов сводок ПМ одним ZIP-архивом", summary="Загрузка файлов сводок ПМ одним ZIP-архивом",
response_model=UploadResponse, response_model=UploadResponse,
@@ -248,3 +257,63 @@ async def get_svodka_pm_data(
raise raise
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(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()
)

View File

@@ -0,0 +1,189 @@
"""
Эндпоинты для сводки ремонта СА
"""
import logging
from fastapi import APIRouter, File, UploadFile, HTTPException, status
from fastapi.responses import JSONResponse
from adapters.storage import MinIOStorageAdapter
from adapters.parsers import SvodkaRepairCAParser
from core.models import UploadRequest, DataRequest
from core.services import ReportService
from core.async_services import AsyncReportService
from app.schemas import UploadResponse, UploadErrorResponse
from app.schemas.svodka_repair_ca import SvodkaRepairCARequest
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_repair_ca/upload", tags=[SvodkaRepairCAParser.name],
summary="Загрузка файла отчета сводки ремонта СА",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def upload_svodka_repair_ca(
file: UploadFile = File(..., description="Excel файл сводки ремонта СА (.xlsx, .xlsm, .xls)")
):
"""Загрузка и обработка отчета сводки ремонта СА"""
report_service = get_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='svodka_repair_ca',
file_content=file_content,
file_name=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_repair_ca/get_data", tags=[SvodkaRepairCAParser.name],
summary="Получение данных из отчета сводки ремонта СА")
async def get_svodka_repair_ca_data(
request_data: SvodkaRepairCARequest
):
"""Получение данных из отчета сводки ремонта СА"""
report_service = get_report_service()
try:
request_dict = request_data.model_dump()
request = DataRequest(
report_type='svodka_repair_ca',
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("/async/svodka_repair_ca/upload", tags=[SvodkaRepairCAParser.name],
summary="Асинхронная загрузка файла отчета сводки ремонта СА",
response_model=UploadResponse,
responses={
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
},)
async def async_upload_svodka_repair_ca(
file: UploadFile = File(..., description="Excel файл сводки ремонта СА (.xlsx, .xlsm, .xls)")
):
"""Асинхронная загрузка и обработка отчета сводки ремонта СА"""
async_service = get_async_report_service()
try:
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=UploadErrorResponse(
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
error_code="INVALID_FILE_TYPE",
details={
"expected_formats": [".xlsx", ".xlsm", ".xls"],
"received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
}
).model_dump()
)
file_content = await file.read()
request = UploadRequest(
report_type='svodka_repair_ca',
file_content=file_content,
file_name=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()
)

View File

@@ -42,15 +42,21 @@ app = FastAPI(
) )
# Подключаем роутеры # Подключаем роутеры
from app.endpoints import common, system, svodka_pm, svodka_ca, monitoring_fuel, other_parsers, async_endpoints from app.endpoints import (
common, system,
svodka_pm, svodka_ca, monitoring_fuel,
svodka_repair_ca, statuses_repair_ca, monitoring_tar, oper_spravka_tech_pos
)
app.include_router(common.router) app.include_router(common.router)
app.include_router(system.router) app.include_router(system.router)
app.include_router(svodka_pm.router) app.include_router(svodka_pm.router)
app.include_router(svodka_ca.router) app.include_router(svodka_ca.router)
app.include_router(monitoring_fuel.router) app.include_router(monitoring_fuel.router)
app.include_router(other_parsers.router) app.include_router(svodka_repair_ca.router)
app.include_router(async_endpoints.router) app.include_router(statuses_repair_ca.router)
app.include_router(monitoring_tar.router)
app.include_router(oper_spravka_tech_pos.router)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -17,7 +17,13 @@ def render_sidebar():
st.subheader("Сервер") st.subheader("Сервер")
st.write(f"PID: {server_info.get('process_id', 'N/A')}") st.write(f"PID: {server_info.get('process_id', 'N/A')}")
st.write(f"CPU ядер: {server_info.get('cpu_cores', 'N/A')}") st.write(f"CPU ядер: {server_info.get('cpu_cores', 'N/A')}")
st.write(f"Память: {server_info.get('memory_mb', 'N/A'):.1f} MB")
# Безопасное форматирование памяти
memory_mb = server_info.get('memory_mb')
if memory_mb is not None:
st.write(f"Память: {memory_mb:.1f} MB")
else:
st.write("Память: N/A")
# Доступные парсеры # Доступные парсеры
parsers = get_available_parsers() parsers = get_available_parsers()