Работает все, кроме отображения задач

This commit is contained in:
2025-09-04 22:42:31 +03:00
parent 6a1f685ee3
commit 36f37ffacb
7 changed files with 681 additions and 39 deletions

View File

@@ -0,0 +1,146 @@
"""
Страница асинхронной загрузки файлов
"""
import streamlit as st
import asyncio
import threading
import time
from api_client import upload_file_to_api
from config import PARSER_TABS
def upload_file_async_background(endpoint, file_data, filename, task_id):
"""Асинхронная загрузка файла в фоновом режиме"""
try:
# Имитируем асинхронную работу
time.sleep(1) # Небольшая задержка для демонстрации
# Выполняем загрузку
result, status = upload_file_to_api(endpoint, file_data, filename)
# Сохраняем результат в session_state
if 'upload_tasks' not in st.session_state:
st.session_state.upload_tasks = {}
st.session_state.upload_tasks[task_id] = {
'status': 'completed' if status == 200 else 'failed',
'result': result,
'status_code': status,
'filename': filename,
'endpoint': endpoint,
'completed_at': time.time()
}
except Exception as e:
# Сохраняем ошибку
if 'upload_tasks' not in st.session_state:
st.session_state.upload_tasks = {}
st.session_state.upload_tasks[task_id] = {
'status': 'failed',
'error': str(e),
'filename': filename,
'endpoint': endpoint,
'completed_at': time.time()
}
def render_async_upload_page():
"""Рендер страницы асинхронной загрузки"""
st.title("🚀 Асинхронная загрузка файлов")
st.markdown("---")
st.info("""
**Асинхронная загрузка** позволяет загружать файлы без блокировки интерфейса.
После загрузки файл будет обработан в фоновом режиме, а вы сможете отслеживать прогресс на странице "Управление задачами".
""")
# Выбор парсера
st.subheader("📋 Выбор парсера")
# Создаем словарь парсеров с их асинхронными эндпоинтами
parser_endpoints = {
"Сводки ПМ": "/async/svodka_pm/upload-zip",
"Сводки СА": "/async/svodka_ca/upload",
"Мониторинг топлива": "/async/monitoring_fuel/upload-zip",
"Ремонт СА": "/svodka_repair_ca/upload", # Пока синхронный
"Статусы ремонта СА": "/statuses_repair_ca/upload", # Пока синхронный
"Мониторинг ТЭР": "/monitoring_tar/upload", # Пока синхронный
"Операционные справки": "/oper_spravka_tech_pos/upload" # Пока синхронный
}
selected_parser = st.selectbox(
"Выберите тип парсера для загрузки:",
list(parser_endpoints.keys()),
key="async_parser_select"
)
st.markdown("---")
# Загрузка файла
st.subheader("📤 Загрузка файла")
uploaded_file = st.file_uploader(
f"Выберите ZIP архив для парсера '{selected_parser}'",
type=['zip'],
key="async_file_upload"
)
if uploaded_file is not None:
st.success(f"✅ Файл выбран: {uploaded_file.name}")
st.info(f"📊 Размер файла: {uploaded_file.size / 1024 / 1024:.2f} MB")
if st.button("🚀 Загрузить асинхронно", key="async_upload_btn", use_container_width=True):
# Создаем уникальный ID задачи
task_id = f"task_{int(time.time())}_{uploaded_file.name}"
# Показываем сообщение о создании задачи
st.success("✅ Задача загрузки создана!")
st.info(f"ID задачи: `{task_id}`")
st.info("📋 Перейдите на страницу 'Управление задачами' для отслеживания прогресса")
# Запускаем загрузку в фоновом потоке
endpoint = parser_endpoints[selected_parser]
file_data = uploaded_file.read()
# Создаем поток для асинхронной загрузки
thread = threading.Thread(
target=upload_file_async_background,
args=(endpoint, file_data, uploaded_file.name, task_id)
)
thread.daemon = True
thread.start()
# Автоматически переключаемся на страницу задач
st.session_state.sidebar_tasks_clicked = True
st.rerun()
st.markdown("---")
# Информация о поддерживаемых форматах
with st.expander(" Поддерживаемые форматы файлов"):
st.markdown("""
**Поддерживаемые форматы:**
- 📦 ZIP архивы с Excel файлами
- 📊 Excel файлы (.xlsx, .xls)
- 📋 CSV файлы (для некоторых парсеров)
**Ограничения:**
- Максимальный размер файла: 100 MB
- Количество файлов в архиве: до 50
- Поддерживаемые кодировки: UTF-8, Windows-1251
""")
# Статистика загрузок
st.subheader("📈 Статистика загрузок")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Всего загружено", "0", "0")
with col2:
st.metric("В обработке", "0", "0")
with col3:
st.metric("Завершено", "0", "0")

View File

@@ -9,7 +9,7 @@ from config import API_PUBLIC_URL
def render_sidebar():
"""Рендер боковой панели"""
with st.sidebar:
st.header(" Информация")
st.header(" Информация1")
# Информация о сервере
server_info = get_server_info()
@@ -25,6 +25,28 @@ def render_sidebar():
st.subheader("Доступные парсеры")
for parser in parsers:
st.write(f"{parser}")
# Навигация по страницам
st.markdown("---")
st.subheader("🧭 Навигация")
# Определяем активную страницу
active_page = st.session_state.get("active_page", 0)
# Кнопка для страницы синхронных парсеров
if st.button("📊 Синхронные парсеры", key="sidebar_sync_btn", use_container_width=True, type="primary" if active_page == 0 else "secondary"):
st.session_state.sidebar_sync_clicked = True
st.rerun()
# Кнопка для страницы асинхронной загрузки
if st.button("🚀 Асинхронная загрузка", key="sidebar_async_btn", use_container_width=True, type="primary" if active_page == 1 else "secondary"):
st.session_state.sidebar_async_clicked = True
st.rerun()
# Кнопка для страницы управления задачами
if st.button("📋 Управление задачами", key="sidebar_tasks_btn", use_container_width=True, type="primary" if active_page == 2 else "secondary"):
st.session_state.sidebar_tasks_clicked = True
st.rerun()
def render_footer():

View File

@@ -1,20 +1,24 @@
import streamlit as st
from config import setup_page_config, PARSER_TABS, API_PUBLIC_URL
from config import setup_page_config, API_PUBLIC_URL
from api_client import check_api_health
from sidebar import render_sidebar, render_footer
from parsers_ui.svodka_pm_ui import render_svodka_pm_tab
from parsers_ui.svodka_ca_ui import render_svodka_ca_tab
from parsers_ui.monitoring_fuel_ui import render_monitoring_fuel_tab
from parsers_ui.svodka_repair_ca_ui import render_svodka_repair_ca_tab
from parsers_ui.statuses_repair_ca_ui import render_statuses_repair_ca_tab
from parsers_ui.monitoring_tar_ui import render_monitoring_tar_tab
from parsers_ui.oper_spravka_tech_pos_ui import render_oper_spravka_tech_pos_tab
from sync_parsers_page import render_sync_parsers_page
from async_upload_page import render_async_upload_page
from tasks_page import render_tasks_page
# Конфигурация страницы
setup_page_config()
def main():
st.title("🚀 NIN Excel Parsers API - Демонстрация")
# Определяем активную страницу для заголовка
active_page = st.session_state.get("active_page", 0)
page_titles = {
0: "Синхронные парсеры",
1: "Асинхронная загрузка",
2: "Управление задачами"
}
st.title(f"🚀 NIN Excel Parsers API - {page_titles.get(active_page, 'Демонстрация')}")
st.markdown("---")
# Проверка доступности API
@@ -25,39 +29,30 @@ def main():
st.success(f"✅ API доступен по адресу {API_PUBLIC_URL}")
# Боковая панель с информацией
# Боковая панель с информацией и навигацией
render_sidebar()
# Основные вкладки - по одной на каждый парсер
tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs(PARSER_TABS)
# Обрабатываем клики по кнопкам в сайдбаре
if st.session_state.get("sidebar_sync_clicked", False):
st.session_state.sidebar_sync_clicked = False
st.session_state.active_page = 0
elif st.session_state.get("sidebar_async_clicked", False):
st.session_state.sidebar_async_clicked = False
st.session_state.active_page = 1
elif st.session_state.get("sidebar_tasks_clicked", False):
st.session_state.sidebar_tasks_clicked = False
st.session_state.active_page = 2
# Вкладка 1: Сводки ПМ - полный функционал
with tab1:
render_svodka_pm_tab()
# Определяем активную страницу
active_page = st.session_state.get("active_page", 0)
# Вкладка 2: Сводки СА - полный функционал
with tab2:
render_svodka_ca_tab()
# Вкладка 3: Мониторинг топлива - полный функционал
with tab3:
render_monitoring_fuel_tab()
# Вкладка 4: Ремонт СА
with tab4:
render_svodka_repair_ca_tab()
# Вкладка 5: Статусы ремонта СА
with tab5:
render_statuses_repair_ca_tab()
# Вкладка 6: Мониторинг ТЭР
with tab6:
render_monitoring_tar_tab()
# Вкладка 7: Операционные справки технологических позиций
with tab7:
render_oper_spravka_tech_pos_tab()
# Рендерим соответствующую страницу
if active_page == 0:
render_sync_parsers_page()
elif active_page == 1:
render_async_upload_page()
else:
render_tasks_page()
# Футер
render_footer()

View File

@@ -0,0 +1,54 @@
"""
Страница синхронных парсеров
"""
import streamlit as st
from parsers_ui.svodka_pm_ui import render_svodka_pm_tab
from parsers_ui.svodka_ca_ui import render_svodka_ca_tab
from parsers_ui.monitoring_fuel_ui import render_monitoring_fuel_tab
from parsers_ui.svodka_repair_ca_ui import render_svodka_repair_ca_tab
from parsers_ui.statuses_repair_ca_ui import render_statuses_repair_ca_tab
from parsers_ui.monitoring_tar_ui import render_monitoring_tar_tab
from parsers_ui.oper_spravka_tech_pos_ui import render_oper_spravka_tech_pos_tab
from config import PARSER_TABS
def render_sync_parsers_page():
"""Рендер страницы синхронных парсеров"""
st.title("📊 Синхронные парсеры")
st.markdown("---")
st.info("""
**Синхронные парсеры** обрабатывают файлы сразу после загрузки.
Интерфейс будет заблокирован до завершения обработки.
""")
# Основные вкладки - по одной на каждый парсер
tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs(PARSER_TABS)
# Вкладка 1: Сводки ПМ - полный функционал
with tab1:
render_svodka_pm_tab()
# Вкладка 2: Сводки СА - полный функционал
with tab2:
render_svodka_ca_tab()
# Вкладка 3: Мониторинг топлива - полный функционал
with tab3:
render_monitoring_fuel_tab()
# Вкладка 4: Ремонт СА
with tab4:
render_svodka_repair_ca_tab()
# Вкладка 5: Статусы ремонта СА
with tab5:
render_statuses_repair_ca_tab()
# Вкладка 6: Мониторинг ТЭР
with tab6:
render_monitoring_tar_tab()
# Вкладка 7: Операционные справки технологических позиций
with tab7:
render_oper_spravka_tech_pos_tab()

159
streamlit_app/tasks_page.py Normal file
View File

@@ -0,0 +1,159 @@
"""
Страница управления задачами загрузки
"""
import streamlit as st
from datetime import datetime
import time
def render_tasks_page():
"""Рендер страницы управления задачами"""
st.title("📋 Управление задачами загрузки")
st.markdown("---")
# Кнопки управления
col1, col2, col3, col4 = st.columns([1, 1, 1, 2])
with col1:
if st.button("🔄 Обновить", key="refresh_tasks_btn", use_container_width=True):
st.rerun()
with col2:
if st.button("🗑️ Очистить завершенные", key="clear_completed_btn", use_container_width=True):
st.info("Функция очистки будет добавлена в следующих версиях")
with col3:
auto_refresh = st.checkbox("🔄 Автообновление", key="auto_refresh_checkbox")
if auto_refresh:
time.sleep(2)
st.rerun()
with col4:
st.caption("Последнее обновление: " + datetime.now().strftime("%H:%M:%S"))
st.markdown("---")
# Статистика задач
st.subheader("📊 Статистика задач")
# Получаем задачи из session_state
tasks = st.session_state.get('upload_tasks', {})
# Подсчитываем статистику
total_tasks = len(tasks)
pending_tasks = len([t for t in tasks.values() if t.get('status') == 'pending'])
running_tasks = len([t for t in tasks.values() if t.get('status') == 'running'])
completed_tasks = len([t for t in tasks.values() if t.get('status') == 'completed'])
failed_tasks = len([t for t in tasks.values() if t.get('status') == 'failed'])
col1, col2, col3, col4, col5 = st.columns(5)
with col1:
st.metric("Всего", total_tasks, f"+{total_tasks}")
with col2:
st.metric("Ожидают", pending_tasks, f"+{pending_tasks}")
with col3:
st.metric("Выполняются", running_tasks, f"+{running_tasks}")
with col4:
st.metric("Завершены", completed_tasks, f"+{completed_tasks}")
with col5:
st.metric("Ошибки", failed_tasks, f"+{failed_tasks}")
st.markdown("---")
# Список задач
st.subheader("📋 Список задач")
# Получаем задачи из session_state
tasks = st.session_state.get('upload_tasks', {})
if tasks:
# Показываем задачи
for task_id, task in tasks.items():
status_emoji = {
'pending': '🟡',
'running': '🔵',
'completed': '🟢',
'failed': '🔴'
}.get(task.get('status', 'pending'), '')
with st.expander(f"{status_emoji} {task.get('filename', 'Unknown')} - {task.get('status', 'unknown').upper()}", expanded=True):
col1, col2 = st.columns([3, 1])
with col1:
st.write(f"**ID:** `{task_id}`")
st.write(f"**Статус:** {status_emoji} {task.get('status', 'unknown').upper()}")
st.write(f"**Файл:** {task.get('filename', 'Unknown')}")
st.write(f"**Эндпоинт:** {task.get('endpoint', 'Unknown')}")
if task.get('completed_at'):
completed_time = datetime.fromtimestamp(task['completed_at']).strftime("%Y-%m-%d %H:%M:%S")
st.write(f"**Завершена:** {completed_time}")
if task.get('result'):
result = task['result']
if task.get('status') == 'completed':
st.success(f"{result.get('message', 'Задача выполнена')}")
if result.get('object_id'):
st.info(f"ID объекта: {result['object_id']}")
else:
st.error(f"{result.get('message', 'Ошибка выполнения')}")
if task.get('error'):
st.error(f"❌ Ошибка: {task['error']}")
with col2:
if task.get('status') in ['pending', 'running']:
if st.button("❌ Отменить", key=f"cancel_{task_id}_btn", use_container_width=True):
st.info("Функция отмены будет реализована в следующих версиях")
else:
if st.button("🗑️ Удалить", key=f"delete_{task_id}_btn", use_container_width=True):
# Удаляем задачу из session_state
if 'upload_tasks' in st.session_state:
del st.session_state.upload_tasks[task_id]
st.rerun()
else:
# Пустое состояние
st.info("""
**Нет активных задач**
Загрузите файл на странице "Асинхронная загрузка", чтобы создать новую задачу.
Здесь вы сможете отслеживать прогресс обработки и управлять задачами.
""")
# Кнопка для создания тестовой задачи
if st.button("🧪 Создать тестовую задачу", key="create_test_task_btn"):
test_task_id = f"test_task_{int(time.time())}"
if 'upload_tasks' not in st.session_state:
st.session_state.upload_tasks = {}
st.session_state.upload_tasks[test_task_id] = {
'status': 'completed',
'filename': 'test_file.zip',
'endpoint': '/test/upload',
'result': {'message': 'Тестовая задача выполнена', 'object_id': 'test-123'},
'completed_at': time.time()
}
st.rerun()
st.markdown("---")
# Информация о статусах задач
with st.expander(" Статусы задач"):
st.markdown("""
**Статусы задач:**
- 🟡 **Ожидает** - задача создана и ожидает выполнения
- 🔵 **Выполняется** - задача обрабатывается
- 🟢 **Завершена** - задача успешно выполнена
- 🔴 **Ошибка** - произошла ошибка при выполнении
- ⚫ **Отменена** - задача была отменена пользователем
**Действия:**
- ❌ **Отменить** - отменить выполнение задачи
- 🔄 **Обновить** - обновить статус задачи
- 📊 **Детали** - просмотреть подробную информацию
""")