feat: unify market/portfolio/system UI, improve exchange errors and asset valuation

This commit is contained in:
2026-04-22 18:21:34 +03:00
parent 2a9ef16524
commit 1fb72ced58
13 changed files with 2034 additions and 822 deletions

View File

@@ -1,7 +1,11 @@
# app/src/core/system_status.py
from __future__ import annotations
import re
from dataclasses import dataclass
from datetime import datetime
from zoneinfo import ZoneInfo
from src.core.config import load_settings
from src.core.constants import APP_NAME, APP_VERSION
@@ -40,7 +44,8 @@ def _extract_postgres_version(raw: str) -> str:
def _build_exchange_status(
exchange_service: ExchangeService, default_symbol: str
exchange_service: ExchangeService,
default_symbol: str,
) -> ComponentStatus:
try:
symbol_validation = exchange_service.validate_symbol(default_symbol)
@@ -48,7 +53,7 @@ def _build_exchange_status(
return ComponentStatus(
name="Биржа",
state="🔴",
details=f"Не удалось проверить инструмент: {exc}",
details=_humanize_error_message(str(exc)),
)
exchange_health = exchange_service.get_health()
@@ -60,7 +65,7 @@ def _build_exchange_status(
return ComponentStatus(
name="Биржа",
state="🔴",
details=exchange_health.message or "Ошибка подключения к API биржи.",
details=_humanize_error_message(exchange_health.message or ""),
)
return ComponentStatus(
@@ -78,7 +83,7 @@ def _build_account_status(exchange_service: ExchangeService) -> ComponentStatus:
return ComponentStatus(
name="Аккаунт",
state="🔴",
details=private_auth_health.message or "Ошибка private API.",
details=_humanize_error_message(private_auth_health.message or ""),
)
@@ -145,26 +150,75 @@ def get_system_snapshot() -> SystemSnapshot:
)
def has_system_alerts(snapshot: SystemSnapshot) -> bool:
return any(component.state != "🟢" for component in snapshot.components)
def _render_component(component: ComponentStatus) -> str:
if component.state == "🟢":
return f"{component.state} <b>{component.name}</b>"
line = f"{component.state} {component.name}"
return f"{component.state} <b>{component.name}</b>\n{component.details}"
if component.state == "🟢" or not component.details:
return line
return f"{line}\n{component.details}"
def build_system_text() -> str:
def _now_hhmmss() -> str:
settings = load_settings()
tz_name = settings.tz or "UTC"
try:
local_dt = datetime.now(ZoneInfo(tz_name))
except Exception:
local_dt = datetime.utcnow()
return local_dt.strftime("%H:%M:%S")
def build_system_text(*, include_updated_at: bool = False) -> str:
snapshot = get_system_snapshot()
components_block = "\n".join(
_render_component(component) for component in snapshot.components
)
return (
"<b>⚙️ Система</b>\n\n"
f"{components_block}\n\n"
"<b>🌐 Окружение</b>\n"
f"• приложение: {snapshot.app_name} {snapshot.app_version}\n"
f"• база данных: {snapshot.db_label}\n"
f"• часовой пояс: {snapshot.timezone_name}\n"
f"• режим: {snapshot.mode_label}\n"
f"• инструмент: {snapshot.default_symbol}"
)
text = (
"<b>⚙️ Система</b>\n"
f"🔸 <b>{snapshot.mode_label}</b>\n"
f"⏱️ {snapshot.timezone_name}\n\n"
f"{components_block}"
)
if include_updated_at:
text += f"\n\n<i>Обновлено: {_now_hhmmss()}</i>"
return text
def _humanize_error_message(text: str) -> str:
t = text.lower()
# сеть
if "nodename nor servname" in t or "name or service not known" in t:
return "Нет связи с биржей"
if "timeout" in t or "timed out" in t:
return "Биржа не отвечает (таймаут)"
if "network error" in t or "connection error" in t:
return "Ошибка сети при обращении к бирже"
# API / доступ
if "private api error" in t:
return "Ошибка доступа к аккаунту"
if "invalid api key" in t or "api key" in t:
return "Неверный API ключ"
if "forbidden" in t or "unauthorized" in t:
return "Нет доступа к аккаунту"
# время
if "-1021" in t or "doesn't match server time" in t:
return "Ошибка времени (рассинхронизация)"
return "Не удалось получить данные с биржи"