from __future__ import annotations import logging import streamlit as st from app.db.base import run_db from app.services import securities as securities_service from app.services.securities import SecurityPreview, SecurityServiceError from app.ui.common import as_table, call_db, current_user logger = logging.getLogger(__name__) _PREVIEW_KEY = "security_preview" _COLUMNS = { "isin": "ISIN", "secid": "SECID", "name": "Наименование", "currency": "Валюта", "initial_face_value": "Номинал (перв.)", "placement_date": "Начало торгов", "maturity_date": "Погашение", "coupon_rate": "Купон, %", "coupon_frequency": "Купонов в год", "is_active": "Обновлять", } def render() -> None: st.header("Бумаги") rows = call_db( securities_service.list_securities(), "Не удалось загрузить список бумаг" ) if rows is not None: if rows: st.dataframe(as_table(rows, _COLUMNS), hide_index=True) else: st.info("Бумаг пока нет — добавьте первую по ISIN.") st.subheader("Добавить по ISIN") _render_lookup_form() preview = st.session_state.get(_PREVIEW_KEY) if isinstance(preview, SecurityPreview): _render_preview(preview) def _render_lookup_form() -> None: with st.form("security_lookup"): isin = st.text_input("ISIN", placeholder="RU000A106169", max_chars=12) submitted = st.form_submit_button("Найти на Мосбирже") if not submitted: return st.session_state.pop(_PREVIEW_KEY, None) try: with st.spinner("Запрос к MOEX…"): preview = run_db(securities_service.preview_by_isin(isin, current_user())) except SecurityServiceError as exc: st.error(str(exc)) return except Exception as exc: # noqa: BLE001 logger.exception("Ошибка предпросмотра бумаги %s", isin) st.error(f"Не удалось получить данные бумаги: {exc}") return st.session_state[_PREVIEW_KEY] = preview def _render_preview(preview: SecurityPreview) -> None: info = preview.info with st.container(border=True): st.markdown(f"**{info.short_name or info.secid}** — данные MOEX") st.dataframe( as_table( [ { "isin": preview.isin, "secid": info.secid, "currency": f"{preview.currency_code} ({info.face_unit})", "initial_face_value": info.initial_face_value, "face_value": info.face_value, "issue_date": info.issue_date, "mat_date": info.mat_date, "coupon_percent": info.coupon_percent, "coupon_frequency": info.coupon_frequency, } ], { "isin": "ISIN", "secid": "SECID", "currency": "Валюта", "initial_face_value": "Номинал (перв.)", "face_value": "Номинал (тек.)", "issue_date": "Начало торгов", "mat_date": "Погашение", "coupon_percent": "Купон, %", "coupon_frequency": "Купонов в год", }, ), hide_index=True, ) save_col, cancel_col = st.columns(2) if save_col.button("Сохранить", type="primary", use_container_width=True): _save(preview) if cancel_col.button("Отмена", use_container_width=True): st.session_state.pop(_PREVIEW_KEY, None) st.rerun() def _save(preview: SecurityPreview) -> None: try: result = run_db(securities_service.save_preview(preview, current_user())) except SecurityServiceError as exc: st.error(str(exc)) return except Exception as exc: # noqa: BLE001 logger.exception("Ошибка сохранения бумаги %s", preview.isin) st.error(f"Не удалось сохранить бумагу: {exc}") return st.session_state.pop(_PREVIEW_KEY, None) st.toast(f"Бумага {result.isin} ({result.name}) добавлена.") st.rerun()