Files
clickstream-ch-kafka-supers…/docs/specs/2026-06-06-superset-dashboard-redesign.md
T
ddadmin 1bec6bb8ad feat(superset): обновлен состав KPI и воронки дашборда
- Зачем:
  - нужно убрать дублирующий KPI Unique Sessions и сделать эталонный dashboard честнее для учебного анализа.
- Что:
  - обновлены KPI, добавлена Conversion to /confirmation и Page Funnel.
  - добавлена идемпотентная миграция старых chart names без дублей.
  - синхронизированы документация, урок 6 и спека редизайна.
  - добавлен handoff для продолжения работы в новой сессии.
- Проверка:
  - python3 -m py_compile superset/create_dashboard.py.
  - make superset-dashboard.
  - Superset metadata: dashboard_charts=10, obsolete_unique_sessions=0, page_funnel_type=funnel.
2026-06-06 17:00:02 +03:00

158 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Редизайн KPI-полосы и состава чартов дашборда «E-commerce Analytics»
Дата: 2026-06-06
Статус: Implemented (`superset/create_dashboard.py`, `docs/SUPERSET_DASHBOARD.md`,
`docs/course/lessons/06_superset_bi.md`)
Связано: [`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
- **Resolved: точная формула Conversion.** Используем page-funnel conversion:
`countIf(page_url_path = '/confirmation') / countIf(page_url_path = '/home')`.
На полном датасете это `35 / 426 = 8.2%`. Визитовая формула даёт 25.3% и
отклонена, потому что KPI должен совпадать с логикой chart `Page Funnel`.
- **Resolved: Events by Hour.** Оставляем: на полном датасете есть два часовых
бакета (`20 → 256`, `21 → 744`), график не пустой.
- **Resolved: Funnel в Superset 4.1.2.** MCP Context7 по `/apache/superset` не
дал точной строки `viz_type`; установленный Superset 4.1.2 проверен по bundled
example `Featured Charts/Funnel.yaml` и frontend assets. Используем
`viz_type: funnel`.
- **Требования к будущему генератору**, вытекающие из упёртостей этих данных
(разнообразие `event_type`, возвраты пользователей → sessions>users,
реалистичная воронка) — при возврате к генератору перенести в его
`KNOWN_ISSUES.md`.