After story 1

This commit is contained in:
2026-08-03 11:30:49 -04:00
commit 82590c392f
32 changed files with 4375 additions and 0 deletions
+216
View File
@@ -0,0 +1,216 @@
# STORY-07: Review Interface
## Epic
**E2: Routing & Review** — As an annotator, I can view low-confidence videos and label them.
## Related Requirements
| ID | Requirement |
|----|------|
| FR-05 | Manual review interface for low-confidence videos: Displays video + contributing frames/crops + model confidence; Supports binary labeling |
| FR-07 | Metadata logging & audit trail: Stores annotated labels for active learning |
| NFR-06 | Observability: Tracks review queue depth |
| TC-06 | Network Security: Internal LAN only; no auth required |
## Description
Implement a lightweight web-based review interface for annotating low-confidence videos. Display the video player, top-k contributing frames, model confidence scores, and allow annotators to toggle the ground truth label. Support CSV/JSON export of annotated data for active learning.
## Scope
### In Scope
- Lightweight web UI (Flask/FastAPI) serving on internal LAN
- Query DB for videos with routing_decision = REVIEW
- Video player with playback controls
- Display top-k contributing frames (highest confidence frames)
- Display model confidence scores per frame
- Binary label toggle (True/False — target class present or not)
- Label persistence to DB (review_queue table)
- CSV/JSON export of annotated data with ground truth
- Accessible via internal IP:Port (no auth, no SSL)
### Out of Scope
- Frame sampling (covered in STORY-03)
- Face detection (covered in STORY-04)
- Classification (covered in STORY-05)
- Confidence aggregation (covered in STORY-05)
- Active learning pipeline / model retraining (covered in STORY-08)
- Monitoring dashboards (covered in STORY-09)
## Deliverables
### 7.1 Review Backend
**File:** `src/review_api.py`
API Endpoints:
- `GET /api/review/queue` — List videos in review queue
- Query: `SELECT * FROM review_queue WHERE annotated = false ORDER BY created_at DESC`
- Pagination: 20 items per page
- Response: `{videos: [...], total: N, page: P, per_page: 20}`
- `GET /api/review/{video_id}` — Get video details for annotation
- Response: `{video_id, file_path, confidence_score, routing_decision, model_version, frame_count, contributing_frames: [{timestamp, crop_path, confidence}], video_duration}`
- `POST /api/review/{video_id}/label` — Submit annotation
- Body: `{ground_truth: true/false, notes: string (optional)}`
- Updates: `review_queue.annotated = true`, `review_queue.ground_truth = value`, `review_queue.annotated_at = NOW()`
- Response: `{status: 'annotated', video_id, ground_truth}`
- `GET /api/review/export` — Export annotated data
- Query params: `format=csv|json`, `annotated=true/false`, `date_from`, `date_to`
- Response: File download with annotated data
- `GET /api/review/stats` — Review queue statistics
- Response: `{total_in_queue: N, annotated_today: N, avg_confidence: F, confidence_distribution: {...}}`
### 7.2 Review Frontend
**File:** `ui/review/`
Pages:
- **Queue Page (`/`):** List of videos awaiting review
- Table columns: Video ID, File Path, Confidence Score, Model Version, Date Added, Actions (View)
- Sortable by confidence, date, file path
- Filter by confidence range, model version
- Pagination (20 items per page)
- **Annotation Page (`/review/{video_id}`):** Video annotation interface
- Video player with playback controls (HTML5 `<video>` element)
- Top-k contributing frames displayed as thumbnails (k=5 default)
- Confidence scores displayed per frame
- Label toggle button (True/False) with confirmation
- Optional notes field
- Submit button (saves to DB via API)
- Navigation: Previous/Next video in queue
### 7.3 Data Export
**File:** `src/review_export.py`
Features:
- **CSV Export:**
```csv
video_id,file_path,confidence_score,routing_decision,model_version,ground_truth,annotated_at,contributing_frames
12345,/data/input/video.mp4,0.62,REVIEW,v1.2.0,true,2026-08-03T10:30:00Z,"[{'timestamp': 30.0, 'crop_path': '/scratch/12345/crops/30000.jpg', 'confidence': 0.82}, ...]"
```
- **JSON Export:**
```json
[
{
"video_id": 12345,
"file_path": "/data/input/video.mp4",
"confidence_score": 0.62,
"routing_decision": "REVIEW",
"model_version": "v1.2.0",
"ground_truth": true,
"annotated_at": "2026-08-03T10:30:00Z",
"contributing_frames": [
{"timestamp": 30.0, "crop_path": "/scratch/12345/crops/30000.jpg", "confidence": 0.82}
]
}
]
```
- **Export Options:**
- Filter by annotation status (annotated/unannotated)
- Filter by date range
- Filter by model version
- Filter by ground truth label
- **Output Location:** `/data/output/reviews/`
### 7.4 UI Container
**File:** `ui/Dockerfile`
Base image: `python:3.10-slim`
Installed packages:
- Flask 3.0+ or FastAPI 0.100+ (lightweight web framework)
- Jinja2 3.1+ (template engine)
- PyMySQL (database connection for API)
- gunicorn (WSGI server)
### 7.5 Docker Compose Update
**File:** `docker-compose.yml` (update)
Add UI service:
```yaml
ui:
build:
context: ./ui
dockerfile: Dockerfile
ports:
- "5000:5000"
volumes:
- ./ui:/app/ui
environment:
- DB_HOST=mariadb
- DB_PORT=3306
- DB_NAME=videodetect
- DB_USER=videodetect
- DB_PASSWORD=${DB_PASSWORD}
depends_on:
- mariadb
networks:
- videodetect-network
```
### 7.6 Database Update
**File:** `db/schema.sql` (review_queue table — from STORY-01)
The `review_queue` table was defined in STORY-01. This story populates and queries it.
### 7.7 Configuration Updates
**File:** `config.yaml` (updates)
New fields:
```yaml
review_ui:
host: "0.0.0.0"
port: 5000
per_page: 20
top_k_frames: 5
export_path: /data/output/reviews
auth_enabled: false # per TC-06
ssl_enabled: false # per TC-06
```
## Acceptance Criteria
### Functional
- [ ] Review queue displays all videos with routing_decision = REVIEW and annotated = false
- [ ] Video player loads and plays the video correctly
- [ ] Top-k contributing frames are displayed as thumbnails with confidence scores
- [ ] Annotator can toggle label (True/False) and submit
- [ ] Submitted label is persisted to DB (review_queue table)
- [ ] Annotated videos are removed from the default queue view
- [ ] CSV export produces valid CSV with all required fields
- [ ] JSON export produces valid JSON with all required fields
- [ ] Export includes ground truth labels and contributing frame data
- [ ] UI is accessible via http://<server-ip>:5000 (no auth, no SSL)
- [ ] Pagination works correctly (20 items per page)
- [ ] Sort and filter operations work on the queue page
### Non-Functional
- [ ] Queue page loads in < 2 seconds (with 1000+ videos in queue)
- [ ] Video player loads in < 3 seconds
- [ ] Label submission completes in < 1 second
- [ ] Export of 1000 annotated videos completes in < 10 seconds
- [ ] UI uses < 100MB RAM at idle
- [ ] No authentication or SSL configured (per TC-06)
### Technical Constraints
- [ ] UI runs in Docker container (per TC-05)
- [ ] No reverse proxy configured
- [ ] No SSL certificates configured
- [ ] No authentication mechanism configured
- [ ] All API responses are JSON
- [ ] Database queries use parameterized statements
- [ ] Export files are UTF-8 encoded
## Dependencies
- **Prerequisites:** STORY-01 (Foundation — DB schema), STORY-05 (Classification — provides routing decisions)
- **Depends on:** None (can be built in parallel with STORY-04, STORY-05)
- **Enables:** STORY-08 (Active Learning — provides labeled training data)
## Risks & Mitigations
| Risk | Mitigation |
|------|--|
| Video playback in browser requires compatible format | Serve videos in web-compatible format (H.264 MP4); transcode if needed |
| Crop paths may not be accessible from UI container | Store crop paths in DB; serve via API endpoint |
| No auth means anyone on LAN can access | Acceptable per TC-06; document in security notes |
| Large review queue slows page loads | Implement server-side pagination; lazy load thumbnails |
## Estimated Effort
- **Sprint:** 7
- **Story Points:** 21
- **Dependencies:** STORY-01, STORY-05