From 3d2cdc4e770ec025261f0c779fe01d61469c80f1 Mon Sep 17 00:00:00 2001 From: Kawa Date: Wed, 15 Apr 2026 11:03:41 +0200 Subject: [PATCH] =?UTF-8?q?feat(11-01):=20UIE-02=20=E2=80=94=20dedicated?= =?UTF-8?q?=20Add=20Printer=20page=20at=20GET=20/printers/new?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create imptune/templates/printers_new.html: full page extending base.html with plain
(no HTMX, browser follows 303) and inline driver upload sub-form - Update imptune/templates/printers.html: remove inline Add Printer form, add Add Printer link - Add GET /printers/new route in imptune/api/pages.py (placed before /{printer_id}) - Change POST /printers to always return RedirectResponse(url='/printers', status_code=303) - Update existing POST /printers tests to assert 303 + follow_redirects=False - Update test_printer_form.py to check /printers/new instead of /printers (UIE-02 arch) - UIE-02 tests GREEN; UIE-01/03 scaffolds remain RED (expected — not yet implemented) --- imptune/api/pages.py | 17 +++++ imptune/api/printers.py | 4 +- imptune/templates/printers.html | 5 +- imptune/templates/printers_new.html | 100 ++++++++++++++++++++++++++++ tests/test_printer_crud.py | 30 ++++----- tests/test_printer_form.py | 37 +++++----- 6 files changed, 151 insertions(+), 42 deletions(-) create mode 100644 imptune/templates/printers_new.html diff --git a/imptune/api/pages.py b/imptune/api/pages.py index 07764b8..204406d 100644 --- a/imptune/api/pages.py +++ b/imptune/api/pages.py @@ -85,6 +85,23 @@ def printers_page(request: Request): ) +@router.get("/printers/new", response_class=HTMLResponse) +def printers_new_page(request: Request): + from imptune.db.models import Client, Driver + + clients = list(Client.select().order_by(Client.name)) + all_drivers = list(Driver.select().order_by(Driver.uploaded_at.desc())) + driver_data = [ + {"driver": d, "names": json.loads(d.driver_desc) if d.driver_desc else []} + for d in all_drivers + ] + return templates.TemplateResponse( + request=request, + name="printers_new.html", + context={"clients": clients, "driver_data": driver_data}, + ) + + @router.get("/printers/{printer_id}", response_class=HTMLResponse) def printer_detail(request: Request, printer_id: int): from imptune.db.models import Client, Driver, Icon, Printer diff --git a/imptune/api/printers.py b/imptune/api/printers.py index 0af9203..7d98664 100644 --- a/imptune/api/printers.py +++ b/imptune/api/printers.py @@ -5,7 +5,7 @@ from collections import defaultdict from pathlib import Path from fastapi import APIRouter, Form, Request -from fastapi.responses import HTMLResponse +from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates from peewee import JOIN @@ -101,7 +101,7 @@ def create_printer( driver=driver_fk, ) - return _render_printer_list(request) + return RedirectResponse(url="/printers", status_code=303) @router.delete("/{printer_id}", response_class=HTMLResponse) diff --git a/imptune/templates/printers.html b/imptune/templates/printers.html index 0819b28..08215a7 100644 --- a/imptune/templates/printers.html +++ b/imptune/templates/printers.html @@ -3,10 +3,7 @@ {% block content %}

Printers

-
-

Add Printer

- {% include "partials/printer_form.html" %} -
+

Add Printer

Printer Library

diff --git a/imptune/templates/printers_new.html b/imptune/templates/printers_new.html new file mode 100644 index 0000000..053a687 --- /dev/null +++ b/imptune/templates/printers_new.html @@ -0,0 +1,100 @@ +{% extends "base.html" %} + +{% block content %} +

Add Printer

+ +

← Back to Printer Library

+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+{% endblock %} diff --git a/tests/test_printer_crud.py b/tests/test_printer_crud.py index 117cac4..ba70a8a 100644 --- a/tests/test_printer_crud.py +++ b/tests/test_printer_crud.py @@ -11,10 +11,9 @@ from fastapi.testclient import TestClient def test_create_printer_persisted(client: TestClient) -> None: - """POST /printers with required fields returns 200; GET /printers contains printer name.""" + """POST /printers with required fields returns 303; GET /printers contains printer name.""" from imptune.db.models import Printer - # FIXME: POST /printers will return 303 after Task 2 — update status_code assertion then resp = client.post( "/printers", data={ @@ -22,8 +21,9 @@ def test_create_printer_persisted(client: TestClient) -> None: "ip_address": "192.168.1.100", "port_name": "IP_192_168_1_100", }, + follow_redirects=False, ) - assert resp.status_code == 200 + assert resp.status_code == 303 # GET /printers page contains the printer name page = client.get("/printers") @@ -39,7 +39,6 @@ def test_create_printer_duplex(client: TestClient) -> None: """POST /printers with duplex_mode=LongEdge persists correctly.""" from imptune.db.models import Printer - # FIXME: POST /printers will return 303 after Task 2 — update status_code assertion then resp = client.post( "/printers", data={ @@ -48,8 +47,9 @@ def test_create_printer_duplex(client: TestClient) -> None: "port_name": "IP_192_168_1_101", "duplex_mode": "LongEdge", }, + follow_redirects=False, ) - assert resp.status_code == 200 + assert resp.status_code == 303 printers = list(Printer.select().where(Printer.name == "Duplex Printer")) assert len(printers) == 1 @@ -60,7 +60,6 @@ def test_create_printer_color_mode(client: TestClient) -> None: """POST /printers with color_mode not sent (unchecked) sets color_mode=False.""" from imptune.db.models import Printer - # FIXME: POST /printers will return 303 after Task 2 — update status_code assertion then resp = client.post( "/printers", data={ @@ -69,8 +68,9 @@ def test_create_printer_color_mode(client: TestClient) -> None: "port_name": "IP_192_168_1_102", # color_mode intentionally omitted (unchecked checkbox) }, + follow_redirects=False, ) - assert resp.status_code == 200 + assert resp.status_code == 303 printers = list(Printer.select().where(Printer.name == "Mono Printer")) assert len(printers) == 1 @@ -81,7 +81,6 @@ def test_create_printer_paper_size(client: TestClient) -> None: """POST /printers with paper_size=Letter persists correctly.""" from imptune.db.models import Printer - # FIXME: POST /printers will return 303 after Task 2 — update status_code assertion then resp = client.post( "/printers", data={ @@ -90,8 +89,9 @@ def test_create_printer_paper_size(client: TestClient) -> None: "port_name": "IP_192_168_1_103", "paper_size": "Letter", }, + follow_redirects=False, ) - assert resp.status_code == 200 + assert resp.status_code == 303 printers = list(Printer.select().where(Printer.name == "Letter Printer")) assert len(printers) == 1 @@ -102,7 +102,6 @@ def test_create_printer_collate(client: TestClient) -> None: """POST /printers with collate not sent (unchecked) sets collate=False.""" from imptune.db.models import Printer - # FIXME: POST /printers will return 303 after Task 2 — update status_code assertion then resp = client.post( "/printers", data={ @@ -111,8 +110,9 @@ def test_create_printer_collate(client: TestClient) -> None: "port_name": "IP_192_168_1_104", # collate intentionally omitted (unchecked checkbox) }, + follow_redirects=False, ) - assert resp.status_code == 200 + assert resp.status_code == 303 printers = list(Printer.select().where(Printer.name == "No Collate Printer")) assert len(printers) == 1 @@ -142,7 +142,6 @@ def test_printer_grouped_by_client(client: TestClient) -> None: contoso = contoso_list[0] # Create printer assigned to that client - # FIXME: POST /printers will return 303 after Task 2 — update status_code assertion then resp = client.post( "/printers", data={ @@ -151,8 +150,9 @@ def test_printer_grouped_by_client(client: TestClient) -> None: "port_name": "IP_10_0_0_1", "client_id": str(contoso.id), }, + follow_redirects=False, ) - assert resp.status_code == 200 + assert resp.status_code == 303 # GET /printers should show "Contoso" as a group header page = client.get("/printers") @@ -242,7 +242,6 @@ def test_delete_printer(client: TestClient) -> None: from imptune.db.models import Printer # Create a printer - # FIXME: POST /printers will return 303 after Task 2 — update status_code assertion then resp = client.post( "/printers", data={ @@ -250,8 +249,9 @@ def test_delete_printer(client: TestClient) -> None: "ip_address": "192.168.1.200", "port_name": "IP_192_168_1_200", }, + follow_redirects=False, ) - assert resp.status_code == 200 + assert resp.status_code == 303 # Find its ID printers = list(Printer.select().where(Printer.name == "To Delete")) diff --git a/tests/test_printer_form.py b/tests/test_printer_form.py index d88253a..2eb6a76 100644 --- a/tests/test_printer_form.py +++ b/tests/test_printer_form.py @@ -3,44 +3,39 @@ from fastapi.testclient import TestClient def test_printer_form_has_inline_driver_upload(client: TestClient) -> None: - """GET /printers renders printer form with: + """GET /printers/new renders printer form with: - Driver must have id=\"printer-form-driver-select\" " + 'Printer form driver