"""Session-level plumbing shared by all dashboard pages: API client, demo "today", local acks, error banners.""" from __future__ import annotations import os from collections.abc import Callable from datetime import date, datetime from html import escape from typing import TypeVar import streamlit as st from dashboard.api_client import ApiClient, ApiError, ApiUnavailable from dashboard.components import ui T = TypeVar("T") # Short doctor names for the header; no other doctor PII is shown. DOCTORS = {"doc-smirnova": ("д-р Смирнова Е. А.", "д-ра Смирновой Е. А.")} PATIENTS_PAGE = "pages/1_Пациенты.py" CONNECT_PAGE = "pages/1b_Подключить_пациента.py" @st.cache_resource def _client(base_url: str | None, doctor_id: str | None) -> ApiClient: return ApiClient(base_url, doctor_id) def client() -> ApiClient: return _client(os.environ.get("API_BASE_URL"), os.environ.get("DOCTOR_ID")) def doctor_names() -> tuple[str, str]: did = client().doctor_id return DOCTORS.get(did, (did, did)) def now() -> datetime | None: """Demo clock from the API (ADR-005), so day 13 of the scenario is "today" on screen; None if unknown.""" c = client().clock() return c.now if c else None def today() -> date | None: n = now() return n.date() if n else None def local_acks() -> dict[int, tuple[str, datetime]]: return st.session_state.setdefault("acked_local", {}) def call(fn: Callable[[], T]) -> T | None: """Run an API call; on failure show a calm banner instead of a traceback and return None.""" try: return fn() except ApiUnavailable as exc: st.html(ui.banner(f"API недоступен. {escape(exc.message)}. Данные обновятся автоматически.", "grey")) except ApiError as exc: st.html(ui.banner(f"Не удалось загрузить данные. {escape(exc.message)}", "grey")) return None