Files no longer go into a shared test space with a guessed folder name. Instead, resolve each user's personal drive id via GET /graph/v1.0/me/drives (Basic Auth, same App Token as uploads) on every run, and upload into Out/год/месяц/день/номер_запуска inside that drive, creating the path via MKCOL since a fresh personal drive won't have it yet. Removes the now-obsolete OPENCLOUD_SPACE_ID / resolve_opencloud_user_folder and the shared-space smoke script. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
102 lines
4.5 KiB
Python
102 lines
4.5 KiB
Python
"""Тестовая страница: прикрепляете .zip с XML, он распаковывается,
|
||
обрабатывается тем же парсером, что и прод, а готовый отчёт/протокол/лог
|
||
грузятся в OpenCloud. Без SMB на входе — специально для локального теста
|
||
всей цепочки целиком.
|
||
|
||
Не трогает main.py и основной SMB-пайплайн.
|
||
|
||
Запуск:
|
||
streamlit run scripts/local_zip_test_app.py
|
||
|
||
Username/токен можно один раз прописать в .env (см. .env.example) — поля
|
||
на странице предзаполнятся, но их всегда можно поменять руками. Папка не
|
||
запрашивается — файлы летят в личное пространство пользователя (id
|
||
находится через GET /graph/v1.0/me/drives, см. app/opencloud_access.py).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import sys
|
||
import tempfile
|
||
from pathlib import Path
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||
|
||
import streamlit as st
|
||
from dotenv import load_dotenv
|
||
|
||
from app.pipeline.service import run_local_zip_batch
|
||
|
||
load_dotenv()
|
||
|
||
st.set_page_config(page_title="СФМ — локальный тест", page_icon="📦")
|
||
st.title("Локальный тест: zip → обработка → OpenCloud")
|
||
st.caption(
|
||
"Прикрепляете .zip с XML-файлами, он распаковывается и обрабатывается "
|
||
"тем же кодом парсинга, что и прод (app/pipeline/parser.py), а отчёт, "
|
||
"протокол и лог грузятся в OpenCloud (app/opencloud_access.py). "
|
||
"SMB тут не используется вообще."
|
||
)
|
||
|
||
username = st.text_input(
|
||
"Username (для Basic Auth в OpenCloud)",
|
||
value=os.getenv("SFM_OPENCLOUD_USERNAME", ""),
|
||
help="Логин, под которым заказчик создал App Token в OpenCloud. Можно прописать в .env как SFM_OPENCLOUD_USERNAME.",
|
||
)
|
||
access_token = st.text_input(
|
||
"App Token",
|
||
value=os.getenv("SFM_OPENCLOUD_ACCESS_TOKEN", ""),
|
||
type="password",
|
||
help=(
|
||
"Долгоживущий App Token из веб-интерфейса OpenCloud (не Bearer из Keycloak — "
|
||
"авторизация теперь через HTTP Basic Auth). Можно прописать в .env как "
|
||
"SFM_OPENCLOUD_ACCESS_TOKEN."
|
||
),
|
||
)
|
||
launcher = st.text_input("Запустил (для протокола)", value="local-test")
|
||
|
||
uploaded_zip = st.file_uploader("Архив с XML-файлами (.zip)", type=["zip"])
|
||
|
||
if st.button("Начать обработку", type="primary", use_container_width=True):
|
||
if not username:
|
||
st.error("Укажите username.")
|
||
elif not access_token:
|
||
st.error("Укажите App Token.")
|
||
elif uploaded_zip is None:
|
||
st.error("Прикрепите .zip-архив.")
|
||
else:
|
||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||
zip_path = Path(tmp_dir) / uploaded_zip.name
|
||
zip_path.write_bytes(uploaded_zip.getvalue())
|
||
with st.spinner("Распаковываем, обрабатываем, грузим в OpenCloud..."):
|
||
result = run_local_zip_batch(
|
||
zip_path=zip_path,
|
||
launcher=launcher or "local-test",
|
||
opencloud_username=username,
|
||
opencloud_access_token=access_token,
|
||
)
|
||
|
||
if result.status == "success":
|
||
st.success("Обработка завершена успешно.")
|
||
elif result.status == "partial":
|
||
st.warning("Обработка завершена частично.")
|
||
else:
|
||
st.error("Обработка завершена с ошибками.")
|
||
|
||
st.write(
|
||
{
|
||
"processed": result.processed,
|
||
"partial": result.partial,
|
||
"errors": result.errors,
|
||
"report_path": result.report_path,
|
||
"protocol_path": result.protocol_path,
|
||
"log_path": result.log_path,
|
||
"duration_seconds": result.duration_seconds,
|
||
}
|
||
)
|
||
if result.messages:
|
||
st.caption("Сообщения обработки:")
|
||
for message in result.messages:
|
||
st.write(f"- {message}")
|