From 18c0e2b00215c4cfede66b75ed23ad9b3598e2e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 6 Jun 2026 12:27:07 +0000 Subject: [PATCH 1/4] Hide failed jobs from !list Add 'failed' to the set of finished statuses filtered out of the queue listing, so failed jobs no longer clutter the view. --- handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers.py b/handlers.py index 6a30ab0..d1823a1 100644 --- a/handlers.py +++ b/handlers.py @@ -966,7 +966,7 @@ async def _await_slice(job_id, timeout=300, interval=4): "failed": "❌", "skipped": "⏭️", "cancelled": "🚫", } # Finished statuses hidden from !liste — they just pile up and clutter the view. -_DONE_QUEUE_STATUS = {"completed", "cancelled", "skipped"} +_DONE_QUEUE_STATUS = {"completed", "cancelled", "skipped", "failed"} async def _skip(group_id): From 93b47b063ebfdccc430ea06a53f1e0ffc2918ec6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 6 Jun 2026 12:58:03 +0000 Subject: [PATCH 2/4] Show print duration per model in !list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Append the sliced print-time prediction (e.g. ' · 21 min') to each queue entry, reusing the plate metadata's print_time_seconds field and the existing _fmt_minutes formatting. Omitted gracefully when the queue item carries no time. --- handlers.py | 6 +++++- tests/test_list.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/test_list.py diff --git a/handlers.py b/handlers.py index d1823a1..1d6320e 100644 --- a/handlers.py +++ b/handlers.py @@ -1063,7 +1063,11 @@ async def _list(group_id): # didn't queue (e.g. a Bambu Studio print — we don't know). e = ejects.get(it.get("id")) tag = eject_tag if e else (noeject_tag if e is False else "") - lines.append(f'{i}. {_STATUS_EMOJI.get(st, "")} {nm} ({st}){tag}'.replace(" ", " ")) + # Sliced print-time prediction (same slicer field as the plate metadata), + # shown as e.g. ' · 21 min'; omitted when the queue item doesn't carry it. + secs = it.get("print_time_seconds") or it.get("prediction") + dur = f" · {colors._fmt_minutes(secs)}" if secs else "" + lines.append(f'{i}. {_STATUS_EMOJI.get(st, "")} {nm} ({st}){dur}{tag}'.replace(" ", " ")) await signal_client.send_to_group(group_id, i18n.t(lang, "list_header") + "\n".join(lines)) diff --git a/tests/test_list.py b/tests/test_list.py new file mode 100644 index 0000000..64b6748 --- /dev/null +++ b/tests/test_list.py @@ -0,0 +1,50 @@ +import asyncio +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) +import config # noqa: E402 +import handlers # noqa: E402 +import store # noqa: E402 + + +def _setup(tmp_path, monkeypatch, queue_items): + config.DB_PATH = str(tmp_path / "t.db") + store.init_db() + sent = [] + + async def fake_send(group_id, msg, **k): + sent.append(msg) + + async def fake_list_queue(): + return queue_items + + monkeypatch.setattr(handlers.signal_client, "send_to_group", fake_send) + monkeypatch.setattr(handlers.bambuddy, "list_queue", fake_list_queue) + return sent + + +def test_list_shows_print_duration(tmp_path, monkeypatch): + sent = _setup(tmp_path, monkeypatch, [ + {"id": 1, "status": "pending", "library_file_name": "Quick", + "print_time_seconds": 1267}, # 21 min + {"id": 2, "status": "pending", "library_file_name": "Long", + "print_time_seconds": 7800}, # 2h10 + {"id": 3, "status": "pending", "library_file_name": "Unknown"}, # no time + ]) + asyncio.run(handlers._list("g1")) + out = sent[-1] + assert "Quick (pending) · 21 min" in out + assert "Long (pending) · 2h10" in out + # No duration field → nothing appended after the status. + assert "Unknown (pending)\n" in out or out.rstrip().endswith("Unknown (pending)") + + +def test_list_done_jobs_hidden(tmp_path, monkeypatch): + sent = _setup(tmp_path, monkeypatch, [ + {"id": 1, "status": "failed", "library_file_name": "Boom"}, + {"id": 2, "status": "completed", "library_file_name": "Done"}, + ]) + asyncio.run(handlers._list("g1")) + assert "Boom" not in sent[-1] + assert "Done" not in sent[-1] From ecce790a1e7c47a9b039a7f98e679d41b912d4e8 Mon Sep 17 00:00:00 2001 From: phieb Date: Sat, 6 Jun 2026 15:07:50 +0200 Subject: [PATCH 3/4] CI: GitHub Actions workflow running pytest on Python 3.12 Co-Authored-By: Claude Opus 4.8 --- .github/workflows/tests.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..e9ec56d --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,30 @@ +name: Tests + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +jobs: + pytest: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + + - name: Install DejaVu fonts + # Matches the Dockerfile; the swatch renderer prefers DejaVu for umlauts. + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends fonts-dejavu-core + + - name: Install dependencies + run: pip install -r requirements-dev.txt + + - name: Run tests + run: pytest -q From decccb20ccb3ef850126bf8d6f4581d8c7a8c064 Mon Sep 17 00:00:00 2001 From: phieb Date: Sat, 6 Jun 2026 15:09:43 +0200 Subject: [PATCH 4/4] !list: drop dead `prediction` fallback; print_time_seconds verified against live API Confirmed against the real Bambuddy /queue/ payload (43 items): every item carries print_time_seconds in seconds; no item ever has a `prediction` field. Removed the misleading fallback. Co-Authored-By: Claude Opus 4.8 --- handlers.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/handlers.py b/handlers.py index 1d6320e..435541f 100644 --- a/handlers.py +++ b/handlers.py @@ -1063,9 +1063,10 @@ async def _list(group_id): # didn't queue (e.g. a Bambu Studio print — we don't know). e = ejects.get(it.get("id")) tag = eject_tag if e else (noeject_tag if e is False else "") - # Sliced print-time prediction (same slicer field as the plate metadata), - # shown as e.g. ' · 21 min'; omitted when the queue item doesn't carry it. - secs = it.get("print_time_seconds") or it.get("prediction") + # Sliced print time in seconds (verified against the real Bambuddy + # /queue/ payload — the field is always `print_time_seconds`), shown as + # e.g. ' · 21 min'; omitted when the queue item doesn't carry it. + secs = it.get("print_time_seconds") dur = f" · {colors._fmt_minutes(secs)}" if secs else "" lines.append(f'{i}. {_STATUS_EMOJI.get(st, "")} {nm} ({st}){dur}{tag}'.replace(" ", " ")) await signal_client.send_to_group(group_id, i18n.t(lang, "list_header") + "\n".join(lines))