--- phase: 03-printer-configuration plan: 02 type: execute wave: 2 depends_on: ["03-01"] files_modified: - tests/test_printer_crud.py - imptune/api/printers.py - imptune/api/pages.py - imptune/templates/partials/printer_detail.html autonomous: true requirements: - PRNT-10 must_haves: truths: - "User can open a saved printer config and see all fields pre-populated" - "User can see the associated driver info on the detail page" - "A regenerate button is visible (disabled/placeholder until Phase 4)" artifacts: - path: "imptune/templates/partials/printer_detail.html" provides: "Printer detail view with all fields and driver info" - path: "imptune/api/printers.py" provides: "GET /printers/{id} detail endpoint" key_links: - from: "imptune/templates/partials/printer_list.html" to: "/printers/{id}" via: "printer name link in list row" pattern: "href.*printers.*id" - from: "imptune/api/printers.py" to: "imptune/db/models.py" via: "Printer.get_by_id with driver FK access" pattern: "Printer\\.get_by_id|printer\\.driver" --- Implement the printer detail/edit page so saved configs can be retrieved and prepared for regeneration. Purpose: PRNT-10 requires that a user can open a saved printer config and regenerate its package without re-uploading drivers. Phase 3's scope is: the config is fully retrievable, driver FK is intact, and a "Regenerate" button exists (placeholder until Phase 4 delivers script generation). This also adds printer name links in the list for navigation. Output: GET /printers/{id} detail page with pre-populated fields, driver info display, and regeneration placeholder button. @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-printer-configuration/03-RESEARCH.md @.planning/phases/03-printer-configuration/03-01-SUMMARY.md From imptune/api/printers.py (created in Plan 01): ```python router = APIRouter(prefix="/printers") def _render_printer_list(request: Request) -> HTMLResponse: """Returns partials/printer_list.html with grouped printers.""" def _error_response(message: str, status_code: int = 400) -> HTMLResponse: """HTMX-friendly error fragment.""" ``` From imptune/db/models.py: ```python class Printer(BaseModel): name = CharField() ip_address = CharField() port_name = CharField() client = ForeignKeyField(Client, null=True, backref="printers") driver = ForeignKeyField(Driver, null=True, backref="printers") duplex_mode = CharField(default="OneSided") color_mode = BooleanField(default=True) paper_size = CharField(default="A4") collate = BooleanField(default=True) class Driver(BaseModel): sha256 = CharField(unique=True) original_filename = CharField() driver_desc = CharField(null=True) # JSON list of driver names ``` From imptune/storage/driver_store.py: ```python class DriverStore: def get_path(self, sha256: str) -> Path: """Returns path to stored driver ZIP.""" ``` Task 1: Write failing test for printer detail page tests/test_printer_crud.py - test_printer_detail_shows_driver: Create a Driver record (via direct Peewee insert with sha256, original_filename, driver_desc=json.dumps(["HP Universal"])), create a Printer with driver FK set. GET /printers/{id} returns 200 with HTML containing printer name, IP, and "HP Universal" driver name. - test_printer_detail_not_found: GET /printers/9999 returns 404. - test_printer_detail_no_driver: Create a Printer with driver=None. GET /printers/{id} returns 200, HTML does not crash, shows "No driver assigned" or similar. Append three new tests to the existing `tests/test_printer_crud.py` file (created in Plan 01): - `test_printer_detail_shows_driver`: Use the `client` fixture. Create a Driver record directly via `Driver.create(sha256="abc123", original_filename="test.zip", size_bytes=1000, driver_desc=json.dumps(["HP Universal"]))`. Create a Printer with `driver=driver_obj`. GET `/printers/{printer.id}` and assert 200 status. Assert "HP Universal" appears in response text. Assert printer name appears. - `test_printer_detail_not_found`: GET `/printers/9999` returns 404. - `test_printer_detail_no_driver`: Create Printer with driver=None. GET `/printers/{printer.id}` returns 200. Assert "No driver assigned" or similar text in response. Run tests to confirm RED state (route does not exist yet). cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && python -m pytest tests/test_printer_crud.py::test_printer_detail_shows_driver tests/test_printer_crud.py::test_printer_detail_not_found tests/test_printer_crud.py::test_printer_detail_no_driver -x -q 2>&1 | head -20 Three new tests exist and fail (RED state). Existing tests still pass. Task 2: Implement printer detail route, template, and list navigation links imptune/api/printers.py, imptune/api/pages.py, imptune/templates/partials/printer_detail.html, imptune/templates/partials/printer_list.html **1. Add GET /printers/{printer_id} to `imptune/api/pages.py`:** - Route: `@router.get("/printers/{printer_id}", response_class=HTMLResponse)` - Handler: `def printer_detail(request: Request, printer_id: int):` - Query: `Printer.select(Printer, Client, Driver).join(Client, JOIN.LEFT_OUTER).switch(Printer).join(Driver, JOIN.LEFT_OUTER).where(Printer.id == printer_id).first()` - If not found: return HTMLResponse with 404 status and a simple error page. - If found: parse `printer.driver.driver_desc` (JSON) into driver_names list if driver exists. Pass `printer`, `driver_names`, and `driver` to template. - Render `printers.html` but with a detail block, OR create a dedicated detail template that extends base.html. Prefer: render `partials/printer_detail.html` inside the printers page layout. Actually, simpler approach: create a standalone detail page. - Render: `templates.TemplateResponse(request=request, name="printer_detail.html", context={"printer": printer, "driver_names": driver_names})` - This requires creating `imptune/templates/printer_detail.html` (NOT a partial — a full page). **2. Create `imptune/templates/printer_detail.html`:** Extends `base.html`. Content: ```

{{ printer.name }}

Configuration

IP Address
{{ printer.ip_address }}
Port Name
{{ printer.port_name }}
Duplex Mode
{{ printer.duplex_mode }}
Color Mode
{{ "Color" if printer.color_mode else "Grayscale" }}
Paper Size
{{ printer.paper_size }}
Collate
{{ "Yes" if printer.collate else "No" }}
Client
{{ printer.client.name if printer.client_id else "Unassigned" }}

Driver

{% if printer.driver_id %}
Package
{{ printer.driver.original_filename }}
Driver Name(s)
{{ driver_names | join(", ") }}
Architecture
{{ printer.driver.architecture or "Unknown" }}
{% else %}

No driver assigned

{% endif %}

Actions

Back to Printers
``` **3. Update `imptune/templates/partials/printer_list.html`:** Make printer names clickable: change the Name `` from plain text to `{{ p.name }}`. **4. Adjust file path:** The detail template is `imptune/templates/printer_detail.html` (full page, not partial). Run all tests to confirm GREEN state.
cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && python -m pytest tests/test_printer_crud.py -x -q && python -m pytest tests/ -v All tests pass (GREEN). GET /printers/{id} shows full printer config with driver info. "Regenerate Package" button is visible but disabled. Printer names in list are clickable links to detail page. Full test suite green.
- `pytest tests/test_printer_crud.py -x -q` — all tests pass including new detail tests - `pytest tests/ -v` — full suite green - GET /printers/{id} displays all printer fields and driver info - Printer names in list link to detail page - "Regenerate Package" button visible but disabled - 404 returned for nonexistent printer IDs - PRNT-10 verified: saved config retrievable with driver association intact, regeneration button present (placeholder) - Detail page shows all configured fields (name, IP, port, duplex, color, paper, collate, client, driver) - Driver info displayed from FK relationship (no re-upload needed) - Full test suite green with no regressions After completion, create `.planning/phases/03-printer-configuration/03-02-SUMMARY.md`