generated from john/python-template
11 KiB
11 KiB
Step 5: app.py + UI Pages (NiceGUI + FastAPI composition)
Objective
Implement the MVP user-facing application layer so users can:
- Upload a document from the UI
- Trigger Step 4 upload/job creation flow
- See live job lifecycle status (
queued,processing,transcribed,failed) - Open a job detail view to read transcript text or failure details
This step composes Steps 1–4 into a usable UI.
Architecture Summary (NiceGUI-aligned)
Step 5 uses a FastAPI app factory + lifespan orchestration and mounts/registers NiceGUI pages via explicit page modules.
Reference baseline: resource://skills/nicegui/document
Core architecture decisions
- App factory:
create_app() - Lifespan-managed resources: worker start/stop managed in startup/shutdown
- Modular pages: upload and jobs pages in separate modules (no monolithic UI file)
- Health endpoint: FastAPI-side
/healthz - UI composition: route pages stay modular and reusable shared shell/components live under
ui/componentsas needed - Styling architecture: shared CSS loaded once at startup; avoid ad-hoc per-page styling drift
- Dependency direction (one-way):
app->config/logging/db/worker/ui/apiui/pages->ui/components+servicesservices->db/models/providers- no reverse imports from services into UI/API
DB and AI stance (explicit)
- DB: already enabled (SQLModel + SQLite), session lifecycle remains request/service-scoped as built in prior steps.
- AI workflow: already in place via Step 3 transcription service + Step 4 worker; UI does not call provider SDK directly.
- Mounted docs: not in Step 5 scope; docs mounting remains disabled for MVP.
Async and responsiveness stance
- Prefer
async deffor page handlers and service boundaries when I/O is involved. - Keep UI handlers non-blocking (no blocking sleeps or synchronous long I/O calls).
- For long-running user actions, always provide explicit loading/progress/error states.
- Keep cancellation/timeout behavior explicit for refresh/poll operations where applicable.
Scope
In scope
src/transcription/app.pysrc/transcription/ui/upload_page.pysrc/transcription/ui/jobs_page.pysrc/transcription/ui/__init__.pysrc/transcription/api/health.py(or equivalent FastAPI health route module)- UI/app tests with MCP scaffold->fill flow
Out of scope
- Auth
- advanced filtering/search UX
- batch upload UX beyond MVP
- deployment/container hardening
Planned Deliverables
Source files
src/transcription/app.py(app factory + lifespan wiring)src/transcription/api/health.py(GET/healthz)src/transcription/ui/upload_page.py(upload flow)src/transcription/ui/jobs_page.py(status list + detail)src/transcription/ui/__init__.py(explicitregister_pages(...)export)src/transcription/ui/components/*(shared shell/navigation/status components if introduced)src/transcription/ui/static/*.css(optional shared CSS loaded once at startup)
Test files
tests/test_app.pytests/api/test_health.pytests/ui/test_pages_registration.pytests/ui/test_upload_page.pytests/ui/test_jobs_page.py
Implementation Plan + Checklist
Plan baseline and guardrails source: resource://skills/nicegui/document
Phase A — App factory and lifespan orchestration
- Create
create_app()insrc/transcription/app.py - Add FastAPI lifespan startup/shutdown handlers
- Startup responsibilities:
setup_logging()create_all()- ensure directories exist (
upload_dir,prompt_dir) - create worker stop event
- start worker background thread/task
- Shutdown responsibilities:
- signal stop event
- join/cleanup worker thread/task cleanly
- Register API router(s), including health route
- Register NiceGUI pages via explicit page registration function
- Load shared CSS once at startup (if present)
Phase B — FastAPI health endpoint
- Create
src/transcription/api/health.py - Add
GET /healthzreturning simple healthy payload - Wire route into app factory
Phase C — Upload page (ui/upload_page.py)
- Add upload route/page registration function
- Render file input accepting supported extensions
- On submit:
- show loading/progress state
- call
create_upload_job(filename, file_bytes, ...) - show success state with job reference/link
- On error:
- show user-safe error message
- restore ready UI state
- Ensure non-blocking I/O in UI event handlers; offload CPU-heavy work to worker path
- Make timeout/cancellation behavior explicit for any long-running action
Phase D — Jobs page (ui/jobs_page.py)
- Add jobs list route/page registration function
- Display jobs with status + timestamps
- Add job detail route/view
- Show transcript on success, error detail on failure
- Include explicit refresh action and loading state
- Ensure error states are surfaced to user and logged
- Keep refresh path async and bounded to avoid UI freeze
Phase E — UI registration module
- Update
src/transcription/ui/__init__.py - Export
register_pages(...) - Ensure each page module exports
register_page(...) - Keep page registration explicit and modular
Phase F — Shared components and style consistency
- Add
ui/componentsmodule only for reusable shell elements (header/nav/status chips), not page-local logic - Keep structural layout in Python; keep visual polish in shared CSS
- Avoid one-off styling duplication across upload/jobs pages
MCP Testing Workflow (Required)
Use these resources directly:
resource://catalog/prompts/pytest-scaffoldresource://prompts/pytest-scaffold/documentresource://catalog/prompts/pytest-fill-scaffoldresource://prompts/pytest-fill-scaffold/document
E1 — Scaffold tests first (structure only)
Target modules:
src/transcription/app.pysrc/transcription/api/health.pysrc/transcription/ui/upload_page.pysrc/transcription/ui/jobs_page.py
Scaffold test files:
tests/test_app.pytests/api/test_health.pytests/ui/test_pages_registration.pytests/ui/test_upload_page.pytests/ui/test_jobs_page.py
Scaffold constraints:
- class/method skeletons only
- one-line docstrings
- concise behavior-focused names
- no implementation assertions yet
Validation:
uv run pytest --collect-only -q
E2 — Fill scaffold tests
Fill constraints from MCP guidance:
- preserve scaffold class/method names and docstrings (locked baseline)
- one behavior target per method
- deterministic tests preferred
- minimal mocking; only nondeterministic boundaries
Stack:
fastapi(ormixedif needed for UI+DB fixture combination)
Suggested coverage:
tests/api/test_health.py
/healthzreturns success status and expected payload shape
tests/ui/test_pages_registration.py
- page registration wiring succeeds
- expected routes are present
tests/test_app.py
- startup path initializes runtime dependencies
- worker start is invoked on startup
- worker shutdown signal/cleanup is invoked on shutdown
tests/ui/test_upload_page.py
- upload action calls upload service
- success feedback displayed
- error feedback displayed for
UploadError - loading/progress state behavior covered
- timeout/cancellation behavior covered (if implemented)
tests/ui/test_jobs_page.py
- list renders job statuses
- detail shows transcript text for successful job
- detail shows error detail for failed job
- refresh/loading state behavior covered
Marker strategy:
unitfor pure helpers/state formattingintegrationfor app/page/service+DB contractsexternalnot required for default Step 5 lane
Async behavior assertions:
- long-running actions keep button/inputs in expected disabled state
- completion/failure returns controls to ready state
Validation Sequence (strict)
uv run pytest --collect-only -quv run pytest -m unit -q(if unit tests touched)uv run pytest tests/api/test_health.py -quv run pytest tests/ui/test_pages_registration.py -quv run pytest tests/test_app.py -quv run pytest tests/ui/test_upload_page.py -quv run pytest tests/ui/test_jobs_page.py -quv run pytest -q
Guardrails (NiceGUI + MVP)
- Do not collapse pages into one file.
- Do not use implicit global side effects for runtime wiring.
- Keep UI responsive with explicit loading/progress/error states.
- Do not block UI handlers with synchronous long I/O.
- Do not place provider SDK calls in UI handlers.
- Keep dependency direction one-way and maintainable.
- Keep shared UI in
ui/components; keep service logic out of page modules.
Definition of Done
- App factory + lifespan are in place
- Health endpoint exists and is tested
- Upload page creates queued jobs through service boundary
- Jobs list/detail pages render status/transcript/failure data
- Worker lifecycle is started/stopped by app lifespan
- Async UI states (loading/success/error) are deterministic and tested
- Scaffold->fill testing flow completed and validated
- Full suite passes:
uv run pytest -q
Completion Checks (NiceGUI skill aligned)
- Uses app factory and FastAPI lifespan
- Pages are modularized (not single-file UI)
- Health endpoint exists on FastAPI side
- Dependency direction is clean and one-way
- Async-first guidance is applied where I/O exists, with explicit non-blocking UX states
- DB/AI/docs decisions are explicit and reflected in structure
- Plan references baseline URI:
resource://skills/nicegui/document
PR Checklist (Integrated)
Implementation
app.pyapp factory + lifespan implemented- FastAPI health route (
/healthz) implemented ui/upload_page.pyimplementedui/jobs_page.pyimplementedui/__init__.pyexplicit page registration implemented- Worker startup/shutdown managed by lifespan
Testing (MCP-compliant)
- Scaffold phase completed first for all Step 5 tests
--collect-onlypassed on scaffolds- Fill phase completed without renaming/re-nesting scaffolded tests
- Marker decisions documented (
unitvsintegration) - Targeted tests passed
- Full suite passed
Evidence
- Validation command outputs captured
- Files created/updated listed
- MCP prompt resources referenced in implementation notes
- Any residual risks/questions documented