1448 lines
54 KiB
Python
1448 lines
54 KiB
Python
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'
|
||
)
|
||
|
||
# Настройка логгера для модуля
|
||
logger = logging.getLogger(__name__)
|
||
|
||
from adapters.storage import MinIOStorageAdapter
|
||
from adapters.parsers import SvodkaPMParser, SvodkaCAParser, MonitoringFuelParser, MonitoringTarParser, SvodkaRepairCAParser, StatusesRepairCAParser, OperSpravkaTechPosParser
|
||
|
||
from core.models import UploadRequest, DataRequest
|
||
from core.services import ReportService, PARSERS
|
||
|
||
from app.schemas import (
|
||
ServerInfoResponse,
|
||
UploadResponse, UploadErrorResponse,
|
||
SvodkaPMTotalOGsRequest, SvodkaPMSingleOGRequest,
|
||
SvodkaCARequest,
|
||
MonitoringFuelMonthRequest, MonitoringFuelTotalRequest
|
||
)
|
||
from app.schemas.oper_spravka_tech_pos import OperSpravkaTechPosRequest, OperSpravkaTechPosResponse
|
||
from app.schemas.svodka_repair_ca import SvodkaRepairCARequest
|
||
from app.schemas.statuses_repair_ca import StatusesRepairCARequest
|
||
from app.schemas.monitoring_tar import MonitoringTarRequest, MonitoringTarFullRequest
|
||
|
||
|
||
# Парсеры
|
||
PARSERS.update({
|
||
'svodka_pm': SvodkaPMParser,
|
||
'svodka_ca': SvodkaCAParser,
|
||
'monitoring_fuel': MonitoringFuelParser,
|
||
'monitoring_tar': MonitoringTarParser,
|
||
'svodka_repair_ca': SvodkaRepairCAParser,
|
||
'statuses_repair_ca': StatusesRepairCAParser,
|
||
'oper_spravka_tech_pos': OperSpravkaTechPosParser,
|
||
# 'svodka_plan_sarnpz': SvodkaPlanSarnpzParser,
|
||
})
|
||
|
||
# Адаптеры
|
||
storage_adapter = MinIOStorageAdapter()
|
||
|
||
|
||
def get_report_service() -> ReportService:
|
||
return ReportService(storage_adapter)
|
||
|
||
|
||
tags_metadata = [
|
||
{
|
||
"name": "Общее",
|
||
"display_name": "Общее",
|
||
},
|
||
{
|
||
"name": SvodkaPMParser.name,
|
||
"description": "✅ Ready",
|
||
},
|
||
{
|
||
"name": SvodkaCAParser.name,
|
||
"description": "✅ Ready",
|
||
"display_name": "Сводка ПМ",
|
||
},
|
||
{
|
||
"name": MonitoringFuelParser.name,
|
||
"description": "✅ Ready",
|
||
"display_name": "Мониторинг топлива",
|
||
},
|
||
# {
|
||
# "name": MonitoringFuelParser.name,
|
||
# "description": "⚠️ WORK IN PROGRESS",
|
||
# },
|
||
|
||
]
|
||
|
||
app = FastAPI(
|
||
title="NIN Excel Parsers API",
|
||
description="API для парсинга сводок и работы с данными экселей НиН",
|
||
version="1.0.0",
|
||
openapi_tags=tags_metadata,
|
||
)
|
||
|
||
|
||
@app.get("/", tags=["Общее"])
|
||
async def root():
|
||
return {"message": "Svodka Parser API", "version": "1.0.0"}
|
||
|
||
|
||
@app.get("/parsers", tags=["Общее"],
|
||
summary="Список доступных парсеров",
|
||
description="Возвращает список идентификаторов всех доступных парсеров",
|
||
response_model=Dict[str, List[str]],
|
||
responses={
|
||
200: {
|
||
"content": {
|
||
"application/json": {
|
||
"example": {
|
||
"parsers": ["monitoring_fuel", "svodka_ca", "svodka_pm"]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},)
|
||
async def get_available_parsers():
|
||
"""Получение списка доступных парсеров"""
|
||
parsers = list(PARSERS.keys())
|
||
return {"parsers": parsers}
|
||
|
||
|
||
@app.get("/parsers/{parser_name}/available_ogs", tags=["Общее"],
|
||
summary="Доступные ОГ для парсера",
|
||
description="Возвращает список доступных ОГ для указанного парсера",
|
||
responses={
|
||
200: {
|
||
"content": {
|
||
"application/json": {
|
||
"example": {
|
||
"parser": "svodka_repair_ca",
|
||
"available_ogs": ["KNPZ", "ANHK", "SNPZ", "BASH"]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},)
|
||
async def get_available_ogs(parser_name: str):
|
||
"""Получение списка доступных ОГ для парсера"""
|
||
if parser_name not in PARSERS:
|
||
raise HTTPException(status_code=404, detail=f"Парсер '{parser_name}' не найден")
|
||
|
||
parser_class = PARSERS[parser_name]
|
||
|
||
# Для парсеров с данными в MinIO возвращаем ОГ из загруженных данных
|
||
if parser_name in ["svodka_repair_ca", "oper_spravka_tech_pos"]:
|
||
try:
|
||
# Создаем экземпляр сервиса и загружаем данные из MinIO
|
||
report_service = get_report_service()
|
||
from core.models import DataRequest
|
||
data_request = DataRequest(report_type=parser_name, get_params={})
|
||
loaded_data = report_service.get_data(data_request)
|
||
# Если данные загружены, извлекаем ОГ из них
|
||
if loaded_data is not None and hasattr(loaded_data, 'data') and loaded_data.data is not None:
|
||
# Для svodka_repair_ca данные возвращаются в формате словаря по ОГ
|
||
if parser_name == "svodka_repair_ca":
|
||
data_value = loaded_data.data.get('value')
|
||
if isinstance(data_value, dict):
|
||
available_ogs = list(data_value.keys())
|
||
return {"parser": parser_name, "available_ogs": available_ogs}
|
||
# Для oper_spravka_tech_pos данные возвращаются в формате списка
|
||
elif parser_name == "oper_spravka_tech_pos":
|
||
# Данные уже в правильном формате, возвращаем их
|
||
if isinstance(loaded_data.data, list) and loaded_data.data:
|
||
# Извлекаем уникальные ОГ из данных
|
||
available_ogs = []
|
||
for item in loaded_data.data:
|
||
if isinstance(item, dict) and 'id' in item:
|
||
available_ogs.append(item['id'])
|
||
if available_ogs:
|
||
return {"parser": parser_name, "available_ogs": available_ogs}
|
||
except Exception as e:
|
||
logger.error(f"⚠️ Ошибка при получении ОГ: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
# Для других парсеров или если нет данных возвращаем статический список из pconfig
|
||
from adapters.pconfig import SINGLE_OGS
|
||
return {"parser": parser_name, "available_ogs": SINGLE_OGS}
|
||
|
||
|
||
@app.get("/parsers/{parser_name}/getters", tags=["Общее"],
|
||
summary="Информация о геттерах парсера",
|
||
description="Возвращает информацию о доступных геттерах для указанного парсера",
|
||
responses={
|
||
200: {
|
||
"content": {
|
||
"application/json": {
|
||
"example": {
|
||
"parser": "svodka_pm",
|
||
"getters": {
|
||
"single_og": {
|
||
"required_params": ["id", "codes", "columns"],
|
||
"optional_params": ["search"],
|
||
"description": "Получение данных по одному ОГ"
|
||
},
|
||
"total_ogs": {
|
||
"required_params": ["codes", "columns"],
|
||
"optional_params": ["search"],
|
||
"description": "Получение данных по всем ОГ"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
404: {
|
||
"description": "Парсер не найден"
|
||
}
|
||
})
|
||
async def get_parser_getters(parser_name: str):
|
||
"""Получение информации о геттерах парсера"""
|
||
if parser_name not in PARSERS:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_404_NOT_FOUND,
|
||
detail=f"Парсер '{parser_name}' не найден"
|
||
)
|
||
|
||
parser_class = PARSERS[parser_name]
|
||
parser_instance = parser_class()
|
||
|
||
getters_info = parser_instance.get_available_getters()
|
||
|
||
return {
|
||
"parser": parser_name,
|
||
"getters": getters_info
|
||
}
|
||
|
||
|
||
@app.get("/server-info", tags=["Общее"],
|
||
summary="Информация о сервере",
|
||
response_model=ServerInfoResponse,)
|
||
async def server_info():
|
||
return {
|
||
"process_id": os.getpid(),
|
||
"parent_id": os.getppid(),
|
||
"cpu_cores": multiprocessing.cpu_count(),
|
||
"memory_mb": os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') / (1024. ** 2)
|
||
}
|
||
|
||
|
||
# @app.get("/svodka_pm/schema", tags=[SvodkaPMParser.name])
|
||
# async def get_svodka_pm_schema():
|
||
# """Получение схемы параметров для парсера сводок ПМ факта и плана"""
|
||
# parser = PARSERS['svodka_pm']()
|
||
# return parser.get_schema()
|
||
|
||
|
||
# @app.get("/svodka_ca/schema", tags=[SvodkaCAParser.name])
|
||
# async def get_svodka_ca_schema():
|
||
# """Получение схемы параметров для парсера сводки СА"""
|
||
# parser = PARSERS['svodka_ca']()
|
||
# return parser.get_schema()
|
||
|
||
|
||
# @app.get("/monitoring_fuel/schema", tags=[MonitoringFuelParser.name])
|
||
# async def get_monitoring_fuel_schema():
|
||
# """Получение схемы параметров для парсера мониторинга топлива"""
|
||
# parser = PARSERS['monitoring_fuel']()
|
||
# return parser.get_schema()
|
||
|
||
|
||
@app.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-архиве**
|
||
|
||
**Шаблоны названий файлов:**
|
||
- Факт: `svodka_fact_pm_<OG_ID>.xlsm`
|
||
- План: `svodka_plan_pm_<OG_ID>.xlsx`
|
||
"""
|
||
report_service = get_report_service()
|
||
try:
|
||
if not zip_file.filename.lower().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()
|
||
)
|
||
|
||
|
||
# @app.post("/svodka_pm/upload", tags=[SvodkaPMParser.name])
|
||
# async def upload_svodka_pm(
|
||
# file: UploadFile = File(...)
|
||
# ):
|
||
# report_service = get_report_service()
|
||
# """
|
||
# Загрузка отчета сводки факта СарНПЗ
|
||
|
||
# - file: Excel файл для загрузки
|
||
# """
|
||
# try:
|
||
# # Проверяем тип файла
|
||
# if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
|
||
# raise HTTPException(
|
||
# status_code=status.HTTP_400_BAD_REQUEST,
|
||
# detail="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)"
|
||
# )
|
||
|
||
# # Читаем содержимое файла
|
||
# file_content = await file.read()
|
||
|
||
# # Создаем запрос
|
||
# request = UploadRequest(
|
||
# report_type='svodka_pm',
|
||
# file_content=file_content,
|
||
# file_name=file.filename
|
||
# )
|
||
|
||
# # Загружаем отчет
|
||
# result = report_service.upload_report(request)
|
||
# # print(result)
|
||
# if result.success:
|
||
# return {
|
||
# "success": True,
|
||
# "message": result.message,
|
||
# "object_id": result.object_id
|
||
# }
|
||
# else:
|
||
# raise HTTPException(status_code=500, detail=result.message)
|
||
|
||
# except HTTPException:
|
||
# raise
|
||
# except Exception as e:
|
||
# raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@app.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()
|
||
"""
|
||
Получение данных из отчета сводки факта СарНПЗ
|
||
|
||
- id: ID ОГ
|
||
- codes: коды выбираемых строк [78, 79]
|
||
- columns: выбираемые колонки ["БП", "СЭБ"]
|
||
- search: "Итого" не обязательный
|
||
"""
|
||
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)}")
|
||
|
||
|
||
@app.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, 394, 395, 396, 397, 81, 82, 83, 84],
|
||
"columns": ["БП", "ПП", "СЭБ"]
|
||
}
|
||
```
|
||
"""
|
||
report_service = get_report_service()
|
||
"""
|
||
Получение данных из отчета сводки факта СарНПЗ
|
||
|
||
- codes: коды выбираемых строк [78, 79]
|
||
- columns: выбираемые колонки ["БП", "СЭБ"]
|
||
- search: "Итого"
|
||
"""
|
||
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)}")
|
||
|
||
|
||
@app.post("/svodka_pm/get_data", tags=[SvodkaPMParser.name])
|
||
async def get_svodka_pm_data(
|
||
request_data: dict
|
||
):
|
||
report_service = get_report_service()
|
||
"""
|
||
Получение данных из отчета сводки факта СарНПЗ
|
||
|
||
- indicator_id: ID индикатора
|
||
- code: Код для поиска
|
||
- search_value: Опциональное значение для поиска
|
||
"""
|
||
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)}")
|
||
|
||
|
||
@app.post("/svodka_ca/upload", tags=[SvodkaCAParser.name],
|
||
summary="Загрузка файла отчета сводки СА",
|
||
response_model=UploadResponse,
|
||
responses={
|
||
400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
|
||
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
|
||
},)
|
||
async def upload_svodka_ca(
|
||
file: UploadFile = File(..., description="Excel файл сводки СА (.xlsx, .xlsm, .xls)")
|
||
):
|
||
"""
|
||
Загрузка и обработка Excel файла отчета сводки СА
|
||
|
||
**Поддерживаемые форматы:**
|
||
- 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_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()
|
||
)
|
||
|
||
|
||
@app.post("/svodka_ca/get_data", tags=[SvodkaCAParser.name],
|
||
summary="Получение данных из отчета сводки СА")
|
||
async def get_svodka_ca_data(
|
||
request_data: SvodkaCARequest
|
||
):
|
||
"""
|
||
Получение данных из отчета сводки СА по указанным режимам и таблицам
|
||
|
||
### Структура параметров:
|
||
- `modes`: **Массив кодов** режимов - `plan`, `fact` или `normativ` (обязательный)
|
||
- `tables`: **Массив названий** таблиц как есть (обязательный)
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"modes": ["plan", "fact"],
|
||
"tables": ["ТиП, %", "Топливо итого, тонн", "Топливо итого, %", "Потери итого, тонн"]
|
||
}
|
||
```
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Создаем запрос
|
||
request_dict = request_data.model_dump()
|
||
request = DataRequest(
|
||
report_type='svodka_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)}")
|
||
|
||
|
||
@app.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 файл или ZIP архив сводки ремонта СА (.xlsx, .xlsm, .xls, .zip)")
|
||
):
|
||
"""
|
||
Загрузка и обработка Excel файла или ZIP архива отчета сводки ремонта СА
|
||
|
||
**Поддерживаемые форматы:**
|
||
- Excel (.xlsx, .xlsm, .xls)
|
||
- ZIP архив (.zip)
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Проверяем тип файла
|
||
if not file.filename.lower().endswith(('.xlsx', '.xlsm', '.xls', '.zip')):
|
||
return JSONResponse(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
content=UploadErrorResponse(
|
||
message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls) или ZIP архивы (.zip)",
|
||
error_code="INVALID_FILE_TYPE",
|
||
details={
|
||
"expected_formats": [".xlsx", ".xlsm", ".xls", ".zip"],
|
||
"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()
|
||
)
|
||
|
||
|
||
@app.post("/svodka_repair_ca/get_data", tags=[SvodkaRepairCAParser.name],
|
||
summary="Получение данных из отчета сводки ремонта СА")
|
||
async def get_svodka_repair_ca_data(
|
||
request_data: SvodkaRepairCARequest
|
||
):
|
||
"""
|
||
Получение данных из отчета сводки ремонта СА
|
||
|
||
### Структура параметров:
|
||
- `og_ids`: **Массив ID ОГ** для фильтрации (опциональный)
|
||
- `repair_types`: **Массив типов ремонта** - `КР`, `КП`, `ТР` (опциональный)
|
||
- `include_planned`: **Включать плановые данные** (по умолчанию true)
|
||
- `include_factual`: **Включать фактические данные** (по умолчанию true)
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"og_ids": ["SNPZ", "KNPZ"],
|
||
"repair_types": ["КР", "КП"],
|
||
"include_planned": true,
|
||
"include_factual": true
|
||
}
|
||
```
|
||
"""
|
||
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)}")
|
||
|
||
|
||
@app.post("/statuses_repair_ca/upload", tags=[StatusesRepairCAParser.name],
|
||
summary="Загрузка отчета статусов ремонта СА")
|
||
async def upload_statuses_repair_ca(
|
||
file: UploadFile = File(...)
|
||
):
|
||
"""
|
||
Загрузка отчета статусов ремонта СА
|
||
|
||
### Поддерживаемые форматы:
|
||
- **Excel файлы**: `.xlsx`, `.xlsm`, `.xls`
|
||
- **ZIP архивы**: `.zip` (содержащие Excel файлы)
|
||
|
||
### Пример использования:
|
||
```bash
|
||
curl -X POST "http://localhost:8000/statuses_repair_ca/upload" \
|
||
-H "accept: application/json" \
|
||
-H "Content-Type: multipart/form-data" \
|
||
-F "file=@statuses_repair_ca.xlsx"
|
||
```
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Проверяем тип файла
|
||
if not file.filename.endswith(('.xlsx', '.xlsm', '.xls', '.zip')):
|
||
raise HTTPException(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
detail="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls) или архивы (.zip)"
|
||
)
|
||
|
||
# Читаем содержимое файла
|
||
file_content = await file.read()
|
||
|
||
# Создаем запрос на загрузку
|
||
upload_request = UploadRequest(
|
||
report_type='statuses_repair_ca',
|
||
file_content=file_content,
|
||
file_name=file.filename
|
||
)
|
||
|
||
# Загружаем отчет
|
||
result = report_service.upload_report(upload_request)
|
||
|
||
if result.success:
|
||
return UploadResponse(
|
||
success=True,
|
||
message="Отчет успешно загружен и обработан",
|
||
report_id=result.object_id,
|
||
filename=file.filename
|
||
).model_dump()
|
||
else:
|
||
return UploadErrorResponse(
|
||
success=False,
|
||
message=result.message,
|
||
error_code="ERR_UPLOAD",
|
||
details=None
|
||
).model_dump()
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@app.post("/statuses_repair_ca/get_data", tags=[StatusesRepairCAParser.name],
|
||
summary="Получение данных из отчета статусов ремонта СА")
|
||
async def get_statuses_repair_ca_data(
|
||
request_data: StatusesRepairCARequest
|
||
):
|
||
"""
|
||
Получение данных из отчета статусов ремонта СА
|
||
|
||
### Структура параметров:
|
||
- `ids`: **Массив ID ОГ** для фильтрации (опциональный)
|
||
- `keys`: **Массив ключей** для извлечения данных (опциональный)
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"ids": ["SNPZ", "KNPZ", "ANHK"],
|
||
"keys": [
|
||
["Дата начала ремонта"],
|
||
["Готовность к КР", "Факт"],
|
||
["Заключение договоров на СМР", "Договор", "%"]
|
||
]
|
||
}
|
||
```
|
||
"""
|
||
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)}")
|
||
|
||
|
||
# @app.post("/monitoring_fuel/upload", tags=[MonitoringFuelParser.name])
|
||
# async def upload_monitoring_fuel(
|
||
# file: UploadFile = File(...),
|
||
# directory_path: str = None
|
||
# ):
|
||
# report_service = get_report_service()
|
||
# """
|
||
# Загрузка отчета мониторинга топлива
|
||
|
||
# - file: Excel файл для загрузки (или архив с файлами)
|
||
# - directory_path: Путь к директории с файлами (опционально)
|
||
# """
|
||
# try:
|
||
# # Проверяем тип файла
|
||
# if not file.filename.endswith(('.xlsx', '.xlsm', '.xls', '.zip')):
|
||
# raise HTTPException(
|
||
# status_code=status.HTTP_400_BAD_REQUEST,
|
||
# detail="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls) или архивы (.zip)"
|
||
# )
|
||
|
||
# # Читаем содержимое файла
|
||
# file_content = await file.read()
|
||
|
||
# # Создаем параметры для парсинга
|
||
# parse_params = {}
|
||
# if directory_path:
|
||
# parse_params['directory_path'] = directory_path
|
||
|
||
# # Создаем запрос
|
||
# request = UploadRequest(
|
||
# report_type='monitoring_fuel',
|
||
# file_content=file_content,
|
||
# file_name=file.filename,
|
||
# parse_params=parse_params
|
||
# )
|
||
|
||
# # Загружаем отчет
|
||
# result = report_service.upload_report(request)
|
||
|
||
# if result.success:
|
||
# return {
|
||
# "success": True,
|
||
# "message": result.message,
|
||
# "object_id": result.object_id
|
||
# }
|
||
# else:
|
||
# raise HTTPException(status_code=500, detail=result.message)
|
||
|
||
# except HTTPException:
|
||
# raise
|
||
# except Exception as e:
|
||
# raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@app.post("/monitoring_fuel/get_data", tags=[MonitoringFuelParser.name])
|
||
async def get_monitoring_fuel_data(
|
||
request_data: dict
|
||
):
|
||
report_service = get_report_service()
|
||
"""
|
||
Получение данных из отчета мониторинга топлива
|
||
|
||
- column: Название колонки для агрегации (normativ, total, total_svod)
|
||
"""
|
||
try:
|
||
# Создаем запрос
|
||
request = DataRequest(
|
||
report_type='monitoring_fuel',
|
||
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)}")
|
||
|
||
|
||
# @app.post("/monitoring_fuel/upload_directory", tags=[MonitoringFuelParser.name])
|
||
# async def upload_monitoring_fuel_directory(
|
||
# request_data: dict
|
||
# ):
|
||
# report_service = get_report_service()
|
||
# """
|
||
# Загрузка отчета мониторинга топлива из директории
|
||
|
||
# - directory_path: Путь к директории с файлами monitoring_SNPZ_*.xlsm
|
||
# """
|
||
# try:
|
||
# import os
|
||
# import glob
|
||
|
||
# # Извлекаем directory_path из request_data
|
||
# directory_path = request_data.get('directory_path')
|
||
# if not directory_path:
|
||
# raise HTTPException(
|
||
# status_code=status.HTTP_400_BAD_REQUEST,
|
||
# detail="Параметр 'directory_path' обязателен"
|
||
# )
|
||
|
||
# # Проверяем существование директории
|
||
# if not os.path.exists(directory_path):
|
||
# raise HTTPException(
|
||
# status_code=status.HTTP_400_BAD_REQUEST,
|
||
# detail=f"Директория не найдена: {directory_path}"
|
||
# )
|
||
|
||
# # Проверяем наличие файлов
|
||
# file_pattern = os.path.join(directory_path, "monitoring_SNPZ_*.xlsm")
|
||
# files = glob.glob(file_pattern)
|
||
|
||
# if not files:
|
||
# raise HTTPException(
|
||
# status_code=status.HTTP_400_BAD_REQUEST,
|
||
# detail=f"Не найдены файлы по паттерну {file_pattern}"
|
||
# )
|
||
|
||
# # Создаем параметры для парсинга
|
||
# parse_params = {
|
||
# 'directory_path': directory_path,
|
||
# 'sheet_name': 'Мониторинг потребления',
|
||
# 'search_value': 'Установка'
|
||
# }
|
||
|
||
# # Создаем запрос (используем пустой файл, так как парсим директорию)
|
||
# request = UploadRequest(
|
||
# report_type='monitoring_fuel',
|
||
# file_content=b'', # Пустой контент, так как парсим директорию
|
||
# file_name='directory_upload',
|
||
# parse_params=parse_params
|
||
# )
|
||
|
||
# # Загружаем отчет
|
||
# result = report_service.upload_report(request)
|
||
|
||
# if result.success:
|
||
# return {
|
||
# "success": True,
|
||
# "message": result.message,
|
||
# "object_id": result.object_id,
|
||
# "files_processed": len(files)
|
||
# }
|
||
# else:
|
||
# raise HTTPException(status_code=500, detail=result.message)
|
||
|
||
# except HTTPException:
|
||
# raise
|
||
# except Exception as e:
|
||
# raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@app.post("/monitoring_fuel/upload-zip", tags=[MonitoringFuelParser.name],
|
||
summary="Загрузка файлов сводок мониторинга топлива одним ZIP-архивом",
|
||
response_model=UploadResponse,
|
||
responses={
|
||
400: {"model": UploadErrorResponse, "description": "Неверный формат архива или файлов"},
|
||
500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
|
||
},)
|
||
async def upload_monitoring_fuel_zip(
|
||
zip_file: UploadFile = File(..., description="ZIP архив с Excel файлами (.zip)")
|
||
):
|
||
"""Загрузка файлов сводок мониторинга топлива по всем ОГ в **одном ZIP-архиве**
|
||
|
||
**Шаблоны названий файлов:**
|
||
- `monitoring_SNPZ_{MM}.xlsm`, `MM` - номер месяца с ведущим 0
|
||
"""
|
||
report_service = get_report_service()
|
||
try:
|
||
if not zip_file.filename.lower().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 = 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()
|
||
)
|
||
|
||
|
||
@app.post("/monitoring_fuel/get_total_by_columns", tags=[MonitoringFuelParser.name],
|
||
summary="Получение данных по колонкам и расчёт средних значений")
|
||
async def get_monitoring_fuel_total_by_columns(
|
||
request_data: MonitoringFuelTotalRequest
|
||
):
|
||
"""Получение данных из сводок мониторинга топлива по колонкам и расчёт средних значений
|
||
|
||
### Структура параметров:
|
||
- `columns`: **Массив названий** выбираемых столбцов (обязательный)
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"columns": ["total", "normativ"]
|
||
}
|
||
```
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Создаем запрос
|
||
request_dict = request_data.model_dump()
|
||
request_dict['mode'] = 'total_by_columns'
|
||
request = DataRequest(
|
||
report_type='monitoring_fuel',
|
||
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)}")
|
||
|
||
|
||
@app.post("/monitoring_fuel/get_month_by_code", tags=[MonitoringFuelParser.name],
|
||
summary="Получение данных за месяц")
|
||
async def get_monitoring_fuel_month_by_code(
|
||
request_data: MonitoringFuelMonthRequest
|
||
):
|
||
"""Получение данных из сводок мониторинга топлива за указанный номер месяца
|
||
|
||
### Структура параметров:
|
||
- `month`: **Номер месяца строкой с ведущим 0** (обязательный)
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"month": "02"
|
||
}
|
||
```
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Создаем запрос
|
||
request_dict = request_data.model_dump()
|
||
request_dict['mode'] = 'month_by_code'
|
||
request = DataRequest(
|
||
report_type='monitoring_fuel',
|
||
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)}")
|
||
|
||
|
||
# ====== MONITORING TAR ENDPOINTS ======
|
||
|
||
@app.post("/monitoring_tar/upload", tags=[MonitoringTarParser.name],
|
||
summary="Загрузка отчета мониторинга ТЭР")
|
||
async def upload_monitoring_tar(
|
||
file: UploadFile = File(...)
|
||
):
|
||
"""Загрузка и обработка отчета мониторинга ТЭР (Топливно-энергетических ресурсов)
|
||
|
||
### Поддерживаемые форматы:
|
||
- **ZIP архивы** с файлами мониторинга ТЭР
|
||
|
||
### Структура данных:
|
||
- Обрабатывает ZIP архивы с файлами по месяцам (svodka_tar_SNPZ_01.xlsx - svodka_tar_SNPZ_12.xlsx)
|
||
- Извлекает данные по установкам (SNPZ_IDS)
|
||
- Возвращает два типа данных: 'total' (строки "Всего") и 'last_day' (последние строки)
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Проверяем тип файла - только ZIP архивы
|
||
if not file.filename.endswith('.zip'):
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="Неподдерживаемый тип файла. Ожидается только ZIP архив (.zip)"
|
||
)
|
||
|
||
# Читаем содержимое файла
|
||
file_content = await file.read()
|
||
|
||
# Создаем запрос на загрузку
|
||
upload_request = UploadRequest(
|
||
report_type='monitoring_tar',
|
||
file_content=file_content,
|
||
file_name=file.filename
|
||
)
|
||
|
||
# Загружаем отчет
|
||
result = report_service.upload_report(upload_request)
|
||
|
||
if result.success:
|
||
return UploadResponse(
|
||
success=True,
|
||
message="Отчет успешно загружен и обработан",
|
||
report_id=result.object_id,
|
||
filename=file.filename
|
||
).model_dump()
|
||
else:
|
||
return UploadErrorResponse(
|
||
success=False,
|
||
message=result.message,
|
||
error_code="ERR_UPLOAD",
|
||
details=None
|
||
).model_dump()
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@app.post("/monitoring_tar/get_data", tags=[MonitoringTarParser.name],
|
||
summary="Получение данных из отчета мониторинга ТЭР")
|
||
async def get_monitoring_tar_data(
|
||
request_data: MonitoringTarRequest
|
||
):
|
||
"""Получение данных из отчета мониторинга ТЭР
|
||
|
||
### Структура параметров:
|
||
- `mode`: **Режим получения данных** (опциональный)
|
||
- `"total"` - строки "Всего" (агрегированные данные)
|
||
- `"last_day"` - последние строки данных
|
||
- Если не указан, возвращаются все данные
|
||
|
||
### Пример тела запроса:
|
||
```json
|
||
{
|
||
"mode": "total"
|
||
}
|
||
```
|
||
"""
|
||
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)}")
|
||
|
||
|
||
@app.post("/monitoring_tar/get_full_data", tags=[MonitoringTarParser.name],
|
||
summary="Получение всех данных из отчета мониторинга ТЭР")
|
||
async def get_monitoring_tar_full_data():
|
||
"""Получение всех данных из отчета мониторинга ТЭР без фильтрации
|
||
|
||
### Возвращает:
|
||
- Все данные по всем установкам
|
||
- И данные 'total', и данные 'last_day'
|
||
- Полная структура данных мониторинга ТЭР
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Создаем запрос без параметров
|
||
request = DataRequest(
|
||
report_type='monitoring_tar',
|
||
get_params={}
|
||
)
|
||
|
||
# Получаем данные
|
||
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)}")
|
||
|
||
|
||
# ====== OPER SPRAVKA TECH POS ENDPOINTS ======
|
||
|
||
@app.post("/oper_spravka_tech_pos/upload", tags=[OperSpravkaTechPosParser.name],
|
||
summary="Загрузка отчета операционной справки технологических позиций")
|
||
async def upload_oper_spravka_tech_pos(
|
||
file: UploadFile = File(...)
|
||
):
|
||
"""Загрузка и обработка отчета операционной справки технологических позиций
|
||
|
||
### Поддерживаемые форматы:
|
||
- **ZIP архивы** с файлами операционных справок
|
||
|
||
### Структура данных:
|
||
- Обрабатывает ZIP архивы с файлами операционных справок по технологическим позициям
|
||
- Извлекает данные по процессам: Первичная переработка, Гидроочистка топлив, Риформирование, Изомеризация
|
||
- Возвращает данные по установкам с планом и фактом
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Проверяем тип файла - только ZIP архивы
|
||
if not file.filename.endswith('.zip'):
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="Неподдерживаемый тип файла. Ожидается только ZIP архив (.zip)"
|
||
)
|
||
|
||
# Читаем содержимое файла
|
||
file_content = await file.read()
|
||
|
||
# Создаем запрос на загрузку
|
||
upload_request = UploadRequest(
|
||
report_type="oper_spravka_tech_pos",
|
||
file_name=file.filename,
|
||
file_content=file_content,
|
||
parse_params={}
|
||
)
|
||
|
||
# Загружаем и обрабатываем отчет
|
||
result = report_service.upload_report(upload_request)
|
||
|
||
if result.success:
|
||
return UploadResponse(
|
||
success=True,
|
||
message="Отчет успешно загружен и обработан",
|
||
object_id=result.object_id
|
||
)
|
||
else:
|
||
return UploadErrorResponse(
|
||
success=False,
|
||
message=result.message,
|
||
error_code="ERR_UPLOAD",
|
||
details=None
|
||
)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
@app.post("/oper_spravka_tech_pos/get_data", tags=[OperSpravkaTechPosParser.name],
|
||
summary="Получение данных операционной справки технологических позиций",
|
||
response_model=OperSpravkaTechPosResponse)
|
||
async def get_oper_spravka_tech_pos_data(request: OperSpravkaTechPosRequest):
|
||
"""Получение данных операционной справки технологических позиций по ОГ
|
||
|
||
### Параметры:
|
||
- **id** (str): ID ОГ (например, 'SNPZ', 'KNPZ')
|
||
|
||
### Возвращает:
|
||
- Данные по технологическим позициям для указанного ОГ
|
||
- Включает информацию о процессах, установках, плане и факте
|
||
"""
|
||
report_service = get_report_service()
|
||
|
||
try:
|
||
# Создаем запрос на получение данных
|
||
data_request = DataRequest(
|
||
report_type="oper_spravka_tech_pos",
|
||
get_params={"id": request.id}
|
||
)
|
||
|
||
# Получаем данные
|
||
result = report_service.get_data(data_request)
|
||
|
||
if result.success:
|
||
# Извлекаем данные из результата
|
||
value_data = result.data.get("value", []) if isinstance(result.data.get("value"), list) else []
|
||
logger.debug(f"🔍 API возвращает данные: {type(value_data)}, длина: {len(value_data) if isinstance(value_data, (list, dict)) else 'N/A'}")
|
||
|
||
return OperSpravkaTechPosResponse(
|
||
success=True,
|
||
data=value_data,
|
||
message="Данные успешно получены"
|
||
)
|
||
else:
|
||
return OperSpravkaTechPosResponse(
|
||
success=False,
|
||
data=None,
|
||
message=result.message
|
||
)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
uvicorn.run(app, host="0.0.0.0", port=8080)
|