## Step 5: `app.py` + UI Pages (NiceGUI + FastAPI composition) ## Objective Implement the MVP user-facing application layer so users can: 1. Upload a document from the UI 2. Trigger Step 4 upload/job creation flow 3. See live job lifecycle status (`queued`, `processing`, `transcribed`, `failed`) 4. 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/components` as 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/api` - `ui/pages` -> `ui/components` + `services` - `services` -> `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 def` for 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.py` - `src/transcription/ui/upload_page.py` - `src/transcription/ui/jobs_page.py` - `src/transcription/ui/__init__.py` - `src/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` (explicit `register_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.py` - `tests/api/test_health.py` - `tests/ui/test_pages_registration.py` - `tests/ui/test_upload_page.py` - `tests/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()` in `src/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 /healthz` returning 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/components` module 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-scaffold` - `resource://prompts/pytest-scaffold/document` - `resource://catalog/prompts/pytest-fill-scaffold` - `resource://prompts/pytest-fill-scaffold/document` ## E1 — Scaffold tests first (structure only) Target modules: - `src/transcription/app.py` - `src/transcription/api/health.py` - `src/transcription/ui/upload_page.py` - `src/transcription/ui/jobs_page.py` Scaffold test files: - `tests/test_app.py` - `tests/api/test_health.py` - `tests/ui/test_pages_registration.py` - `tests/ui/test_upload_page.py` - `tests/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` (or `mixed` if needed for UI+DB fixture combination) Suggested coverage: ### `tests/api/test_health.py` - [ ] `/healthz` returns 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: - [ ] `unit` for pure helpers/state formatting - [ ] `integration` for app/page/service+DB contracts - [ ] `external` not 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 -q` - [ ] `uv run pytest -m unit -q` *(if unit tests touched)* - [ ] `uv run pytest tests/api/test_health.py -q` - [ ] `uv run pytest tests/ui/test_pages_registration.py -q` - [ ] `uv run pytest tests/test_app.py -q` - [ ] `uv run pytest tests/ui/test_upload_page.py -q` - [ ] `uv run pytest tests/ui/test_jobs_page.py -q` - [ ] `uv 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.py` app factory + lifespan implemented - [ ] FastAPI health route (`/healthz`) implemented - [ ] `ui/upload_page.py` implemented - [ ] `ui/jobs_page.py` implemented - [ ] `ui/__init__.py` explicit page registration implemented - [ ] Worker startup/shutdown managed by lifespan ### Testing (MCP-compliant) - [ ] Scaffold phase completed first for all Step 5 tests - [ ] `--collect-only` passed on scaffolds - [ ] Fill phase completed without renaming/re-nesting scaffolded tests - [ ] Marker decisions documented (`unit` vs `integration`) - [ ] 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 ---