SFM/main.py
Raykov-MS 7b90daa396 Remove SMB entirely, switch input to attached zip, add self-service token popover
- 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.
2026-09-17 15:34:53 +03:00

112 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()