Удалил старые файлы
This commit is contained in:
@@ -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()
|
|
||||||
)
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
"""
|
|
||||||
Главный файл FastAPI приложения
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import multiprocessing
|
|
||||||
import uvicorn
|
|
||||||
import logging
|
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
# Настройка логирования
|
|
||||||
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__)
|
|
||||||
|
|
||||||
# Импортируем парсеры и обновляем PARSERS
|
|
||||||
from adapters.parsers import SvodkaPMParser, SvodkaCAParser, MonitoringFuelParser, MonitoringTarParser, SvodkaRepairCAParser, StatusesRepairCAParser, OperSpravkaTechPosParser
|
|
||||||
from core.services import PARSERS
|
|
||||||
|
|
||||||
# Обновляем словарь парсеров
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
|
|
||||||
# Создаем FastAPI приложение
|
|
||||||
app = FastAPI(
|
|
||||||
title="Svodka Parser API",
|
|
||||||
description="API для парсинга различных типов отчетов",
|
|
||||||
version="1.0.0",
|
|
||||||
docs_url="/docs",
|
|
||||||
redoc_url="/redoc"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Подключаем роутеры
|
|
||||||
from app.endpoints import common, system, svodka_pm, svodka_ca, monitoring_fuel, other_parsers, async_endpoints
|
|
||||||
|
|
||||||
app.include_router(common.router)
|
|
||||||
app.include_router(system.router)
|
|
||||||
app.include_router(svodka_pm.router)
|
|
||||||
app.include_router(svodka_ca.router)
|
|
||||||
app.include_router(monitoring_fuel.router)
|
|
||||||
app.include_router(other_parsers.router)
|
|
||||||
app.include_router(async_endpoints.router)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# Настройка для запуска в продакшене
|
|
||||||
workers = multiprocessing.cpu_count()
|
|
||||||
uvicorn.run(
|
|
||||||
"app.main:app",
|
|
||||||
host="0.0.0.0",
|
|
||||||
port=8000,
|
|
||||||
workers=workers,
|
|
||||||
reload=False
|
|
||||||
)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,100 +0,0 @@
|
|||||||
import streamlit as st
|
|
||||||
import pandas as pd
|
|
||||||
import numpy as np
|
|
||||||
import plotly.express as px
|
|
||||||
import plotly.graph_objects as go
|
|
||||||
from minio import Minio
|
|
||||||
import os
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
# Конфигурация страницы
|
|
||||||
st.set_page_config(
|
|
||||||
page_title="Сводка данных",
|
|
||||||
page_icon="📊",
|
|
||||||
layout="wide",
|
|
||||||
initial_sidebar_state="expanded"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Заголовок приложения
|
|
||||||
st.title("📊 Анализ данных сводки")
|
|
||||||
st.markdown("---")
|
|
||||||
|
|
||||||
# Инициализация MinIO клиента
|
|
||||||
@st.cache_resource
|
|
||||||
def init_minio_client():
|
|
||||||
try:
|
|
||||||
client = Minio(
|
|
||||||
os.getenv("MINIO_ENDPOINT", "localhost:9000"),
|
|
||||||
access_key=os.getenv("MINIO_ACCESS_KEY", "minioadmin"),
|
|
||||||
secret_key=os.getenv("MINIO_SECRET_KEY", "minioadmin"),
|
|
||||||
secure=os.getenv("MINIO_SECURE", "false").lower() == "true"
|
|
||||||
)
|
|
||||||
return client
|
|
||||||
except Exception as e:
|
|
||||||
st.error(f"Ошибка подключения к MinIO: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Боковая панель
|
|
||||||
with st.sidebar:
|
|
||||||
st.header("⚙️ Настройки")
|
|
||||||
|
|
||||||
# Выбор типа данных
|
|
||||||
data_type = st.selectbox(
|
|
||||||
"Тип данных",
|
|
||||||
["Мониторинг топлива", "Сводка ПМ", "Сводка ЦА"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Выбор периода
|
|
||||||
period = st.date_input(
|
|
||||||
"Период",
|
|
||||||
value=pd.Timestamp.now().date()
|
|
||||||
)
|
|
||||||
|
|
||||||
st.markdown("---")
|
|
||||||
st.markdown("### 📈 Статистика")
|
|
||||||
st.info("Выберите тип данных для анализа")
|
|
||||||
|
|
||||||
# Основной контент
|
|
||||||
col1, col2 = st.columns([2, 1])
|
|
||||||
|
|
||||||
with col1:
|
|
||||||
st.subheader(f"📋 {data_type}")
|
|
||||||
|
|
||||||
if data_type == "Мониторинг топлива":
|
|
||||||
st.info("Анализ данных мониторинга топлива")
|
|
||||||
# Здесь будет логика для работы с данными мониторинга топлива
|
|
||||||
|
|
||||||
elif data_type == "Сводка ПМ":
|
|
||||||
st.info("Анализ данных сводки ПМ")
|
|
||||||
# Здесь будет логика для работы с данными сводки ПМ
|
|
||||||
|
|
||||||
elif data_type == "Сводка ЦА":
|
|
||||||
st.info("Анализ данных сводки ЦА")
|
|
||||||
# Здесь будет логика для работы с данными сводки ЦА
|
|
||||||
|
|
||||||
with col2:
|
|
||||||
st.subheader("📊 Быстрая статистика")
|
|
||||||
st.metric("Всего записей", "0")
|
|
||||||
st.metric("Активных", "0")
|
|
||||||
st.metric("Ошибок", "0")
|
|
||||||
|
|
||||||
# Нижняя панель
|
|
||||||
st.markdown("---")
|
|
||||||
st.subheader("🔍 Детальный анализ")
|
|
||||||
|
|
||||||
# Заглушка для графиков
|
|
||||||
placeholder = st.empty()
|
|
||||||
with placeholder.container():
|
|
||||||
col1, col2 = st.columns(2)
|
|
||||||
|
|
||||||
with col1:
|
|
||||||
st.write("📈 График 1")
|
|
||||||
# Здесь будет график
|
|
||||||
|
|
||||||
with col2:
|
|
||||||
st.write("📊 График 2")
|
|
||||||
# Здесь будет график
|
|
||||||
|
|
||||||
# Футер
|
|
||||||
st.markdown("---")
|
|
||||||
st.markdown("**Разработано для анализа данных сводки** | v1.0.0")
|
|
||||||
Reference in New Issue
Block a user