From 1d7262eb784b815a832a9782e722e93e7c4acf8c Mon Sep 17 00:00:00 2001 From: benya Date: Sun, 8 Feb 2026 02:03:34 +0300 Subject: [PATCH] Add queue details view --- handlers/backup.py | 8 +++++++- keyboards.py | 2 +- services/queue.py | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/handlers/backup.py b/handlers/backup.py index 5ff8ebb..d8f178c 100644 --- a/handlers/backup.py +++ b/handlers/backup.py @@ -7,7 +7,7 @@ from app import dp from auth import is_admin_msg from keyboards import backup_kb from lock_utils import acquire_lock, release_lock -from services.queue import enqueue, format_status +from services.queue import enqueue, format_status, format_details from services.backup import backup_badge, restore_help from services.runner import run_cmd @@ -250,6 +250,12 @@ async def qb(msg: Message): await msg.answer(format_status(), reply_markup=backup_kb) +@dp.message(F.text == "🧾 Queue details") +async def qd(msg: Message): + if is_admin_msg(msg): + await msg.answer(format_details(), reply_markup=backup_kb) + + @dp.message(F.text == "▶️ Run backup") async def br(msg: Message): if is_admin_msg(msg): diff --git a/keyboards.py b/keyboards.py index 54c3b8d..9cb7209 100644 --- a/keyboards.py +++ b/keyboards.py @@ -37,7 +37,7 @@ backup_kb = ReplyKeyboardMarkup( keyboard=[ [KeyboardButton(text="📦 Status"), KeyboardButton(text="📦 Last snapshot")], [KeyboardButton(text="📊 Repo stats"), KeyboardButton(text="🧯 Restore help")], - [KeyboardButton(text="▶️ Run backup"), KeyboardButton(text="🧾 Queue")], + [KeyboardButton(text="▶️ Run backup"), KeyboardButton(text="🧾 Queue"), KeyboardButton(text="🧾 Queue details")], [KeyboardButton(text="🧪 Restic check"), KeyboardButton(text="📬 Weekly report"), KeyboardButton(text="⬅️ Назад")], ], resize_keyboard=True, diff --git a/services/queue.py b/services/queue.py index ac51319..291dbb9 100644 --- a/services/queue.py +++ b/services/queue.py @@ -1,34 +1,58 @@ import asyncio -from typing import Awaitable, Callable +import time +from typing import Awaitable, Callable, Any _queue: asyncio.Queue = asyncio.Queue() _current_label: str | None = None +_current_meta: dict[str, Any] | None = None async def enqueue(label: str, job: Callable[[], Awaitable[None]]) -> int: - await _queue.put((label, job)) + await _queue.put((label, job, time.time())) return _queue.qsize() async def worker(): - global _current_label + global _current_label, _current_meta while True: - label, job = await _queue.get() + label, job, enqueued_at = await _queue.get() _current_label = label + _current_meta = {"enqueued_at": enqueued_at, "started_at": time.time()} try: await job() finally: _current_label = None + _current_meta = None _queue.task_done() def format_status() -> str: - pending = [label for label, _ in list(_queue._queue)] + pending = list(_queue._queue) lines = ["🧾 Queue"] lines.append(f"🔄 Running: {_current_label or 'idle'}") lines.append(f"⏳ Pending: {len(pending)}") if pending: - preview = ", ".join(pending[:5]) + preview = ", ".join([p[0] for p in pending[:5]]) lines.append(f"➡️ Next: {preview}") return "\n".join(lines) + + +def format_details(limit: int = 10) -> str: + now = time.time() + lines = ["🧾 Queue details"] + if _current_label: + started_at = _current_meta.get("started_at") if _current_meta else None + runtime = f"{int(now - started_at)}s" if started_at else "n/a" + lines.append(f"🔄 Running: {_current_label} ({runtime})") + else: + lines.append("🔄 Running: idle") + + pending = list(_queue._queue) + lines.append(f"⏳ Pending: {len(pending)}") + if pending: + lines.append("🔢 Position | Label | Wait") + for i, (label, _job, enqueued_at) in enumerate(pending[:limit], start=1): + wait = int(now - enqueued_at) + lines.append(f"{i:>3} | {label} | {wait}s") + return "\n".join(lines)