mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
fix(queue): persist "Print Anyway" so scheduler stops re-flagging it
When the user clicked Print Anyway on a filament-deficit warning, the
acknowledgement was one-shot. The route cleared manual_start and
filament_short, then the next scheduler tick re-ran
compute_deficit_for_queue_item against identical spool state, found
the same deficit, and re-set both flags. The item bounced between
"user said anyway" and "scheduler re-blocked" — every Play click
returned 409, every confirm got rolled back on the next tick.
Add a persistent acknowledgement flag on the queue item:
- New column `skip_filament_check` on print_queue. SQLite + Postgres
migration branched on is_sqlite() so Postgres doesn't reject
DEFAULT 0 on BOOLEAN.
- PrintQueueItemCreate + PrintQueueItemResponse schemas + the
TypeScript types carry the field.
- POST /print-queue/{id}/start with skip_filament_check=true now
ALSO sets item.skip_filament_check = True (not just clearing
manual_start / filament_short).
- PrintScheduler._block_on_filament_deficit short-circuits to
False — no compute, no flag-setting, no notification — when
item.skip_filament_check is True. We trust the operator's
decision and stop fighting them.
- PrintModal at queue-creation time threads
skip_filament_check=true into the create payload when the user
clicks Print Anyway on the frontend deficit warning, so a print
that was warned-then-acknowledged at add-to-queue time goes in
pre-acknowledged — scheduler never blocks it on first tick.
Flag is not auto-cleared on spool swap by design: if remaining is
now sufficient, the check returns no deficit anyway, so the flag
is moot. Auto-clearing would add lifecycle complexity without
changing behaviour.
AMS Backup awareness (the other half of the discussion) intentionally
NOT included — verified the H2D's bit-26 of print.cfg toggles with
the printer-side AMS Backup setting, but the X1C's cfg has a
different shape entirely and verifying every model family isn't
realistic. Silently under-warning would be worse than always
per-slot. The check stays single-slot for now.
This commit is contained in:
@@ -147,6 +147,40 @@ class TestPrintQueueAPI:
|
||||
assert result["status"] == "pending"
|
||||
assert result["manual_start"] is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_with_skip_filament_check(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""PrintModal "Print Anyway" persists skip_filament_check on creation (#1698-followup)."""
|
||||
printer = await printer_factory()
|
||||
archive = await archive_factory()
|
||||
|
||||
data = {
|
||||
"printer_id": printer.id,
|
||||
"archive_id": archive.id,
|
||||
"skip_filament_check": True,
|
||||
}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["skip_filament_check"] is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_skip_filament_check_defaults_false(
|
||||
self, async_client: AsyncClient, printer_factory, archive_factory, db_session
|
||||
):
|
||||
"""Default add-to-queue has skip_filament_check=False — no silent bypass."""
|
||||
printer = await printer_factory()
|
||||
archive = await archive_factory()
|
||||
|
||||
data = {"printer_id": printer.id, "archive_id": archive.id}
|
||||
response = await async_client.post("/api/v1/queue/", json=data)
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["skip_filament_check"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_add_to_queue_with_project_id(
|
||||
@@ -583,6 +617,53 @@ class TestQueueStartEndpoint:
|
||||
# decision to print anyway.
|
||||
assert called_with == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_start_with_skip_flag_persists_acknowledgement(
|
||||
self,
|
||||
async_client: AsyncClient,
|
||||
queue_item_factory,
|
||||
db_session,
|
||||
):
|
||||
"""skip_filament_check=true sets the persistent flag on the queue item
|
||||
so the scheduler doesn't re-flag it on the next tick (#1698-followup).
|
||||
|
||||
Without persistence the route's flag-clearing only survives until the
|
||||
next scheduler tick re-runs the deficit check on identical spool
|
||||
state and re-promotes the item — the user has to click Play+Confirm
|
||||
every single tick.
|
||||
"""
|
||||
item = await queue_item_factory(manual_start=True, filament_short=True)
|
||||
assert item.skip_filament_check is False
|
||||
|
||||
response = await async_client.post(f"/api/v1/queue/{item.id}/start?skip_filament_check=true")
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert body["skip_filament_check"] is True
|
||||
|
||||
await db_session.refresh(item)
|
||||
assert item.skip_filament_check is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.integration
|
||||
async def test_start_without_skip_flag_does_not_set_acknowledgement(
|
||||
self,
|
||||
async_client: AsyncClient,
|
||||
queue_item_factory,
|
||||
db_session,
|
||||
):
|
||||
"""A successful Play click with no deficit must NOT silently set the
|
||||
acknowledgement flag — only an explicit Print Anyway should.
|
||||
"""
|
||||
item = await queue_item_factory(manual_start=False, filament_short=False)
|
||||
assert item.skip_filament_check is False
|
||||
|
||||
response = await async_client.post(f"/api/v1/queue/{item.id}/start")
|
||||
assert response.status_code == 200
|
||||
|
||||
await db_session.refresh(item)
|
||||
assert item.skip_filament_check is False
|
||||
|
||||
|
||||
class TestQueueCancelEndpoint:
|
||||
"""Tests for the /queue/{item_id}/cancel endpoint."""
|
||||
|
||||
Reference in New Issue
Block a user