feat(11-02): PATCH /printers/{id} route handler and updated _render_printer_list

- Add PATCH /{printer_id} route with validation and in-place update
- Optional ip_address/port_name fall back to existing values when not sent
- updated_at set explicitly on save (datetime.now(UTC))
- Update _render_printer_list to include clients and driver_data in context
- Import Driver at module level for use in both handler and helper
This commit is contained in:
2026-04-15 11:08:02 +02:00
parent c00ac80e6c
commit 4b212b6da9
+63 -3
View File
@@ -1,4 +1,4 @@
"""Printer CRUD API — POST /printers, DELETE /printers/{id}.""" """Printer CRUD API — POST /printers, DELETE /printers/{id}, PATCH /printers/{id}."""
from __future__ import annotations from __future__ import annotations
from collections import defaultdict from collections import defaultdict
@@ -9,7 +9,7 @@ from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from peewee import JOIN from peewee import JOIN
from imptune.db.models import Client, Printer from imptune.db.models import Client, Driver, Printer
router = APIRouter(prefix="/printers") router = APIRouter(prefix="/printers")
@@ -31,6 +31,8 @@ def _error_response(message: str, status_code: int = 400) -> HTMLResponse:
def _render_printer_list(request: Request) -> HTMLResponse: def _render_printer_list(request: Request) -> HTMLResponse:
"""Query printers with LEFT JOIN on client and render grouped partial.""" """Query printers with LEFT JOIN on client and render grouped partial."""
import json
query = ( query = (
Printer.select(Printer, Client) Printer.select(Printer, Client)
.join(Client, JOIN.LEFT_OUTER) .join(Client, JOIN.LEFT_OUTER)
@@ -41,10 +43,17 @@ def _render_printer_list(request: Request) -> HTMLResponse:
client_name = p.client.name if p.client_id else "Unassigned" client_name = p.client.name if p.client_id else "Unassigned"
grouped[client_name].append(p) grouped[client_name].append(p)
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( return templates.TemplateResponse(
request=request, request=request,
name="partials/printer_list.html", name="partials/printer_list.html",
context={"grouped": grouped}, context={"grouped": grouped, "clients": clients, "driver_data": driver_data},
) )
@@ -112,3 +121,54 @@ def delete_printer(request: Request, printer_id: int) -> HTMLResponse:
return _error_response(f"Printer {printer_id} not found.", status_code=404) return _error_response(f"Printer {printer_id} not found.", status_code=404)
return _render_printer_list(request) return _render_printer_list(request)
@router.patch("/{printer_id}", response_class=HTMLResponse)
def update_printer(
request: Request,
printer_id: int,
name: str = Form(...),
ip_address: str = Form(""),
port_name: str = Form(""),
duplex_mode: str = Form("OneSided"),
color_mode: str = Form(""),
paper_size: str = Form("A4"),
collate: str = Form(""),
client_id: str = Form(""),
driver_id: str = Form(""),
) -> HTMLResponse:
"""Update an existing printer configuration in-place."""
from datetime import UTC, datetime
printer = Printer.get_or_none(Printer.id == printer_id)
if printer is None:
return _error_response(f"Printer {printer_id} not found.", status_code=404)
name = name.strip()
ip_address = ip_address.strip() or printer.ip_address
port_name = port_name.strip() or printer.port_name
if not name:
return _error_response("Printer name is required.")
if not ip_address:
return _error_response("IP address is required.")
if not port_name:
return _error_response("Port name is required.")
if duplex_mode not in _VALID_DUPLEX:
return _error_response(f"Invalid duplex mode: {duplex_mode}.")
if paper_size not in _VALID_PAPER:
return _error_response(f"Invalid paper size: {paper_size}.")
printer.name = name
printer.ip_address = ip_address
printer.port_name = port_name
printer.duplex_mode = duplex_mode
printer.color_mode = color_mode == "on"
printer.paper_size = paper_size
printer.collate = collate == "on"
printer.client = int(client_id) if client_id.strip() else None
printer.driver = int(driver_id) if driver_id.strip() else None
printer.updated_at = datetime.now(UTC).replace(tzinfo=None)
printer.save()
return _render_printer_list(request)