docs(superset): спека редизайна дашборда и handoff
- Зачем:
- дашборд-эталон показывал две одинаковые KPI-плитки; нужен честный состав метрик
на полных данных, зафиксированный до реализации.
- Что:
- docs/specs/2026-06-06-...: KPI Events/Users/Avg per Visit/Conversion, Top Pages → Funnel, триаж чартов.
- .scratch/handoffs/2026-06-06-...: handoff для реализации в новой сессии.
- Проверка:
- числа спеки сверены с прямым запросом в ClickHouse на полном датасете.
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
# Handoff: реализация редизайна Superset-дашборда
|
||||||
|
|
||||||
|
Дата: 2026-06-06 · Язык сессии: русский · Понять-режим был включён (можно не продолжать)
|
||||||
|
|
||||||
|
## Источник истины
|
||||||
|
|
||||||
|
**Сначала прочитать спеку:** [`docs/specs/2026-06-06-superset-dashboard-redesign.md`](../../docs/specs/2026-06-06-superset-dashboard-redesign.md)
|
||||||
|
— там весь дизайн (проблема, числа данных, триаж чартов, состав KPI, решения, риски,
|
||||||
|
критерии проверки). Этот handoff — только «как возобновить», не дублирует дизайн.
|
||||||
|
Доменные термины — [`CONTEXT.md`](../../CONTEXT.md).
|
||||||
|
|
||||||
|
## Решение одной строкой
|
||||||
|
|
||||||
|
KPI-полоса = `Total Events · Unique Users · Avg Events/Visit · Conversion to /confirmation`
|
||||||
|
(дубль «Unique Sessions» убрать); `Top Pages → Funnel`-чарт по страницам; различие
|
||||||
|
user/session — текстом, не двумя одинаковыми цифрами; мёртвые колонки purchases — выкинуть.
|
||||||
|
|
||||||
|
## Сделать ПЕРВЫМ делом
|
||||||
|
|
||||||
|
1. **Снять open questions из спеки** (без них реализация буксует):
|
||||||
|
- точная формула Conversion (доля визитов с ≥1 pageview `/confirmation`?);
|
||||||
|
- поддерживает ли Superset **4.1.2** `viz_type` воронки (иначе — упорядоченный bar);
|
||||||
|
- судьба `Events by Hour` (проверить, не пустой ли на полных данных).
|
||||||
|
2. **Перегрузить стенд на ПОЛНЫЕ данные** — сейчас в ClickHouse отладочный срез
|
||||||
|
(50 событий). Нужно: `make data` (без `LIMIT`) + `make transform`. На полных
|
||||||
|
данных: 1000 событий, 99 визитов, 99 пользователей.
|
||||||
|
|
||||||
|
## Где править и как прогонять
|
||||||
|
|
||||||
|
- Единственный файл реализации: `superset/create_dashboard.py`
|
||||||
|
(`CHARTS_CONFIG` / `DASHBOARD_ROWS` / `ROW_HEIGHTS`).
|
||||||
|
- Прогон: `make superset-dashboard` — идемпотентно (чарты по `slice_name`, дашборд
|
||||||
|
по `slug`, обновляются на месте). При переименовании чартов следить, чтобы не
|
||||||
|
плодились дубли.
|
||||||
|
|
||||||
|
## Проверка
|
||||||
|
|
||||||
|
- `GET /api/v1/dashboard/<id>/datasets` → 200; DQ Summary без `Columns missing in datasource`.
|
||||||
|
- Визуально: `playwright-cli` — логин формой `admin`/`admin` на `http://localhost:8088/login/`,
|
||||||
|
затем `goto .../superset/dashboard/ecommerce-analytics/`, `screenshot --filename=/tmp/x.png` (читать через Read).
|
||||||
|
Скриншоты — в `/tmp`. `.playwright-cli/` НЕ коммитить.
|
||||||
|
- Сверка чисел с ClickHouse: креды в `configs/default_user.xml` (default / `123456`),
|
||||||
|
`docker exec clickstream-ch-kafka-superset-demo-clickhouse-1 clickhouse-client --password 123456 -q "..."`.
|
||||||
|
(Через `docker exec printenv` креды НЕ дёргать — классификатор блокирует.)
|
||||||
|
|
||||||
|
## Синхронизировать доки ПРИ реализации
|
||||||
|
|
||||||
|
- `docs/SUPERSET_DASHBOARD.md` — раздел «Структура дашборда» (новый состав чартов/KPI).
|
||||||
|
- `docs/course/lessons/06_superset_bi.md` — убрать `LIMIT=50 make data`, синхронизировать состав/скриншоты.
|
||||||
|
|
||||||
|
## Git-гигиена
|
||||||
|
|
||||||
|
- Ветка `docs/advanced-clickstream-course`, на ней **параллельно пишет Codex** —
|
||||||
|
git строго **аддитивно**, не amend/rebase/reset чужих коммитов. `git add` только своих файлов.
|
||||||
|
- Артефакты этой сессии (если ещё не закоммичены): `CONTEXT.md`, `docs/adr/0001` (правка),
|
||||||
|
`docs/adr/0002`, `docs/adr/0003`, `docs/specs/2026-06-06-...`, `AGENTS.md` (правка), этот handoff.
|
||||||
|
|
||||||
|
## Suggested skills
|
||||||
|
|
||||||
|
`conventional-commits` (любой коммит) · `playwright-cli` (визуальная проверка) ·
|
||||||
|
`diagnose` (если чарт/датасет отвалится).
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# Редизайн KPI-полосы и состава чартов дашборда «E-commerce Analytics»
|
||||||
|
|
||||||
|
Дата: 2026-06-06
|
||||||
|
Статус: Accepted (не реализовано — код `superset/create_dashboard.py` правится
|
||||||
|
отдельным заходом на реализацию)
|
||||||
|
Связано: [`CONTEXT.md`](../../CONTEXT.md), [ADR-0002](../adr/0002-specs-as-durable-design-docs.md),
|
||||||
|
урок [`docs/course/lessons/06_superset_bi.md`](../course/lessons/06_superset_bi.md)
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Дашборд-эталон показывает две KPI-плитки — `Unique Users` и `Unique Sessions` — с
|
||||||
|
одинаковым числом (26 на срезе, 99 на полных данных), потому что в датасете
|
||||||
|
`user_domain_id ↔ click_id` строго 1:1. Две одинаковые цифры читаются как баг
|
||||||
|
расчёта и ничему не учат менти, который копирует дашборд как образец. Нужен
|
||||||
|
честный, копируемый состав KPI и чартов на полных данных.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- Дашборд — учебный эталон для менти. Требований обратной совместимости нет:
|
||||||
|
состав можно менять свободно, критерий — учебная ценность.
|
||||||
|
- Доменные термины зафиксированы в `CONTEXT.md`: **пользователь** =
|
||||||
|
`user_domain_id`, **визит/сессия** = `click_id`, **событие** = `event_id`.
|
||||||
|
- Источник данных — статический сид `data/*.jsonl`. Синтетический генератор
|
||||||
|
(ветка `feature/data-generator`) источником **не** является и семантику ломает
|
||||||
|
(см. `generator/KNOWN_ISSUES.md` на той ветке).
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- KPI-полоса из 4 различающихся честных метрик (без дубля одинаковых чисел).
|
||||||
|
- Превратить слабые места данных в учебные объекты, а не маскировать их.
|
||||||
|
- Консистентность с `CONTEXT.md` и уроком 6.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Не модернизируем **типы** виджетов (`pie`/`world_map`/`dist_bar` → ECharts) —
|
||||||
|
отдельный косметический заход.
|
||||||
|
- Не трогаем эмодзи в тайтлах, секции-заголовки, языковой винегрет.
|
||||||
|
- Не трогаем генератор.
|
||||||
|
- Не правим код `create_dashboard.py` в рамках этой спеки — это реализация.
|
||||||
|
|
||||||
|
## Current state
|
||||||
|
|
||||||
|
Полный датасет (`make data` без `LIMIT`):
|
||||||
|
|
||||||
|
```
|
||||||
|
события: 1000 | визиты (click_id): 99 | пользователи (user_domain_id): 99 (1:1)
|
||||||
|
avg событий на визит: 10.10 | bounce (визиты с 1 событием): 4/99 = 4.0%
|
||||||
|
event_type: 100% pageview (других типов НЕТ)
|
||||||
|
page_url_path — воронка: /home 426 → /product_a 236 + /product_b 157 →
|
||||||
|
/cart 93 → /payment 53 → /confirmation 35 (~8% в конец)
|
||||||
|
geo_country: 40 | device_type: 2 (Mobile/Computer) | utm_source: 5 | utm_medium: 4
|
||||||
|
```
|
||||||
|
|
||||||
|
Текущие чарты и триаж по учебной ценности:
|
||||||
|
|
||||||
|
| Чарт | Тип | Вердикт |
|
||||||
|
|---|---|---|
|
||||||
|
| Total Events, Unique Users | big_number_total | оставить (KPI) |
|
||||||
|
| Unique Sessions | big_number_total | **убрать** (дубль Users) |
|
||||||
|
| Avg Events/Session | big_number_total | оставить, переименовать `/Visit` |
|
||||||
|
| Top Pages | dist_bar | **апгрейд в Funnel** (центральный учебный объект) |
|
||||||
|
| UTM Effectiveness | table | оставить; **выкинуть колонки purchases/add_to_cart** (всегда 0) |
|
||||||
|
| Geography Map | world_map | оставить (40 стран; тип модернизировать отдельно) |
|
||||||
|
| Traffic by Device | pie | оставить |
|
||||||
|
| Events by Hour | line | **проверить на пустоту**, иначе дропнуть |
|
||||||
|
| Data Quality Summary | dist_bar | оставить |
|
||||||
|
|
||||||
|
## Options considered
|
||||||
|
|
||||||
|
- **A — честная воронка вовлечённости (стандартный KPI-strip).** 4 плитки
|
||||||
|
объём→охват→глубина→качество; различие user/session уходит в текст.
|
||||||
|
- **B — оставить обе плитки + Markdown-панель**, объясняющая равенство 1:1.
|
||||||
|
- **C — strip как форма модели:** воронка `Events → Visits → Users` вместо
|
||||||
|
бизнес-KPI; вырожденное 1:1 становится наглядным.
|
||||||
|
|
||||||
|
## Recommended approach
|
||||||
|
|
||||||
|
**A для KPI-полосы + идея C отдельным чартом-воронкой.** KPI-полоса —
|
||||||
|
стандартная и чистая (копируемый паттерн); иерархия модели подаётся отдельным
|
||||||
|
чартом, а `Top Pages` превращается в **Funnel** по страницам.
|
||||||
|
|
||||||
|
KPI-полоса:
|
||||||
|
|
||||||
|
```
|
||||||
|
📊 Total Events 1000 · 👤 Unique Users 99 · 📈 Avg Events/Visit 10.1 · 🎯 Conversion to /confirmation ~8%
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
- Конверсия через `page_url_path` честнее блёклого bounce (4%) и превращает
|
||||||
|
«всё pageview» из слабости в сильный урок: воронка реально затухает.
|
||||||
|
- Различие user/session учим текстом (`CONTEXT.md` + урок 6), а не двумя
|
||||||
|
одинаковыми числами — это убирает сигнал «баг».
|
||||||
|
- Стандартный KPI-strip ценнее для копирования, чем нестандартный вариант C;
|
||||||
|
но идею C сохраняем как отдельный наглядный чарт.
|
||||||
|
|
||||||
|
## Decisions and rejected alternatives
|
||||||
|
|
||||||
|
- **Данные: полный датасет** и для дашборда, и для урока 6. Срез `LIMIT=50`
|
||||||
|
отклонён: числа эталона разойдутся с тем, что менти получит у себя. Урок 6 —
|
||||||
|
убрать `LIMIT=50 make data`. (Срез `LIMIT` оставить в доке как явный приём
|
||||||
|
отладки, не как основной прогон. Замечание: ограничение `head -n` исходно
|
||||||
|
возникло, чтобы агентский контекст не давился полным jsonl — это ограничение
|
||||||
|
для агента, не для пайплайна.)
|
||||||
|
- **Убрать `Unique Sessions` из KPI** — дубль `Unique Users` при 1:1.
|
||||||
|
- **4-я KPI = Conversion**, не bounce. Bounce 4% отклонён как блёклый.
|
||||||
|
- **Top Pages → Funnel.** Воронка по страницам — главный учебный объект.
|
||||||
|
- **UTM-таблица: убрать колонки purchases/add_to_cart** — всегда 0, т.к.
|
||||||
|
`event_type` только `pageview`.
|
||||||
|
- **Гео/device/DQ не удаляем** — данные оправдывают ценность; модернизация их
|
||||||
|
типов виджетов — отдельно (non-goal здесь).
|
||||||
|
- **«Снести всё legacy»** на практике = убрать дубль-KPI + мёртвые колонки +
|
||||||
|
проверить Events by Hour. Инсайты не вырезаем — у большинства есть данные.
|
||||||
|
|
||||||
|
## Risks and mitigations
|
||||||
|
|
||||||
|
- **Funnel в Superset 4.1.2** — проверить доступность `viz_type` воронки до
|
||||||
|
реализации; при отсутствии — упорядоченный bar.
|
||||||
|
- **Формула Conversion** — определить точно и валидировать SQL (см. Open
|
||||||
|
questions).
|
||||||
|
- **Идемпотентность `create_dashboard.py`** при смене состава (чарты по
|
||||||
|
`slice_name`, дашборд по `slug`) — переименование `Unique Sessions`/`Top Pages`
|
||||||
|
не должно плодить дубли; проверить апдейт на месте.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- KPI-полоса: 4 разных числа, дубля нет.
|
||||||
|
- `GET /api/v1/dashboard/<id>/datasets` → 200; DQ Summary без
|
||||||
|
`Columns missing in datasource`.
|
||||||
|
- Визуальная проверка через playwright-cli (скриншот в `/tmp`).
|
||||||
|
- Числа на дашборде сходятся с прямым запросом в ClickHouse.
|
||||||
|
|
||||||
|
## Documentation impact
|
||||||
|
|
||||||
|
- `docs/SUPERSET_DASHBOARD.md` — обновить раздел «Структура дашборда»
|
||||||
|
(состав чартов/KPI/фильтров) **в момент реализации**, чтобы пользовательская
|
||||||
|
дока не описывала несуществующие чарты.
|
||||||
|
- `docs/course/lessons/06_superset_bi.md` — убрать `LIMIT=50`, синхронизировать
|
||||||
|
состав чартов и скриншоты.
|
||||||
|
- `CONTEXT.md` — уже отражает user/visit/event; правок не требует.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
- **Точная формула Conversion:** доля визитов с ≥1 pageview `/confirmation`, или
|
||||||
|
просмотры `/confirmation` / просмотры `/home`? Влияет на число (~8%) и на то,
|
||||||
|
что именно учим (визит-конверсия vs page-to-page).
|
||||||
|
- **Events by Hour** — оставляем ли (зависит от проверки на пустоту на полных
|
||||||
|
данных: разброс `event_ts` по часам).
|
||||||
|
- **Требования к будущему генератору**, вытекающие из упёртостей этих данных
|
||||||
|
(разнообразие `event_type`, возвраты пользователей → sessions>users,
|
||||||
|
реалистичная воронка) — при возврате к генератору перенести в его
|
||||||
|
`KNOWN_ISSUES.md`.
|
||||||
Reference in New Issue
Block a user