"""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