docs(02-02): complete driver upload endpoint plan
- SUMMARY.md with deviations, decisions, metrics - STATE.md: decisions logged, progress updated, session recorded - ROADMAP.md: phase 02 marked complete (2/2 plans with summaries) - REQUIREMENTS.md: DRV-01, DRV-03, DRV-04 marked complete
This commit is contained in:
@@ -9,10 +9,10 @@ Requirements for initial release. Each maps to roadmap phases.
|
|||||||
|
|
||||||
### Driver Management
|
### Driver Management
|
||||||
|
|
||||||
- [ ] **DRV-01**: User can upload a driver package (ZIP containing INF + supporting files)
|
- [x] **DRV-01**: User can upload a driver package (ZIP containing INF + supporting files)
|
||||||
- [x] **DRV-02**: System parses uploaded INF files and extracts valid driver names (DriverDesc)
|
- [x] **DRV-02**: System parses uploaded INF files and extracts valid driver names (DriverDesc)
|
||||||
- [ ] **DRV-03**: User can select driver name from parsed INF dropdown (no free-text)
|
- [x] **DRV-03**: User can select driver name from parsed INF dropdown (no free-text)
|
||||||
- [ ] **DRV-04**: Driver packages are persisted on Docker volume across container restarts
|
- [x] **DRV-04**: Driver packages are persisted on Docker volume across container restarts
|
||||||
- [x] **DRV-05**: System flags unused files in driver packages to help reduce package size
|
- [x] **DRV-05**: System flags unused files in driver packages to help reduce package size
|
||||||
|
|
||||||
### Printer Configuration
|
### Printer Configuration
|
||||||
@@ -85,10 +85,10 @@ Which phases cover which requirements. Updated during roadmap creation.
|
|||||||
|-------------|-------|--------|
|
|-------------|-------|--------|
|
||||||
| INFRA-01 | Phase 1 | Complete |
|
| INFRA-01 | Phase 1 | Complete |
|
||||||
| INFRA-02 | Phase 1 | Complete |
|
| INFRA-02 | Phase 1 | Complete |
|
||||||
| DRV-01 | Phase 2 | Pending |
|
| DRV-01 | Phase 2 | Complete |
|
||||||
| DRV-02 | Phase 2 | Complete |
|
| DRV-02 | Phase 2 | Complete |
|
||||||
| DRV-03 | Phase 2 | Pending |
|
| DRV-03 | Phase 2 | Complete |
|
||||||
| DRV-04 | Phase 2 | Pending |
|
| DRV-04 | Phase 2 | Complete |
|
||||||
| DRV-05 | Phase 2 | Complete |
|
| DRV-05 | Phase 2 | Complete |
|
||||||
| PRNT-01 | Phase 3 | Pending |
|
| PRNT-01 | Phase 3 | Pending |
|
||||||
| PRNT-02 | Phase 3 | Pending |
|
| PRNT-02 | Phase 3 | Pending |
|
||||||
|
|||||||
+11
-6
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.0
|
milestone: v1.0
|
||||||
milestone_name: milestone
|
milestone_name: milestone
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Completed 02-01-PLAN.md
|
stopped_at: Completed 02-02-PLAN.md
|
||||||
last_updated: "2026-04-10T09:57:42.006Z"
|
last_updated: "2026-04-10T10:03:29.688Z"
|
||||||
last_activity: "2026-04-10 — Plan 01-01 complete: Docker scaffold, FastAPI app shell, sidebar templates, 3-test green suite"
|
last_activity: "2026-04-10 — Plan 01-01 complete: Docker scaffold, FastAPI app shell, sidebar templates, 3-test green suite"
|
||||||
progress:
|
progress:
|
||||||
total_phases: 5
|
total_phases: 5
|
||||||
completed_phases: 1
|
completed_phases: 2
|
||||||
total_plans: 5
|
total_plans: 5
|
||||||
completed_plans: 4
|
completed_plans: 5
|
||||||
percent: 7
|
percent: 7
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -52,6 +52,7 @@ Progress: [░░░░░░░░░░] 7%
|
|||||||
*Updated after each plan completion*
|
*Updated after each plan completion*
|
||||||
| Phase 01-foundation P01-02 | 3 | 2 tasks | 7 files |
|
| Phase 01-foundation P01-02 | 3 | 2 tasks | 7 files |
|
||||||
| Phase 02 P01 | 3 | 1 tasks | 6 files |
|
| Phase 02 P01 | 3 | 1 tasks | 6 files |
|
||||||
|
| Phase 02-driver-management P02-02 | 3 | 2 tasks | 7 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -71,6 +72,10 @@ Recent decisions affecting current work:
|
|||||||
- [Phase 01-foundation]: init_db() added to lifespan (not @app.on_event) consistent with 01-01 established pattern
|
- [Phase 01-foundation]: init_db() added to lifespan (not @app.on_event) consistent with 01-01 established pattern
|
||||||
- [Phase 02]: optionxform=str on RawConfigParser preserves DriverDesc key casing; strings dict uses lowercase keys for case-insensitive token lookup
|
- [Phase 02]: optionxform=str on RawConfigParser preserves DriverDesc key casing; strings dict uses lowercase keys for case-insensitive token lookup
|
||||||
- [Phase 02]: RawConfigParser(strict=False) required for real INFs that have duplicate option keys within sections
|
- [Phase 02]: RawConfigParser(strict=False) required for real INFs that have duplicate option keys within sections
|
||||||
|
- [Phase 02-driver-management]: Always render <select> even for single-model drivers — uniform UI and simpler template logic
|
||||||
|
- [Phase 02-driver-management]: Dynamic DRIVERS_DIR read (import config module not constant) so monkeypatch works in integration tests
|
||||||
|
- [Phase 02-driver-management]: TestClient must use context manager for Starlette 0.46+ lifespan trigger (init_db)
|
||||||
|
- [Phase 02-driver-management]: HTMX errors: return HTMLResponse with driver-list div wrapper so HTMX outerHTML swap works for error states
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -83,6 +88,6 @@ None yet.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-10T09:57:42.003Z
|
Last session: 2026-04-10T10:03:29.685Z
|
||||||
Stopped at: Completed 02-01-PLAN.md
|
Stopped at: Completed 02-02-PLAN.md
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
---
|
||||||
|
phase: 02-driver-management
|
||||||
|
plan: "02"
|
||||||
|
subsystem: api
|
||||||
|
tags: [fastapi, htmx, jinja2, peewee, zipfile, sha256, dedup, inf-parser]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 02-01
|
||||||
|
provides: INF parser service (parse_inf, ParsedInf, _detect_encoding)
|
||||||
|
- phase: 01-foundation
|
||||||
|
provides: FastAPI app shell, DriverStore, Driver model, init_db, base templates
|
||||||
|
provides:
|
||||||
|
- POST /drivers/upload endpoint with ZIP validation, INF parsing, SHA256 dedup, Peewee persistence
|
||||||
|
- GET /drivers page with HTMX upload form and driver library table
|
||||||
|
- HTMX partial (partials/driver_list.html) returned on upload with select dropdown and unused-file notice
|
||||||
|
- Integration test suite (8 tests) for driver upload flow
|
||||||
|
affects: [03-printer-management, 04-package-generation]
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- HTMX outerHTML swap: upload endpoint returns partial HTML fragment replacing #driver-list div
|
||||||
|
- Dynamic config read: import imptune.config as _cfg and read _cfg.DRIVERS_DIR at call time for monkeypatch compatibility
|
||||||
|
- TDD workflow: RED (test commit) -> GREEN (impl commit) within same task
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- imptune/api/drivers.py
|
||||||
|
- imptune/templates/drivers.html
|
||||||
|
- imptune/templates/partials/driver_list.html
|
||||||
|
- tests/test_driver_upload.py
|
||||||
|
modified:
|
||||||
|
- imptune/api/pages.py
|
||||||
|
- imptune/main.py
|
||||||
|
- tests/conftest.py
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Always render <select> even for single-model drivers — simplifies template logic and consistent UI"
|
||||||
|
- "Dynamic DRIVERS_DIR read (import config module, not top-level constant) so monkeypatch works in tests"
|
||||||
|
- "TestClient context manager in conftest client fixture — required for lifespan/init_db to trigger in integration tests"
|
||||||
|
- "HTMX-friendly 400 error: return HTMLResponse with <div id='driver-list'> wrapper so HTMX can swap error inline"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "HTMX partial pattern: upload returns <div id='driver-list'> fragment; page has matching hx-target; outerHTML swap replaces entire div"
|
||||||
|
- "driver_data pattern: routes build list of dicts with {'driver': orm_obj, 'names': list[str]} to pre-parse JSON in Python rather than Jinja2"
|
||||||
|
- "Config monkeypatch: endpoints import config module (not constants) so test fixtures can override DRIVERS_DIR/DB_PATH"
|
||||||
|
|
||||||
|
requirements-completed: [DRV-01, DRV-03, DRV-04, DRV-05]
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 3min
|
||||||
|
completed: 2026-04-10
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 02 Plan 02: Driver Upload Endpoint Summary
|
||||||
|
|
||||||
|
**HTMX-driven driver ZIP upload with INF parsing, SHA256 dedup, Peewee persistence, and select dropdown returning 8/8 integration tests green**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** ~3 min
|
||||||
|
- **Started:** 2026-04-10T10:18:52Z
|
||||||
|
- **Completed:** 2026-04-10T10:22:00Z
|
||||||
|
- **Tasks:** 2 (Task 1 TDD: RED + GREEN; Task 2 templates completed inline)
|
||||||
|
- **Files modified:** 7
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- POST /drivers/upload: validates ZIP, finds INF, parses via INF parser service, saves via DriverStore (SHA256 content-addressed), upserts Driver record with json.dumps(driver_names) in driver_desc — full dedup on re-upload
|
||||||
|
- GET /drivers page renders upload form with HTMX attributes and existing driver library table
|
||||||
|
- HTMX partial (partials/driver_list.html): wraps content in `<div id="driver-list">` for outerHTML swap; shows unused-file notice with count and expandable list; renders driver names as `<select>` dropdown
|
||||||
|
- 8 integration tests written TDD-first (RED commit, then GREEN): page render, valid upload, non-ZIP 400, no-INF 400, select presence, DB persistence, dedup, unused files in response
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
1. **Test RED phase: failing integration tests** - `8ecfbf2` (test)
|
||||||
|
2. **Task 1 + Task 2: upload endpoint, templates, pages route, router registration** - `c648fc5` (feat)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `imptune/api/drivers.py` - POST /drivers/upload endpoint with full validation, INF parsing, DriverStore save, Peewee get_or_create
|
||||||
|
- `imptune/api/pages.py` - Added GET /drivers route with driver_data context
|
||||||
|
- `imptune/main.py` - Registered drivers.router
|
||||||
|
- `imptune/templates/drivers.html` - Drivers page extending base.html with HTMX upload form
|
||||||
|
- `imptune/templates/partials/driver_list.html` - HTMX swap target with table, select dropdown, unused-files notice
|
||||||
|
- `tests/test_driver_upload.py` - 8 integration tests covering all success and error paths
|
||||||
|
- `tests/conftest.py` - Fixed client fixture to use TestClient as context manager
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- Always render `<select>` even for single driver name — uniform UI and simpler template logic
|
||||||
|
- Read `_cfg.DRIVERS_DIR` dynamically (not top-level constant import) so test monkeypatching works
|
||||||
|
- `TestClient(app)` must be used as a context manager for Starlette 0.46+ to trigger lifespan and run `init_db()`
|
||||||
|
- HTMX errors: return `HTMLResponse` with `<div id='driver-list'>` wrapper at status 400 so HTMX can swap error into target area
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 3 - Blocking] TestClient context manager required for lifespan trigger**
|
||||||
|
- **Found during:** Task 1 GREEN (first test run)
|
||||||
|
- **Issue:** `TestClient(app)` without context manager does not run lifespan in Starlette 0.46+, so `init_db()` never called; DB remained deferred (None), causing `InterfaceError` on all ORM queries
|
||||||
|
- **Fix:** Changed conftest `client` fixture from `return TestClient(app)` to `with TestClient(app) as c: yield c`
|
||||||
|
- **Files modified:** tests/conftest.py
|
||||||
|
- **Verification:** All 48 tests pass including pre-existing health, static, DB, and parser tests
|
||||||
|
- **Committed in:** c648fc5 (Task 1 feat commit)
|
||||||
|
|
||||||
|
**2. [Rule 1 - Bug] Dynamic DRIVERS_DIR read to support monkeypatch**
|
||||||
|
- **Found during:** Task 1 GREEN (test_driver_persisted failure)
|
||||||
|
- **Issue:** `from imptune.config import DRIVERS_DIR` captured the value at import time; tests patching `cfg.DRIVERS_DIR` had no effect — files written to `/data/drivers` (production path) not the tmp dir
|
||||||
|
- **Fix:** Changed to `import imptune.config as _cfg` and use `_cfg.DRIVERS_DIR` at call time
|
||||||
|
- **Files modified:** imptune/api/drivers.py
|
||||||
|
- **Verification:** test_driver_persisted passes; file found in tmp_data_dir/drivers/
|
||||||
|
- **Committed in:** c648fc5 (Task 1 feat commit)
|
||||||
|
|
||||||
|
**3. [Rule 1 - Bug] Template always renders `<select>` for any non-empty names list**
|
||||||
|
- **Found during:** Task 1 GREEN (test_upload_returns_select failure)
|
||||||
|
- **Issue:** Template only showed `<select>` for multiple names; sample INF has 1 driver name, so test failed
|
||||||
|
- **Fix:** Changed template condition from `{% if item.names | length > 1 %}` to `{% if item.names %}`
|
||||||
|
- **Files modified:** imptune/templates/partials/driver_list.html
|
||||||
|
- **Verification:** test_upload_returns_select passes
|
||||||
|
- **Committed in:** c648fc5 (Task 1 feat commit)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Total deviations:** 3 auto-fixed (1 Rule 3 blocking, 2 Rule 1 bugs)
|
||||||
|
**Impact on plan:** All three fixes necessary for correct test isolation and behavior. No scope creep.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None beyond the three auto-fixed deviations above.
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None - no external service configuration required.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
- Driver upload feature fully functional: upload, parse, persist, dedup, UI feedback
|
||||||
|
- Driver records in SQLite with driver_desc (JSON list), architecture, inf_filename, has_cat_file
|
||||||
|
- Phase 03 (printer management) can reference drivers via Driver model and driver select dropdowns
|
||||||
|
- Phase 04 (package generation) can read persisted driver ZIPs from DriverStore using sha256
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 02-driver-management*
|
||||||
|
*Completed: 2026-04-10*
|
||||||
Reference in New Issue
Block a user