fix(09-01): resolve driver upload 500 and add HTMX OOB refresh path

- Import Form from fastapi for mixed multipart + form field support
- Add caller: str = Form('') parameter to upload_driver handler
- Capture new_driver from Driver.get_or_create() return tuple
- Branch on caller == 'printer_form' to emit OOB-enabled response
- Create partials/driver_upload_with_oob.html with primary driver_list
  include + hx-swap-oob select targeting id=printer-form-driver-select
- New driver is auto-selected via new_driver_id context variable

All 13 driver upload tests pass including 4 new OOB contract tests.
Full non-e2e suite: 111 passed.
This commit is contained in:
2026-04-13 10:50:27 +02:00
parent c6fe284e28
commit 10ee09a5aa
2 changed files with 31 additions and 3 deletions
+20 -3
View File
@@ -6,7 +6,7 @@ import json
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from fastapi import APIRouter, Request, UploadFile from fastapi import APIRouter, Form, Request, UploadFile
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
@@ -33,7 +33,11 @@ def _error_response(message: str, status_code: int = 400) -> HTMLResponse:
@router.post("/upload", response_class=HTMLResponse) @router.post("/upload", response_class=HTMLResponse)
def upload_driver(request: Request, file: UploadFile) -> HTMLResponse: def upload_driver(
request: Request,
file: UploadFile,
caller: str = Form(""),
) -> HTMLResponse:
"""Accept a driver ZIP, parse its INF, persist via DriverStore + Peewee ORM. """Accept a driver ZIP, parse its INF, persist via DriverStore + Peewee ORM.
Returns an HTMX partial (partials/driver_list.html) on success, or an Returns an HTMX partial (partials/driver_list.html) on success, or an
@@ -86,7 +90,7 @@ def upload_driver(request: Request, file: UploadFile) -> HTMLResponse:
sha256 = store.save(data) sha256 = store.save(data)
# Upsert Driver record (no duplicate if same SHA256) # Upsert Driver record (no duplicate if same SHA256)
Driver.get_or_create( new_driver, _created = Driver.get_or_create(
sha256=sha256, sha256=sha256,
defaults={ defaults={
"original_filename": filename, "original_filename": filename,
@@ -105,6 +109,19 @@ def upload_driver(request: Request, file: UploadFile) -> HTMLResponse:
names = json.loads(d.driver_desc) if d.driver_desc else [] names = json.loads(d.driver_desc) if d.driver_desc else []
driver_data.append({"driver": d, "names": names}) driver_data.append({"driver": d, "names": names})
# When called from the printer form, emit primary fragment + OOB select refresh
if caller == "printer_form":
return templates.TemplateResponse(
request=request,
name="partials/driver_upload_with_oob.html",
context={
"driver_data": driver_data,
"new_driver_id": new_driver.id,
"parsed": parsed,
},
)
# Default: existing behavior — driver list fragment only
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="partials/driver_list.html", name="partials/driver_list.html",
@@ -0,0 +1,11 @@
{% include "partials/driver_list.html" %}
<select name="driver_id" id="printer-form-driver-select" hx-swap-oob="true">
<option value="">-- No driver --</option>
{% for item in driver_data %}
<option value="{{ item.driver.id }}"
{% if item.driver.id == new_driver_id %}selected{% endif %}>
{{ item.driver.original_filename }} ({{ item.names | join(', ') }})
</option>
{% endfor %}
</select>