mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 14:11:12 +00:00
510df68bcf
Captures the full output of autodev existing-code Phase A through Step 4 (Code Testability Revision) for the Azaion UI workspace: - Step 1 Document: _docs/02_document/ (FINAL_report, architecture, glossary, components/, modules/, diagrams/, system-flows, module-layout) plus _docs/00_problem/ + _docs/01_solution/ + _docs/legacy/ + _docs/how_to_test + README. - Step 2 Architecture Baseline: architecture_compliance_baseline.md. - Step 3 Test Spec: _docs/02_document/tests/ (environment, test-data, blackbox/performance/resilience/security/ resource-limit tests, traceability-matrix), enum_spec_snapshot, expected_results/results_report.md (98 rows), plus the run-tests.sh + run-performance-tests.sh runners. - Step 4 Code Testability Revision: 01-testability-refactoring/ run dir (list-of-changes C01-C07, deferred_to_refactor, analysis/research_findings + refactoring_roadmap) and the 7 child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/ plus _dependencies_table.md. - _docs/_autodev_state.md pins the cursor at Step 4 / refactor Phase 4 entry so /autodev resumes cleanly. Epic AZ-447 (UI testability gates) tracks the 7 child tasks that will land in subsequent commits. Co-authored-by: Cursor <cursoragent@cursor.com>
167 lines
9.3 KiB
Markdown
167 lines
9.3 KiB
Markdown
# Azaion UI — Data Model
|
||
|
||
> Synthesis output of `/document` Step 3c. Consolidated from `src/types/index.ts`,
|
||
> per-component data sections, and the parent suite spec at
|
||
> `_docs/legacy/wpf-era.md` §8 + the suite-level docs (cross-referenced).
|
||
>
|
||
> The UI does NOT own a database. The entities below are the **contract shapes**
|
||
> the UI consumes from suite REST + SSE endpoints. The authoritative schema is
|
||
> server-side; this document captures the **client-side type expectations** and
|
||
> the **mismatches** between those expectations and the suite spec (enum drift,
|
||
> shape drift) for resolution at autodev Step 4.
|
||
|
||
## 1. Entities by component
|
||
|
||
| Entity | Component | Backing service | Wire shape |
|
||
|--------|-----------|-----------------|------------|
|
||
| `AuthUser` | `02_auth` | `admin/` | `{id, email, name, role, permissions[]}` |
|
||
| `User` | `08_admin` | `admin/` | `{id, name, email, role, isActive}` |
|
||
| `Aircraft` | `08_admin` / `09_settings` | `admin/` | `{id, model, type:'Plane'\|'Copter', isDefault}` |
|
||
| `Flight` | `05_flights` | `flights/` | `{id, name, createdDate, aircraftId\|null}` |
|
||
| `Waypoint` | `05_flights` | `flights/` | UI sends: `{id, flightId, name, latitude, longitude, order}`. Spec wants: `{Geopoint:{Lat,Lon,MGRS}, Source, Objective, OrderNum, Height}` — **drift, finding #20** |
|
||
| `Media` | `06_annotations` | `annotations/` | `{id, name, path, mediaType, mediaStatus, duration, annotationCount, waypointId, userId}` |
|
||
| `AnnotationListItem` | `06_annotations` | `annotations/` | `{id, mediaId, time, createdDate, userId, source, status, isSplit, splitTile, detections[]}` |
|
||
| `Detection` | `06_annotations` | `annotations/` (storage) + `detect/` (production) | `{id, classNum, label, confidence, affiliation, combatReadiness, centerX, centerY, width, height}` |
|
||
| `DetectionClass` | `08_admin` (write) + `06_annotations` (read) | `admin/` (write) + `annotations/` (read) | `{id, name, shortName, color, maxSizeM, photoMode}` |
|
||
| `DatasetItem` | `07_dataset` | `annotations/` | `{annotationId, imageName, thumbnailPath, status, createdDate, createdEmail, flightName, source, isSeed, isSplit}` |
|
||
| `ClassDistributionItem` | (currently unused — backs missing chart) | `annotations/` | `{classNum, label, color, count}` |
|
||
| `SystemSettings` | `09_settings` | `admin/` | `{id, name, militaryUnit, defaultCameraWidth, defaultCameraFoV, thumbnailWidth, thumbnailHeight, thumbnailBorder, generateAnnotatedImage, silentDetection}` |
|
||
| `DirectorySettings` | `09_settings` | `admin/` | `{id, videosDir, imagesDir, labelsDir, resultsDir, thumbnailsDir, gpsSatDir, gpsRouteDir}` |
|
||
| `CameraSettings` | `09_settings` | `admin/` | `{id, altitude, focalLength, sensorWidth}` |
|
||
| `UserSettings` | `09_settings` | `admin/` | `{id, userId, selectedFlightId, annotationsLeftPanelWidth, annotationsRightPanelWidth, datasetLeftPanelWidth, datasetRightPanelWidth}` |
|
||
| `PaginatedResponse<T>` | shared (`00_foundation`) | every list endpoint | `{items[], totalCount, page, pageSize}` |
|
||
|
||
## 2. Enums (numeric wire format)
|
||
|
||
> The suite uses **numeric** wire values for every enum. The UI types in `src/types/index.ts`
|
||
> match in *shape* but several have **wrong values** vs. spec. The state-of-the-world is captured
|
||
> in `state.json::notes[]` (2026-05-10 02:13Z entries) — Step 4 will fix the UI side.
|
||
|
||
| Enum | UI values today | Spec values | Status |
|
||
|------|-----------------|-------------|--------|
|
||
| `MediaType` | `None=0, Image=1, Video=2` | matches | ✓ |
|
||
| `MediaStatus` | `New=0, AiProcessing=1, AiProcessed=2, ManualCreated=3` | also has `None`, `Confirmed`, `Error` | **drift** — UI cannot render error state. Step 4 fix. |
|
||
| `AnnotationSource` | `AI=0, Manual=1` | matches numerically; spec doc shows strings (cross-repo doc fix replayed 2026-05-10) | ✓ (after parent-doc fix) |
|
||
| `AnnotationStatus` | `Created=0, Edited=1, Validated=2` | spec is `None=0, Created=10, Edited=20, Validated=30, Deleted=40` | **drift, severe** — wire payloads will be wrong. Step 4 PRIORITY. |
|
||
| `Affiliation` | `Unknown=0, Friendly=1, Hostile=2` | spec also has `None` | **drift** — Step 4. |
|
||
| `CombatReadiness` | `NotReady=0, Ready=1` | spec also has `Unknown` | **drift** — Step 4. |
|
||
|
||
## 3. Entity-relationship diagram
|
||
|
||
```mermaid
|
||
erDiagram
|
||
User ||--o{ Flight : "creates"
|
||
Flight ||--o{ Waypoint : "has"
|
||
Flight ||--o{ Media : "captures"
|
||
Media ||--o{ AnnotationListItem : "annotated by"
|
||
AnnotationListItem ||--o{ Detection : "contains"
|
||
DetectionClass ||--o{ Detection : "classifies (by classNum)"
|
||
Aircraft }o--o| Flight : "default for"
|
||
User ||--|| AuthUser : "session view of"
|
||
User ||--|| UserSettings : "preferences"
|
||
User ||--o{ DatasetItem : "validated by"
|
||
SystemSettings ||--|| Aircraft : "may default to"
|
||
|
||
Detection {
|
||
string id
|
||
int classNum "raw int including PhotoMode offset"
|
||
string label
|
||
float confidence
|
||
Affiliation affiliation
|
||
CombatReadiness combatReadiness
|
||
float centerX "normalized 0..1"
|
||
float centerY "normalized 0..1"
|
||
float width "normalized 0..1"
|
||
float height "normalized 0..1"
|
||
}
|
||
|
||
DetectionClass {
|
||
int id
|
||
string name
|
||
string shortName
|
||
string color "hex"
|
||
float maxSizeM "GSD constraint"
|
||
int photoMode "0=Regular, 1=Winter (+20), 2=Night (+40)"
|
||
}
|
||
|
||
Waypoint {
|
||
string id
|
||
string flightId
|
||
string name
|
||
float latitude "wire shape: drift — see finding 20"
|
||
float longitude
|
||
int order
|
||
}
|
||
|
||
AnnotationListItem {
|
||
string id
|
||
string mediaId
|
||
string time "video timestamp HH:MM:SS or null"
|
||
string createdDate "ISO 8601"
|
||
string userId
|
||
AnnotationSource source
|
||
AnnotationStatus status
|
||
bool isSplit
|
||
string splitTile "YOLO label string or null"
|
||
}
|
||
|
||
Media {
|
||
string id
|
||
string name
|
||
string path
|
||
MediaType mediaType
|
||
MediaStatus mediaStatus
|
||
string duration "HH:MM:SS or null"
|
||
int annotationCount
|
||
string waypointId
|
||
string userId
|
||
}
|
||
|
||
DatasetItem {
|
||
string annotationId
|
||
string imageName
|
||
string thumbnailPath
|
||
AnnotationStatus status
|
||
string createdDate
|
||
string createdEmail
|
||
string flightName
|
||
AnnotationSource source
|
||
bool isSeed
|
||
bool isSplit
|
||
}
|
||
```
|
||
|
||
## 4. Migration / schema-evolution strategy
|
||
|
||
The UI does NOT own a database, so there is no client-side migration to run. However:
|
||
|
||
- **Enum drift** above is effectively a "client-side schema migration" — every drifted enum must be aligned with the suite spec in Step 4 (or in the case of `AnnotationSource`, the parent-suite doc is what's wrong and was already fixed via cross-repo doc patch on 2026-05-10).
|
||
- **Backwards compatibility** is the suite's responsibility. The UI assumes the latest contract; if the suite needs to roll out a breaking change, it must coordinate with the UI version (typically by gating the change behind a feature flag in the admin service, then deploying both at once).
|
||
- **`UserSettings.{annotationsLeftPanelWidth, ...}`** — the type exists; the wire endpoint exists; the UI does not write these today (`useResizablePanel` finding #11). The fix is purely client-side wiring.
|
||
|
||
## 5. Seed data observations
|
||
|
||
The UI has no seed data of its own. Two sources of "default" data are observable:
|
||
|
||
1. **`FALLBACK_CLASS_NAMES`** (`11_class-colors`) — 12 generic English labels (Car, Person, Truck, …) shown only when admin classes failed to load. These are NOT a seed for the admin service; they are a defensive UI fallback only.
|
||
2. **`Constants.DefaultAnnotationClasses`** in the legacy WPF — referenced in `_docs/legacy/wpf-era.md §10`. The React UI does NOT carry an equivalent (admin classes are always fetched). Acceptable.
|
||
|
||
## 6. Validation rules (client-side)
|
||
|
||
| Field | Rule | Source | Defect |
|
||
|-------|------|--------|--------|
|
||
| Login email | non-empty | `LoginPage` | none observed |
|
||
| Login password | non-empty | `LoginPage` | none observed |
|
||
| Numeric settings | `parseInt(v) \|\| 0` | `SettingsPage` | clearing field silently writes 0 — finding B4 |
|
||
| Waypoint lat/lng | implicit (Leaflet bounds) | `FlightsPage` | no explicit clamp — Step 4 |
|
||
| Detection bbox normalized 0..1 | implicit (CanvasEditor scaling) | `CanvasEditor` | normalized-coordinate clamping mentioned in `_docs/legacy/wpf-era.md §10` — verify in Step 4 |
|
||
| Class 1–9 keyboard pick | `classes[idx + photoMode]` | `DetectionClasses` | ordering vs admin contract unverified — Step 4 |
|
||
| File upload size | server cap 500 MB (nginx) | `nginx.conf` | client does not pre-validate — Step 4 cosmetic |
|
||
|
||
## 7. Wire-format gotchas
|
||
|
||
- **`time` as a string** in `AnnotationListItem` (e.g., `"00:01:23.456"`) is parsed/formatted client-side; precision is millisecond. Used for video annotation overlay window math (50 ms before / 150 ms after — currently wrong, finding #6).
|
||
- **`splitTile` as a YOLO label string** is the raw `<class> <cx> <cy> <w> <h>` text, parsed client-side in `CanvasEditor.tsx`. Format mismatch is a Step 4 verification candidate.
|
||
- **`PaginatedResponse<T>` ceiling** — `pageSize=1000` is hardcoded for flights (finding B3). Other lists vary; no canonical default.
|
||
- **Date strings** — assumed ISO 8601; no timezone handling beyond what the browser's `Date` constructor implies. Step 4 verification when timezone bugs surface.
|