Files
ss-tools/specs/030-dataset-lifecycle-workspace/data-model.md
2026-05-20 09:59:11 +03:00

5.0 KiB

Data Model: Dataset Lifecycle Workspace

Entity Overview

GET /api/datasets            GET /api/datasets/{id}
─────────────────────────────────────────────────────────────
DatasetsResponse              DatasetDetailResponse
├── datasets: DatasetItem[]   ├── ... (existing fields)
│   ├── id                     ├── metrics: MetricItem[]
│   ├── table_name             │   ├── id
│   ├── schema                 │   ├── metric_name
│   ├── database               │   ├── expression
│   ├── mapped_fields          │   ├── verbose_name
│   └── last_task              │   ├── description
├── stats: StatsCounts         │   └── metric_type
│   ├── total                  └── metric_count
│   ├── unmapped_count
│   ├── mapped_count
│   └── linked_count
├── total, page, page_size,
│   total_pages (existing)
└── [unchanged pagination]

PUT /api/datasets/{id}/columns/{col_id}/description
PUT /api/datasets/{id}/metrics/{metric_id}/description
─────────────────────────────────────────────────────────────
Request:  ColumnDescriptionUpdate | MetricDescriptionUpdate
  { description: string }

Response: { success: true, description: string }

Backend Pydantic Schemas (in backend/src/api/routes/datasets.py)

MetricItem (new)

class MetricItem(BaseModel):
    id: int
    metric_name: str
    expression: str | None = None
    verbose_name: str | None = None
    description: str | None = None
    metric_type: str | None = None

StatsCounts (new)

class StatsCounts(BaseModel):
    total: int                              # all datasets
    unmapped_count: int                     # mapped_fields.mapped == 0
    mapped_count: int                       # mapped_fields.mapped > 0
    linked_count: int                       # linked_dashboard_count > 0

DatasetsResponse (modified)

class DatasetsResponse(BaseModel):
    datasets: list[DatasetItem]
    stats: StatsCounts                      # NEW
    total: int
    page: int
    page_size: int
    total_pages: int

DatasetDetailResponse (modified)

class DatasetDetailResponse(BaseModel):
    # ... existing fields unchanged ...
    metrics: list[MetricItem]               # NEW
    metric_count: int                       # NEW

ColumnDescriptionUpdate (new)

class ColumnDescriptionUpdate(BaseModel):
    description: str

MetricDescriptionUpdate (new)

class MetricDescriptionUpdate(BaseModel):
    description: str

Frontend Types (TypeScript / Svelte inferred)

interface StatsCounts {
    total: number;
    unmapped_count: number;
    mapped_count: number;
    linked_count: number;
}

interface MetricItem {
    id: number;
    metric_name: string;
    expression?: string;
    verbose_name?: string;
    description?: string;
    metric_type?: string;
}

// UI-only state types (not in API)
type ColumnRow = {
    column_name: string;
    verbose_name?: string;
    type?: string;
    description?: string;
    is_dttm: boolean;
    is_active: boolean;
    id: number;
    isEditing?: boolean;   // UI state for inline edit
};

type MetricRow = {
    metric_name: string;
    expression?: string;
    verbose_name?: string;
    description?: string;
    metric_type?: string;
    id: number;
    isEditing?: boolean;   // UI state for inline edit
};

type StatsBarFilter = 'all' | 'unmapped' | 'mapped' | 'linked' | null;

WebSocket Message

{
  "type": "dataset.updated",
  "payload": {
    "env_id": "ss-dev",
    "dataset_ids": ["242377ce-bdbd-42df-85da-ca1340a72fbb"]
  }
}

State Transitions

Stats Bar Tile States

Idle → (click tile) → Active (highlighted, list filtered)
Active → (click same tile) → Idle (filter cleared)
Active → (click different tile) → Active (new tile, list re-filtered)

Inline Edit States

Display → (click ✏️) → Editing (textarea visible)
Editing → (Save + API success) → Display (new description shown)
Editing → (Save + API error) → Editing (error message, textarea stays)
Editing → (Cancel) → Display (original description restored)

Detail Panel States

NoSelection → (click card) → Loading → Loaded
Loaded → (click different card) → Loading → Loaded
Loaded → (deselect / bulk mode) → NoSelection
Loading → (API error) → Error → (Retry) → Loading

Bulk Selection States

None → (check 1+ cards) → Selecting (bottom panel hidden for 1)
Selecting → (check 2+ cards) → BulkActive (bottom panel visible)
BulkActive → (uncheck all) → None
BulkActive → (click action) → Modal → (complete) → None