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