Files
ss-tools/specs/030-dataset-lifecycle-workspace/spec.md
busya ec6421de35 rename ss-tools to superset-tools across the entire project
- Replace all occurrences of 'ss-tools' with 'superset-tools' in 104 files
- Rename git bundle file ss-tools.bundle → superset-tools.bundle
- Update .gitignore pattern accordingly
- Preserve variable names (hasSsTools etc.) and code identifiers
2026-06-16 11:15:19 +03:00

29 KiB
Raw Blame History

Feature Specification: Dataset Lifecycle Workspace

Feature Branch: 030-dataset-lifecycle-workspace Created: 2026-05-19 Status: Draft Input: Feature 029 — Dataset Lifecycle Workspace (variant C)

Clarifications

Session 2026-05-19

  • Q: Stats Bar фильтрует клиентски или серверно? → A: Серверная фильтрация через query параметр. Бэкенд в ответе GET /api/datasets возвращает 4 stats-числа (total, unmapped_count, mapped_count, linked_count). При клике на метрику Stats Bar фронтенд отправляет запрос с ?filter=unmapped|mapped|linked|all и filter сбрасывает page на 1. Пагинация остаётся серверной; бэкенд фильтрует полный список перед пагинацией. Stats-числа считаются глобально (не зависят от фильтра).
  • Q: Каким запросом сохранять inline-edit описания колонок/метрик? → A: Два новых superset-tools эндпоинта: PUT /api/datasets/{id}/columns/{col_id}/description и PUT /api/datasets/{id}/metrics/{metric_id}/description (body: {description: "text"}). Бэкенд выполняет GET датасета из Superset, модифицирует одну колонку/метрику, PUT обратно полный объект. В Superset нет PATCH — только PUT с полным датасетом.
  • Q: Как обрабатывать конфликт одновременного редактирования? → A: Last-write-wins. Бэкенд всегда выполняет GET→modify→PUT без проверки версии. Последний сохранивший перезаписывает. Для описаний колонок/метрик это приемлемый риск. ETag/блокировки — out of scope.
  • Q: Параметр override_columns при PUT в Superset? → A: Всегда override_columns=false. Бэкенд получает полный датасет из GET, модифицирует только description нужной колонки, PUT обратно с override_columns=false. Superset сохраняет все колонки, не участвовавшие в payload.
  • Q: Как Stats Bar узнаёт об изменении counts после асинхронных задач? → A: WebSocket push. При завершении mapping/docs задачи сервер отправляет событие dataset.updated через существующий WebSocket. Фронтенд по событию перезапрашивает GET /api/datasets и обновляет Stats Bar + карточки автоматически.

User Scenarios & Testing

User Story 1 — Stats-Driven Overview & Filtering (Priority: P1)

Пользователь заходит на страницу датасетов и видит 4 ключевые метрики в Stats Bar вверху. Каждая метрика кликабельна как фильтр списка. Метрики дают мгновенный обзор состояния датасетов и позволяют быстро отфильтровать те, которые требуют внимания.

Why this priority: Stats bar — первое, что видит пользователь. Он заменяет текущую пустую область над таблицей и даёт моментальный actionable insight. Без него страница неотличима от текущей реализации.

Independent Test: Перейти на /datasets, проверить что каждая из 4 метрик отображается и клик по ней фильтрует список.

Acceptance Scenarios:

  1. Given пользователь видит страницу датасетов с загруженными данными, When страница загружена, Then Stats Bar показывает 4 метрики: Все (total), Без маппинга, С маппингом, С дашбордами
  2. Given Stats Bar отображается, When пользователь кликает на "Без маппинга", Then список фильтруется до датасетов с mapped_fields.mapped === 0, метрика подсвечена
  3. Given Stats Bar отображается, When пользователь кликает на уже активный фильтр, Then фильтр сбрасывается и показываются все датасеты
  4. Given API возвращает ошибку при загрузке, When страница рендерится, Then Stats Bar показывает 0 для всех метрик или состояние ошибки

User Story 2 — Split-View с карточками и детальной панелью (Priority: P1)

Пользователь видит split-view: слева (40%) — список датасетов в виде карточек с прогресс-баром маппинга, справа (60%) — детальный просмотр выбранного датасета без перезагрузки страницы. На мобильных (< 768px) список занимает весь экран, а детали открываются как slide-over.

Why this priority: Split-view — ядро workspace. Без него нет смысла в Stats Bar и inline-редактировании. Это главное UX-изменение относительно текущей таблицы + отдельной страницы деталей.

Independent Test: Выбрать датасет в левой панели, убедиться что правая панель показывает его детали без перезагрузки страницы.

Acceptance Scenarios:

  1. Given страница загружена, When список датасетов отрендерен, Then карточки отображаются в левой панели (40%), правая панель показывает placeholder "Выберите датасет"
  2. Given список загружен, When пользователь кликает на карточку датасета, Then правая панель загружает детали выбранного датасета (имя, схема, колонки, метрики, дашборды)
  3. Given датасет выбран в правой панели, When пользователь кликает на другую карточку, Then правая панель обновляется без полной перезагрузки страницы
  4. Given ширина экрана < 768px, When пользователь кликает на карточку, Then правая панель открывается как slide-over поверх списка
  5. Given slide-over открыт на мобильном, When пользователь кликает "Назад" или вне slide-over, Then slide-over закрывается, возвращаясь к списку

User Story 3 — Карточки датасетов с прогресс-баром (Priority: P2)

Каждая карточка в левой панели содержит: имя таблицы, схему, количество колонок, количество метрик, прогресс-бар маппинга (mapped/total в процентах), кнопки быстрых действий (Маппинг, Доки). Прогресс-бар визуально показывает состояние маппинга.

Why this priority: Карточки — основной навигационный элемент. Без них split-view бесполезен, но технически карточки зависят от структуры split-view (US2).

Independent Test: Проверить что карточка содержит все обязательные поля и прогресс-бар правильно отражает mapped/total.

Acceptance Scenarios:

  1. Given список датасетов загружен, When карточки отрендерены, Then каждая карточка показывает: имя таблицы, схему, кол-во колонок, кол-во метрик, прогресс-бар
  2. Given датасет имеет mapped_fields: {total: 10, mapped: 7}, When карточка рендерится, Then прогресс-бар заполнен на 70% зелёным/жёлтым/синим в зависимости от процента
  3. Given датасет без метрик (metric_count = 0), When карточка рендерится, Then кол-во метрик показывает "0" или скрыто
  4. Given карточка отображается, When пользователь нажимает кнопку "Маппинг", Then открывается модалка маппинга колонок для этого датасета

User Story 4 — Детальная панель с колонками и метриками (Priority: P2)

Правая панель показывает: хедер датасета (полное имя, схема, кол-во колонок/метрик, связанные дашборды, дата создания), секцию "Колонки" (таблица с column_name, verbose_name, type, description, кнопка inline-редактирования), секцию "Метрики" (таблица с metric_name, expression, verbose_name, description, кнопка inline-редактирования), связанные дашборды.

Why this priority: Детальная панель — вторая половина split-view. Без неё нет рабочего пространства, но технически зависит от US2.

Independent Test: Выбрать датасет с метриками и колонками, проверить что обе секции отображаются корректно.

Acceptance Scenarios:

  1. Given датасет выбран, When правая панель загружена, Then хедер показывает: table_name, schema, column_count, metric_count, linked_dashboard_count, created_on
  2. Given у колонки есть verbose_name, When секция колонок рендерится, Then verbose_name показывается жирным, column_name не показывается
  3. Given у колонки нет verbose_name, When секция колонок рендерится, Then column_name показывается курсивом как fallback
  4. Given у колонки/метрики нет description, When строка рендерится, Then показывается "✏️ Добавить описание" как action prompt
  5. Given у метрики нет description, When строка метрики рендерится, Then под именем показывается expression как подсказка

User Story 5 — Inline-редактирование описаний (Priority: P3)

Пользователь может редактировать description колонок и метрик прямо в детальной панели. Клик по ✏️ на строке открывает textarea, пользователь вводит текст, сохраняет через API. Изменения применяются без перезагрузки страницы.

Why this priority: Inline-редактирование — ключевая фича workspace, но она менее критична чем базовая навигация и просмотр. Зависит от US4 (детальная панель) и бэкенд-эндпоинта.

Independent Test: Кликнуть ✏️ на колонке, ввести описание, сохранить — проверить что описание обновилось и отображается.

Acceptance Scenarios:

  1. Given строка колонки без описания, When пользователь кликает "✏️ Добавить описание", Then появляется textarea с кнопками "Сохранить" и "Отмена"
  2. Given textarea открыта, When пользователь вводит текст и нажимает "Сохранить", Then описание колонки сохраняется через PUT /api/datasets/{id}/columns/{col_id}/description, описание метрики — через PUT /api/datasets/{id}/metrics/{metric_id}/description; textarea закрывается, описание отображается
  3. Given textarea открыта, When пользователь нажимает "Отмена", Then textarea закрывается без изменений
  4. Given сохранение не удалось (ошибка API), When пользователь нажимает "Сохранить", Then показывается сообщение об ошибке, textarea остаётся открытой для повторной попытки
  5. Given строка колонки с существующим описанием, When пользователь кликает ✏️, Then textarea открывается с предзаполненным текстом описания

User Story 6 — Bulk-режим с нижней панелью (Priority: P3)

Чекбоксы на карточках позволяют выбрать несколько датасетов. При выборе 2+ датасетов появляется нижняя панель с bulk-действиями: "Маппинг колонок" и "Генерация документации". Модалки bulk-маппинга и генерации доков переиспользуются из текущей реализации.

Why this priority: Bulk-режим уже существует в текущей реализации, его нужно адаптировать под карточки в split-view. Менее приоритетно чем базовый просмотр.

Independent Test: Выбрать 3 датасета через чекбоксы, проверить что нижняя панель появляется и кнопки работают.

Acceptance Scenarios:

  1. Given карточки отображаются с чекбоксами, When пользователь отмечает 2+ датасета, Then нижняя панель появляется с кнопками "Маппинг колонок" и "Генерация документации"
  2. Given нижняя панель видна, When пользователь снимает выделение со всех датасетов, Then панель исчезает
  3. Given нижняя панель видна, When пользователь нажимает "Маппинг колонок", Then открывается модалка маппинга (переиспользованная из текущей реализации)
  4. Given выбраны датасеты с разных страниц пагинации, When нижняя панель отображается, Then счётчик показывает общее количество выбранных датасетов

User Story 7 — Отображение метрик в API и UI (Priority: P2)

Бэкенд API возвращает метрики в DatasetDetailResponse. Фронтенд отображает их в секции "Метрики" детальной панели.

Why this priority: Метрики — обязательная часть детальной панели (US4), но требуют изменений на бэкенде. Без бэкенд-поддержки UI нечего показывать.

Independent Test: Вызвать GET /api/datasets/{id}, проверить наличие полей metrics и metric_count в ответе, и что MetricItem содержит все обязательные поля.

Acceptance Scenarios:

  1. Given датасет имеет метрики в Superset API, When бэкенд получает GET /api/datasets/{id}, Then ответ содержит metrics: [...] и metric_count
  2. Given у метрики есть все поля Superset API, When бэкенд трансформирует ответ, Then MetricItem содержит: id, metric_name, expression, verbose_name, description, metric_type
  3. Given Superset API возвращает датасет без метрик, When бэкенд обрабатывает ответ, Then metrics: [] и metric_count: 0

Edge Cases

  • Что происходит когда API датасетов возвращает 503 (Superset недоступен)?
  • Как обрабатывается случай когда detail API для выбранного датасета падает, но список загружен?
  • Как ведёт себя split-view при ресайзе окна между десктопным и мобильным layout? → Решено: Выбранный датасет сохраняется. При сжатии до <768px открытая детальная панель становится slide-over (открытым). При расширении до ≥768px восстанавливается split-view; если slide-over был закрыт — правая панель показывает последний выбранный датасет или placeholder. (FR-034FR-036)
  • Что показывать в Stats Bar когда данные ещё не загружены (loading state)?
  • Как обрабатывать случай когда у датасета 0 колонок и 0 метрик?
  • Что происходит при сохранении inline-edit когда несколько пользователей редактируют один датасет (конфликт)? → Решено: last-write-wins. Последний сохранивший перезаписывает. ETag/блокировки — out of scope.
  • Как Stats Bar обновляет числа при переключении окружения — запрашивает новые counts?

Requirements

Functional Requirements

  • FR-001: Система MUST отображать Stats Bar с 4 метриками (Все, Без маппинга, С маппингом, С дашбордами) вверху страницы /datasets
  • FR-002: Каждая метрика в Stats Bar MUST быть кликабельной и действовать как фильтр списка датасетов. Фильтрация MUST выполняться серверно через query параметр ?filter=unmapped|mapped|linked|all с серверной пагинацией. Stats-числа считаются глобально.
  • FR-003: Система MUST отображать split-view layout: левая панель (40%) со списком карточек, правая панель (60%) с деталями выбранного датасета
  • FR-004: При ширине экрана < 768px правая панель MUST открываться как slide-over поверх списка
  • FR-005: Карточка датасета MUST содержать: имя таблицы, схему, количество колонок, количество метрик, прогресс-бар маппинга, кнопки быстрых действий
  • FR-006: Прогресс-бар MUST визуально отображать процент маппинга (mapped/total) с цветовой кодировкой: зелёный (100%), жёлтый (≥50%), синий (<50%)
  • FR-007: Правая панель MUST показывать хедер датасета (полное имя, схема, кол-во колонок/метрик, связанные дашборды, дата создания)
  • FR-008: Правая панель MUST содержать секцию "Колонки" с таблицей (column_name, verbose_name, type, description, ✏️ inline edit)
  • FR-009: Правая панель MUST содержать секцию "Метрики" с таблицей (metric_name, expression, verbose_name, description, ✏️ inline edit)
  • FR-010: При наличии verbose_name система MUST показывать его жирным; при отсутствии — показывать column_name/metric_name курсивом
  • FR-011: При отсутствии description система MUST показывать "✏️ Добавить описание" как action prompt
  • FR-012: Для метрик без описания система MUST показывать expression как подсказку под именем
  • FR-013: Inline-редактирование MUST поддерживать открытие textarea с кнопками "Сохранить"/"Отмена", с предзаполнением существующего описания
  • FR-014: Сохранение описания колонки MUST выполняться через PUT /api/datasets/{id}/columns/{col_id}/description (body: {description: "text"}). Сохранение описания метрики — PUT /api/datasets/{id}/metrics/{metric_id}/description. Бэкенд выполняет GET→modify→PUT в Superset.
  • FR-015: Карточки MUST содержать чекбоксы для bulk-выбора, при 2+ выбранных MUST появляться нижняя панель с bulk-действиями
  • FR-016: Bulk-действия "Маппинг колонок" и "Генерация документации" MUST переиспользовать существующие модалки
  • FR-017: DatasetDetailResponse на бэкенде MUST включать metrics: list[MetricItem] и metric_count: int
  • FR-018: MetricItem Pydantic DTO MUST содержать поля: id, metric_name, expression, verbose_name, description, metric_type
  • FR-019: SupersetClient.get_dataset_detail MUST извлекать dataset.get("metrics", []) из ответа Superset API
  • FR-020: Система MUST поддерживать i18n ключи (EN/RU) для: all, without_mapping, with_mapping, linked_to_dashboards, metrics, metric_count, no_selection, save_description, add_description
  • FR-021: Breadcrumb для /datasets MUST отображать "Датасеты / Datasets" (EN/RU), а не "Маппер колонок"
  • FR-022: Ответ GET /api/datasets MUST включать поля stats: total (int), unmapped_count (int), mapped_count (int), linked_count (int) — для отрисовки Stats Bar без дополнительных запросов
  • FR-023: PUT в Superset при сохранении inline-edit MUST выполняться с override_columns=false (query parameter) — предотвращает случайное удаление колонок, не упомянутых в payload
  • FR-024: По WebSocket-событию dataset.updated (отправляемому при завершении mapping/docs задачи) фронтенд MUST перезапрашивать GET /api/datasets и обновлять Stats Bar counts + карточки без ручного «Обновить»
  • FR-025: DatasetItem в list response MUST включать metric_count: int (количество метрик датасета) для отображения на карточках
  • FR-026: GET /api/datasets MUST поддерживать query параметр ?filter=unmapped|mapped|linked|all для серверной фильтрации перед пагинацией. При отсутствии параметра поведение эквивалентно filter=all.
  • FR-027: Stats Bar метрики MUST быть кнопками с aria-pressed, отражающим активное состояние фильтра
  • FR-028: Slide-over на мобильных MUST захватывать фокус при открытии и закрываться по Escape
  • FR-029: Ошибки сохранения inline-edit MUST анонсироваться через aria-live регион
  • FR-030: Skeleton-лоадеры MUST использовать aria-busy="true" на время загрузки
  • FR-031: Клик по чекбоксу карточки MUST только переключать выделение датасета, НЕ открывая детальную панель
  • FR-032: Клик по quick-action кнопке на карточке MUST открывать соответствующую модалку, НЕ открывая детальную панель
  • FR-033: Клик по телу карточки (вне чекбокса и quick-action кнопок) MUST выбирать датасет и открывать детальную панель
  • FR-034: Выбранный датасет MUST сохраняться при ресайзе между десктопным и мобильным layout без потери состояния
  • FR-035: При ресайзе с десктопа на мобильный: если детальная панель открыта, она MUST перейти в режим slide-over (открытый)
  • FR-036: При ресайзе с мобильного на десктоп: если slide-over закрыт, правая панель MUST показать placeholder или последний выбранный датасет
  • FR-037: Сохранение описания MUST принимать plain text, max 2000 символов. Пустая строка разрешена (очищает описание). Пробельные символы в начале/конце сохраняются без обрезки. 404 при отсутствии датасета/колонки/метрики. 502/503 при недоступности Superset.

Key Entities

  • StatsBarMetric: Метрика в Stats Bar — label, count, icon, filter_key, is_active
  • DatasetCard: Карточка в левой панели — id, table_name, schema, column_count, metric_count, mapped_progress (%), quick_actions[]
  • DatasetDetailPanel: Детальная панель — header_info, columns_table, metrics_table, linked_dashboards
  • MetricItem (новый): id, metric_name, expression, verbose_name, description, metric_type
  • ColumnRow: Строка в таблице колонок — column_name, verbose_name, type, description, is_editing (UI state)
  • MetricRow: Строка в таблице метрик — metric_name, expression, verbose_name, description, is_editing (UI state)
  • BulkActionPanel: Нижняя панель — selected_count, available_actions[], is_visible

Success Criteria

Measurable Outcomes

  • SC-001: Пользователь может за 1 клик перейти от Stats Bar к отфильтрованному списку датасетов
  • SC-002: Loading state для переключения между датасетами в split-view отображается в течение 100ms; контент деталей загружается согласно latency API (после получения ответа детали рендерятся без дополнительной задержки)
  • SC-003: Пользователь может отредактировать и сохранить описание колонки/метрики < 3 кликов
  • SC-004: Stats Bar корректно обновляет значения при переключении окружения
  • SC-005: Все существующие bulk-действия (маппинг, генерация доков) продолжают работать в новом UI
  • SC-006: Страница корректно адаптируется между десктопным split-view и мобильным slide-over без потери функциональности
  • SC-007: Все новые строки интерфейса имеют переводы на EN и RU