Stage 06.1 - journal management UI, export and system menu redesign
This commit is contained in:
@@ -19,6 +19,7 @@ from src.telegram.ui.currency_ui import (
|
||||
is_zero_balance,
|
||||
)
|
||||
from src.telegram.ui.exchange_error import (
|
||||
classify_exchange_error,
|
||||
show_callback_exchange_error,
|
||||
show_message_exchange_error,
|
||||
)
|
||||
@@ -52,42 +53,6 @@ def _portfolio_warning_keyboard() -> InlineKeyboardMarkup:
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def _safe_log_info(
|
||||
journal: JournalService,
|
||||
event_type: str,
|
||||
message: str,
|
||||
payload: dict | None = None,
|
||||
) -> None:
|
||||
try:
|
||||
journal.log_info(event_type, message, payload)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _safe_log_warning(
|
||||
journal: JournalService,
|
||||
event_type: str,
|
||||
message: str,
|
||||
payload: dict | None = None,
|
||||
) -> None:
|
||||
try:
|
||||
journal.log_warning(event_type, message, payload)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _safe_log_error(
|
||||
journal: JournalService,
|
||||
event_type: str,
|
||||
message: str,
|
||||
payload: dict | None = None,
|
||||
) -> None:
|
||||
try:
|
||||
journal.log_error(event_type, message, payload)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def sort_balances(items: list[BalanceSummary]) -> list[BalanceSummary]:
|
||||
def sort_key(item: BalanceSummary) -> tuple[int, str]:
|
||||
currency = item.currency.upper()
|
||||
@@ -103,32 +68,31 @@ async def _render_portfolio_screen(
|
||||
user_id: int | None,
|
||||
chat_id: int | None,
|
||||
edit_mode: bool,
|
||||
action: str,
|
||||
) -> None:
|
||||
service = AccountsService()
|
||||
exchange_service = ExchangeService()
|
||||
journal = JournalService()
|
||||
|
||||
_safe_log_info(
|
||||
journal,
|
||||
"user_open_portfolio",
|
||||
"Пользователь открыл экран портфеля.",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"chat_id": chat_id,
|
||||
},
|
||||
journal.log_ui_info(
|
||||
event_type="portfolio_open_requested",
|
||||
message="Запрошено открытие экрана портфеля.",
|
||||
screen="portfolio",
|
||||
action=action,
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
)
|
||||
|
||||
balances = service.get_live_balance_summary()
|
||||
|
||||
if not balances:
|
||||
_safe_log_warning(
|
||||
journal,
|
||||
"portfolio_empty",
|
||||
"Портфель открыт, но баланс пуст.",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"chat_id": chat_id,
|
||||
},
|
||||
journal.log_ui_warning(
|
||||
event_type="portfolio_empty",
|
||||
message="Нет данных по балансу.",
|
||||
screen="portfolio",
|
||||
action=action,
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
)
|
||||
|
||||
text = (
|
||||
@@ -147,15 +111,14 @@ async def _render_portfolio_screen(
|
||||
visible_balances = sort_balances(visible_balances)
|
||||
|
||||
if not visible_balances:
|
||||
_safe_log_warning(
|
||||
journal,
|
||||
"portfolio_zero_balances",
|
||||
"Портфель открыт, но все балансы нулевые.",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"chat_id": chat_id,
|
||||
"assets_count": len(balances),
|
||||
},
|
||||
journal.log_ui_warning(
|
||||
event_type="portfolio_zero_balances",
|
||||
message="Нет активов с балансом.",
|
||||
screen="portfolio",
|
||||
action=action,
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
payload={"assets_count": len(balances)},
|
||||
)
|
||||
|
||||
text = (
|
||||
@@ -211,30 +174,25 @@ async def _render_portfolio_screen(
|
||||
has_partial_data = len(missing_estimate_assets) > 0
|
||||
|
||||
if missing_estimate_assets:
|
||||
lines.extend(
|
||||
[
|
||||
"🟡 <b>Данные загружены частично</b>",
|
||||
]
|
||||
)
|
||||
lines.append("🟡 <b>Данные загружены частично</b>")
|
||||
|
||||
if has_any_estimate:
|
||||
lines.append(f"ОЦЕНКА · ~${format_usd_amount(total_estimated_usd)}")
|
||||
|
||||
if missing_estimate_assets:
|
||||
lines.append(
|
||||
f"Нет оценки: {', '.join(missing_estimate_assets)}"
|
||||
)
|
||||
lines.append(f"Нет оценки: {', '.join(missing_estimate_assets)}")
|
||||
|
||||
for block in asset_blocks:
|
||||
lines.extend(block)
|
||||
|
||||
_safe_log_info(
|
||||
journal,
|
||||
"portfolio_open_success",
|
||||
"Портфель успешно показан пользователю.",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"chat_id": chat_id,
|
||||
journal.log_ui_info(
|
||||
event_type="portfolio_open_success",
|
||||
message="Портфель загружен.",
|
||||
screen="portfolio",
|
||||
action=action,
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
payload={
|
||||
"assets_count": len(visible_balances),
|
||||
"estimated_usd": round(total_estimated_usd, 2) if has_any_estimate else None,
|
||||
"missing_estimate_assets": missing_estimate_assets,
|
||||
@@ -242,25 +200,23 @@ async def _render_portfolio_screen(
|
||||
)
|
||||
|
||||
if missing_estimate_assets:
|
||||
_safe_log_warning(
|
||||
journal,
|
||||
"portfolio_partial_estimate",
|
||||
"Портфель показан частично: не для всех активов доступна USD-оценка.",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"chat_id": chat_id,
|
||||
"missing_estimate_assets": missing_estimate_assets,
|
||||
journal.log_ui_warning(
|
||||
event_type="portfolio_partial_estimate",
|
||||
message="Портфель загружен частично.",
|
||||
screen="portfolio",
|
||||
action=action,
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
payload={
|
||||
"assets_count": len(visible_balances),
|
||||
"estimated_assets": len(visible_balances) - len(missing_estimate_assets),
|
||||
"failed_assets": missing_estimate_assets,
|
||||
},
|
||||
)
|
||||
|
||||
if has_partial_data:
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
now_line(),
|
||||
]
|
||||
)
|
||||
|
||||
lines.extend(["", now_line()])
|
||||
|
||||
text = "\n".join(lines).rstrip()
|
||||
|
||||
reply_markup = (
|
||||
@@ -288,18 +244,20 @@ async def open_portfolio(message: Message, state: FSMContext) -> None:
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
edit_mode=False,
|
||||
action="open",
|
||||
)
|
||||
except ExchangeError as exc:
|
||||
journal = JournalService()
|
||||
_safe_log_error(
|
||||
journal,
|
||||
"portfolio_open_error",
|
||||
f"Не удалось открыть портфель: {exc}",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"chat_id": chat_id,
|
||||
},
|
||||
JournalService().log_ui_error(
|
||||
event_type="portfolio_open_error",
|
||||
message="Не удалось загрузить портфель.",
|
||||
screen="portfolio",
|
||||
action="open",
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
)
|
||||
|
||||
await show_message_exchange_error(
|
||||
message,
|
||||
title="<b>💼 Портфель</b>",
|
||||
@@ -327,19 +285,21 @@ async def retry_portfolio(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
edit_mode=True,
|
||||
action="retry",
|
||||
)
|
||||
await callback.answer()
|
||||
except ExchangeError as exc:
|
||||
journal = JournalService()
|
||||
_safe_log_error(
|
||||
journal,
|
||||
"portfolio_retry_error",
|
||||
f"Не удалось повторно открыть портфель: {exc}",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"chat_id": chat_id,
|
||||
},
|
||||
JournalService().log_ui_error(
|
||||
event_type="portfolio_retry_error",
|
||||
message="Не удалось обновить портфель.",
|
||||
screen="portfolio",
|
||||
action="retry",
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
)
|
||||
|
||||
await show_callback_exchange_error(
|
||||
callback,
|
||||
title="<b>💼 Портфель</b>",
|
||||
|
||||
Reference in New Issue
Block a user