- Drop SMB/SAMBA subsystem completely: app/samba_access.py, app/path_settings.py, app/path_store.py, app/pipeline/file_manager.py and their tests, plus the smbprotocol dependency and all related UI (login/password fields, path editor, SMB diagnostics) in main.py. - run_batch now takes a .zip attached via Streamlit's file_uploader (same as the local dev tool) instead of reading from SMB or a fictitious OpenCloud "Attach" folder; renamed from run_local_zip_batch since it's now the single canonical batch entrypoint used by both main.py and scripts/local_zip_test_app.py. - Fixed run number resolution: added next_opencloud_run_number() which lists the Out/year/month/day folder via WebDAV PROPFIND, so repeated runs land in incrementing subfolders instead of always overwriting "1". - Added a self-service "Добавить токен" popover in main.py so users can link their own OpenCloud App Token to their Keycloak account (opencloud_token_store) without an admin running the CLI script; reworded the missing-token error to point at it.
112 lines
4.3 KiB
Python
112 lines
4.3 KiB
Python
import asyncio
|
||
import tempfile
|
||
from pathlib import Path
|
||
|
||
import streamlit as st
|
||
from dotenv import load_dotenv
|
||
|
||
from app.app_config import (
|
||
resolve_opencloud_access_token,
|
||
resolve_opencloud_username,
|
||
resolve_user_id,
|
||
)
|
||
from app.auth.service import AuthService, clear_session
|
||
from app.opencloud_token_store import OpenCloudTokenStoreError, add_token
|
||
from app.pipeline.service import run_batch
|
||
|
||
|
||
def main() -> None:
|
||
load_dotenv()
|
||
|
||
st.set_page_config(page_title="СФМ", page_icon=":lock:", layout="wide")
|
||
st.title("СФМ")
|
||
|
||
if not AuthService().run():
|
||
return
|
||
|
||
_, center_col, _ = st.columns([1, 2, 1])
|
||
with center_col:
|
||
_render_token_popover()
|
||
|
||
st.caption(
|
||
"Прикрепите .zip с XML-файлами — отчёт, протокол и лог "
|
||
"загрузятся в Out вашего личного пространства OpenCloud."
|
||
)
|
||
|
||
uploaded_zip = st.file_uploader("Архив с XML-файлами (.zip)", type=["zip"])
|
||
|
||
if st.button(
|
||
"Запустить обработку xml в excel",
|
||
type="primary",
|
||
use_container_width=True,
|
||
disabled=uploaded_zip is None,
|
||
):
|
||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||
zip_path = Path(tmp_dir) / uploaded_zip.name
|
||
zip_path.write_bytes(uploaded_zip.getvalue())
|
||
with st.spinner("Выполняется обработка..."):
|
||
launcher_name = resolve_opencloud_username() or resolve_user_id()
|
||
batch_result = run_batch(
|
||
zip_path=zip_path,
|
||
launcher=launcher_name,
|
||
opencloud_username=resolve_opencloud_username(),
|
||
opencloud_access_token=resolve_opencloud_access_token(),
|
||
)
|
||
if batch_result.status == "success":
|
||
st.success("Обработка завершена успешно.")
|
||
elif batch_result.status == "partial":
|
||
st.warning("Обработка завершена частично.")
|
||
else:
|
||
st.error("Обработка завершена с ошибками.")
|
||
st.write(
|
||
{
|
||
"processed": batch_result.processed,
|
||
"partial": batch_result.partial,
|
||
"errors": batch_result.errors,
|
||
"report_path": batch_result.report_path,
|
||
"protocol_path": batch_result.protocol_path,
|
||
"log_path": batch_result.log_path,
|
||
}
|
||
)
|
||
if batch_result.messages:
|
||
st.caption("Последние сообщения обработки:")
|
||
for message in batch_result.messages:
|
||
st.write(f"- {message}")
|
||
|
||
if st.button("Выйти", type="secondary"):
|
||
clear_session()
|
||
st.rerun()
|
||
|
||
|
||
def _render_token_popover() -> None:
|
||
"""Самообслуживание: пользователь сам привязывает свой App Token
|
||
OpenCloud к своей учётке (по preferred_username из Keycloak), без
|
||
обращения к администратору с CLI-скриптом.
|
||
"""
|
||
with st.popover("Добавить токен"):
|
||
username = resolve_opencloud_username()
|
||
if not username:
|
||
st.error("Не удалось определить вашу учётную запись.")
|
||
return
|
||
|
||
token = st.text_input(
|
||
"App Token",
|
||
type="password",
|
||
help="Долгоживущий App Token из веб-интерфейса OpenCloud (6 слов через пробел).",
|
||
key="opencloud_token_input",
|
||
)
|
||
if st.button("Сохранить", key="opencloud_token_save"):
|
||
if not token:
|
||
st.error("Введите токен.")
|
||
else:
|
||
try:
|
||
asyncio.run(add_token(username, token, added_by=username))
|
||
except OpenCloudTokenStoreError as exc:
|
||
st.error(f"Не удалось сохранить токен: {exc}")
|
||
else:
|
||
st.success("Токен сохранён.")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|