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.
This commit is contained in:
@@ -0,0 +1,96 @@
|
|||||||
|
# Handoff: Superset dashboard redesign implemented
|
||||||
|
|
||||||
|
Дата: 2026-06-06 · Язык сессии: русский
|
||||||
|
|
||||||
|
## Статус
|
||||||
|
|
||||||
|
Редизайн Superset-дашборда реализован и закоммичен.
|
||||||
|
|
||||||
|
Коммит:
|
||||||
|
|
||||||
|
```text
|
||||||
|
04995af832743ed984c25af08a185f7abe0485f1
|
||||||
|
feat(superset): обновлен состав KPI и воронки дашборда
|
||||||
|
```
|
||||||
|
|
||||||
|
Рабочее дерево было чистым перед созданием этого handoff; сам handoff создан после коммита.
|
||||||
|
|
||||||
|
## Источники истины
|
||||||
|
|
||||||
|
- Спека: [`docs/specs/2026-06-06-superset-dashboard-redesign.md`](../../docs/specs/2026-06-06-superset-dashboard-redesign.md)
|
||||||
|
- Реализация: [`superset/create_dashboard.py`](../../superset/create_dashboard.py)
|
||||||
|
- Пользовательская документация: [`docs/SUPERSET_DASHBOARD.md`](../../docs/SUPERSET_DASHBOARD.md)
|
||||||
|
- Урок 6: [`docs/course/lessons/06_superset_bi.md`](../../docs/course/lessons/06_superset_bi.md)
|
||||||
|
- Предыдущий handoff: [`2026-06-06-superset-dashboard-redesign.md`](./2026-06-06-superset-dashboard-redesign.md)
|
||||||
|
|
||||||
|
## Что сделано
|
||||||
|
|
||||||
|
- KPI-полоса теперь:
|
||||||
|
`Total Events · Unique Users · Avg Events/Visit · Conversion to /confirmation`.
|
||||||
|
- `Unique Sessions` удалён из KPI и из Superset metadata как obsolete chart.
|
||||||
|
- `Avg Events/Session` переименован в `Avg Events/Visit` идемпотентно через `previous_slice_names`.
|
||||||
|
- `Top Pages` переименован в `Page Funnel` и переведён на `viz_type: funnel`.
|
||||||
|
- Conversion считается как page-funnel metric:
|
||||||
|
`countIf(page_url_path = '/confirmation') / countIf(page_url_path = '/home')`.
|
||||||
|
- UTM-таблица оставлена без мёртвых колонок `purchases` / `add_to_cart`.
|
||||||
|
- `Events by Hour` оставлен: на полном датасете есть два часовых бакета.
|
||||||
|
- Документация и урок 6 синхронизированы с полным датасетом и новым dashboard flow.
|
||||||
|
|
||||||
|
## Проверки
|
||||||
|
|
||||||
|
Выполнено:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make data
|
||||||
|
make transform
|
||||||
|
python3 -m py_compile superset/create_dashboard.py
|
||||||
|
make superset-dashboard
|
||||||
|
make superset-dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
Результаты:
|
||||||
|
|
||||||
|
- полный датасет в DM: `1000` events, `99` visits, `99` users;
|
||||||
|
- `Avg Events/Visit = 10.1`;
|
||||||
|
- `Conversion to /confirmation = 35 / 426 = 8.2%`;
|
||||||
|
- Superset dashboard metadata:
|
||||||
|
- `dashboard_charts = 10`;
|
||||||
|
- `obsolete_unique_sessions = 0`;
|
||||||
|
- `page_funnel_type = funnel`;
|
||||||
|
- Superset API `/api/v1/dashboard/1/datasets` вернул `200`;
|
||||||
|
- browser chart-data для `Page Funnel` вернул `status: success`, 6 строк;
|
||||||
|
- browser chart-data для `Data Quality Summary` вернул `errors: []`, `status: success`.
|
||||||
|
|
||||||
|
## Визуальная проверка
|
||||||
|
|
||||||
|
Пользователь визуально подтвердил: «Визуально - выглядит норм».
|
||||||
|
|
||||||
|
Скриншоты были сохранены в `/tmp` во время сессии:
|
||||||
|
|
||||||
|
- `/tmp/ecommerce-analytics-dashboard-desktop.png`
|
||||||
|
- `/tmp/ecommerce-analytics-dashboard-lower.png`
|
||||||
|
|
||||||
|
Они могут исчезнуть после очистки `/tmp`; при необходимости переснять через `playwright-cli`.
|
||||||
|
|
||||||
|
## Важные детали реализации
|
||||||
|
|
||||||
|
- Для Superset 4.1.2 `funnel` проверялся через MCP Context7 по `/apache/superset`; Context7 не дал точную строку `viz_type`.
|
||||||
|
- Решение подтверждено по установленному Superset 4.1.2: bundled example `Featured Charts/Funnel.yaml` использует `viz_type: funnel`.
|
||||||
|
- `create_dashboard.py` ищет старые имена через `previous_slice_names`, чтобы не плодить дубли при rename.
|
||||||
|
- Cleanup obsolete charts удаляет все найденные `🎯 Unique Sessions`, если metadata уже была загрязнена дублями.
|
||||||
|
|
||||||
|
## Что осталось
|
||||||
|
|
||||||
|
Обязательных незакрытых задач по редизайну нет.
|
||||||
|
|
||||||
|
Возможные следующие шаги, если продолжать dashboard-направление:
|
||||||
|
|
||||||
|
- экспортировать обновлённый dashboard JSON через `make superset-export`, если экспортный артефакт должен соответствовать новой metadata;
|
||||||
|
- отдельно модернизировать legacy chart types (`pie`, `world_map`, `dist_bar`) на ECharts, если это станет целью следующего захода;
|
||||||
|
- при развитии генератора перенести требования из спеки в его `KNOWN_ISSUES.md`.
|
||||||
|
|
||||||
|
## Suggested skills
|
||||||
|
|
||||||
|
- `playwright-cli` — если нужно переснять визуальную проверку dashboard.
|
||||||
|
- `conventional-commits` — если нужно коммитить этот handoff или дальнейшие правки.
|
||||||
|
- `diagnose` — если Superset chart-data или layout начнут падать после сброса volumes.
|
||||||
@@ -59,10 +59,12 @@ make superset-dashboard
|
|||||||
#### KPI-блок (верх дашборда)
|
#### KPI-блок (верх дашборда)
|
||||||
- **📊 Total Events** — общее количество событий
|
- **📊 Total Events** — общее количество событий
|
||||||
- **👤 Unique Users** — уникальные пользователи
|
- **👤 Unique Users** — уникальные пользователи
|
||||||
- **🎯 Unique Sessions** — уникальные сессии (click_id)
|
- **📈 Avg Events/Visit** — среднее количество событий на визит (`click_id`)
|
||||||
- **📈 Avg Events/Session** — среднее количество событий на сессию
|
- **🎯 Conversion to /confirmation** — доля просмотров `/confirmation` от просмотров `/home`
|
||||||
|
|
||||||
KPI разложены в одну строку по 12-колоночной сетке Superset: четыре блока по 3 колонки.
|
KPI разложены в одну строку по 12-колоночной сетке Superset: четыре блока по 3 колонки.
|
||||||
|
`Unique Sessions` не вынесен отдельной KPI-плиткой, потому что в демо-данных
|
||||||
|
`user_domain_id` и `click_id` идут 1:1 и дают то же число, что `Unique Users`.
|
||||||
|
|
||||||
#### Динамика трафика
|
#### Динамика трафика
|
||||||
- **📅 Events by Hour** — линейный график событий по часам
|
- **📅 Events by Hour** — линейный график событий по часам
|
||||||
@@ -73,7 +75,13 @@ KPI разложены в одну строку по 12-колоночной с
|
|||||||
|
|
||||||
#### Маркетинг
|
#### Маркетинг
|
||||||
- **🔗 UTM Effectiveness Table** — таблица эффективности UTM-меток
|
- **🔗 UTM Effectiveness Table** — таблица эффективности UTM-меток
|
||||||
- **📄 Top Pages** — bar chart топ-20 страниц
|
- **🪜 Page Funnel** — funnel chart по просмотрам страниц, от `/home` к `/confirmation`
|
||||||
|
|
||||||
|
> **Что проверили по 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`, поэтому dashboard создаёт именно funnel chart.
|
||||||
|
|
||||||
#### Качество данных
|
#### Качество данных
|
||||||
- **🔍 Data Quality Summary** — статистика по слоям STG/ODS/DDS
|
- **🔍 Data Quality Summary** — статистика по слоям STG/ODS/DDS
|
||||||
@@ -219,7 +227,11 @@ make superset-restart
|
|||||||
# Полная переинициализация
|
# Полная переинициализация
|
||||||
docker compose down -v
|
docker compose down -v
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
make ddl
|
||||||
|
make data
|
||||||
|
make transform
|
||||||
make superset-init
|
make superset-init
|
||||||
|
make superset-dashboard
|
||||||
```
|
```
|
||||||
|
|
||||||
### Нет данных в чартах
|
### Нет данных в чартах
|
||||||
|
|||||||
@@ -63,17 +63,19 @@ DM-витрина — это SQL-объект в ClickHouse. Она задаёт
|
|||||||
|
|
||||||
## 2. Руки: запускаем Superset и смотрим дашборд
|
## 2. Руки: запускаем Superset и смотрим дашборд
|
||||||
|
|
||||||
Подними стенд и прогони маленький срез:
|
Подними стенд и прогони полный демо-датасет:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make up
|
make up
|
||||||
make ddl
|
make ddl
|
||||||
LIMIT=50 make data
|
make data
|
||||||
make transform
|
make transform
|
||||||
```
|
```
|
||||||
|
|
||||||
`make transform` прогоняет цепочку STG → ODS → DDS → DM вне Airflow. Для этого урока так
|
`make transform` прогоняет цепочку STG → ODS → DDS → DM вне Airflow. Для этого урока так
|
||||||
быстрее: нам нужен готовый DM-слой, а не разбор DAG.
|
быстрее: нам нужен готовый DM-слой, а не разбор DAG. Отладочный срез через
|
||||||
|
`LIMIT=50 make data` можно использовать для быстрых экспериментов, но эталонный dashboard
|
||||||
|
и числа урока рассчитаны на полном наборе данных.
|
||||||
|
|
||||||
Теперь инициализируй Superset:
|
Теперь инициализируй Superset:
|
||||||
|
|
||||||
@@ -84,7 +86,16 @@ make superset-init
|
|||||||
Эта команда создаёт или обновляет:
|
Эта команда создаёт или обновляет:
|
||||||
|
|
||||||
- подключение `clickhouse_dwh`;
|
- подключение `clickhouse_dwh`;
|
||||||
- 6 datasets поверх `dm.*`;
|
- 6 datasets поверх `dm.*`.
|
||||||
|
|
||||||
|
После этого создай или обнови charts и сам dashboard:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make superset-dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
Эта команда создаёт или обновляет:
|
||||||
|
|
||||||
- 10 charts;
|
- 10 charts;
|
||||||
- dashboard `E-commerce Analytics Dashboard`.
|
- dashboard `E-commerce Analytics Dashboard`.
|
||||||
|
|
||||||
@@ -105,12 +116,17 @@ http://localhost:8088/superset/dashboard/1/
|
|||||||
|
|
||||||
На экране должны быть блоки:
|
На экране должны быть блоки:
|
||||||
|
|
||||||
- KPI сверху: `Total Events`, `Unique Users`, `Unique Sessions`, `Avg Events/Session`;
|
- KPI сверху: `Total Events`, `Unique Users`, `Avg Events/Visit`,
|
||||||
|
`Conversion to /confirmation`;
|
||||||
- динамика: `Events by Hour`, `Traffic by Device`;
|
- динамика: `Events by Hour`, `Traffic by Device`;
|
||||||
- география: `Geography Map`;
|
- география: `Geography Map`;
|
||||||
- маркетинг: `UTM Effectiveness Table`, `Top Pages`;
|
- маркетинг: `UTM Effectiveness Table`, `Page Funnel`;
|
||||||
- качество данных: `Data Quality Summary`.
|
- качество данных: `Data Quality Summary`.
|
||||||
|
|
||||||
|
`Conversion to /confirmation` считается как просмотры `/confirmation` / просмотры `/home`.
|
||||||
|
Это page-funnel метрика, а не доля визитов: она совпадает с тем, как ниже устроен chart
|
||||||
|
`Page Funnel`.
|
||||||
|
|
||||||
### Фильтр даты
|
### Фильтр даты
|
||||||
|
|
||||||
В демо-данных события датированы `2022-11-28`. В текущей конфигурации dashboard фильтр даты
|
В демо-данных события датированы `2022-11-28`. В текущей конфигурации dashboard фильтр даты
|
||||||
@@ -136,7 +152,7 @@ SELECT count() AS events
|
|||||||
FROM dm.v_events_enriched;
|
FROM dm.v_events_enriched;
|
||||||
```
|
```
|
||||||
|
|
||||||
И посмотри, откуда берётся график `Top Pages`:
|
И посмотри, откуда берётся график `Page Funnel`:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT page_url_path, sum(pageviews) AS pageviews
|
SELECT page_url_path, sum(pageviews) AS pageviews
|
||||||
@@ -233,18 +249,20 @@ Metadata Superset живёт в PostgreSQL, а сами данные остаю
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
{
|
{
|
||||||
"slice_name": "📄 Top Pages",
|
"slice_name": "🪜 Page Funnel",
|
||||||
"viz_type": "dist_bar",
|
"viz_type": "funnel",
|
||||||
"dataset_name": "v_top_pages_daily",
|
"dataset_name": "v_top_pages_daily",
|
||||||
"params": {
|
"params": {
|
||||||
"groupby": ["page_url_path"],
|
"groupby": ["page_url_path"],
|
||||||
"metrics": [
|
"metric": {
|
||||||
{"expressionType": "SQL", "sqlExpression": "SUM(pageviews)", "label": "Pageviews"}
|
"expressionType": "SQL",
|
||||||
],
|
"sqlExpression": "SUM(pageviews)",
|
||||||
|
"label": "Pageviews"
|
||||||
|
},
|
||||||
"row_limit": 20,
|
"row_limit": 20,
|
||||||
"time_range": "No filter",
|
"time_range": "No filter",
|
||||||
"orientation": "vertical",
|
"sort_by_metric": True,
|
||||||
"show_legend": False
|
"percent_calculation_type": "first_step"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -280,14 +298,16 @@ Native filters создаются в `build_dashboard_metadata`. Там есть
|
|||||||
|
|
||||||
> **Что проверили по API.** Перед уроком Superset сверили через MCP Context7 (`/apache/superset`):
|
> **Что проверили по API.** Перед уроком Superset сверили через MCP Context7 (`/apache/superset`):
|
||||||
> в Superset есть отдельные сущности charts и dashboards, metadata хранится отдельно от
|
> в Superset есть отдельные сущности charts и dashboards, metadata хранится отдельно от
|
||||||
> подключаемых источников данных, а row limit — часть настройки запросов и конфигурации.
|
> подключаемых источников данных. Для `funnel` Context7 не дал точную строку `viz_type`, поэтому
|
||||||
> Поэтому в уроке не лезем в REST API Superset, а работаем через уже существующий скрипт стенда.
|
> дополнительно проверили установленный Superset 4.1.2: bundled example
|
||||||
|
> `Featured Charts/Funnel.yaml` использует `viz_type: funnel`. Поэтому в уроке не лезем в REST API
|
||||||
|
> Superset, а работаем через уже существующий скрипт стенда.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Управляемая правка: уменьшаем Top Pages
|
## 4. Управляемая правка: уменьшаем Page Funnel
|
||||||
|
|
||||||
Сейчас chart `Top Pages` показывает до 20 страниц:
|
Сейчас chart `Page Funnel` показывает до 20 страниц:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
"row_limit": 20,
|
"row_limit": 20,
|
||||||
@@ -296,7 +316,7 @@ Native filters создаются в `build_dashboard_metadata`. Там есть
|
|||||||
Сделай маленькую видимую правку: временно покажи только топ-3 страницы.
|
Сделай маленькую видимую правку: временно покажи только топ-3 страницы.
|
||||||
|
|
||||||
Открой [`superset/create_dashboard.py`](../../../superset/create_dashboard.py), найди chart
|
Открой [`superset/create_dashboard.py`](../../../superset/create_dashboard.py), найди chart
|
||||||
`Top Pages` и поменяй:
|
`Page Funnel` и поменяй:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
"row_limit": 20,
|
"row_limit": 20,
|
||||||
@@ -314,7 +334,7 @@ Native filters создаются в `build_dashboard_metadata`. Там есть
|
|||||||
make superset-dashboard
|
make superset-dashboard
|
||||||
```
|
```
|
||||||
|
|
||||||
Вернись в Superset и обнови страницу dashboard. В chart `Top Pages` должно остаться не больше
|
Вернись в Superset и обнови страницу dashboard. В chart `Page Funnel` должно остаться не больше
|
||||||
трёх страниц. Если фильтр даты снова скрыл данные, поставь **Date Range → No filter** и нажми
|
трёх страниц. Если фильтр даты снова скрыл данные, поставь **Date Range → No filter** и нажми
|
||||||
**Apply filters**.
|
**Apply filters**.
|
||||||
|
|
||||||
@@ -339,17 +359,17 @@ make superset-dashboard
|
|||||||
make superset-dashboard
|
make superset-dashboard
|
||||||
```
|
```
|
||||||
|
|
||||||
После обновления страницы chart `Top Pages` снова может показывать до 20 страниц.
|
После обновления страницы chart `Page Funnel` снова может показывать до 20 страниц.
|
||||||
|
|
||||||
Если после экспериментов Superset выглядит странно, самый простой учебный возврат dashboard
|
Если после экспериментов Superset выглядит странно, самый простой учебный возврат dashboard
|
||||||
metadata к конфигурации из репозитория:
|
metadata к конфигурации из репозитория:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make superset-init
|
make superset-dashboard
|
||||||
```
|
```
|
||||||
|
|
||||||
Данные в ClickHouse эта команда не удаляет. Она повторно применяет подключение, datasets и
|
Данные в ClickHouse эта команда не удаляет. Она повторно применяет charts и dashboard
|
||||||
dashboard metadata Superset.
|
metadata Superset.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -362,8 +382,8 @@ dashboard metadata Superset.
|
|||||||
| открыть **Datasets** | Superset UI | есть datasets `v_events_enriched`, `v_top_pages_daily`, `dq_summary` |
|
| открыть **Datasets** | Superset UI | есть datasets `v_events_enriched`, `v_top_pages_daily`, `dq_summary` |
|
||||||
| открыть dashboard | Superset UI | видны KPI, маркетинг, география и качество данных |
|
| открыть dashboard | Superset UI | видны KPI, маркетинг, география и качество данных |
|
||||||
| поставить **Date Range → No filter** | dashboard filters | графики не скрываются из-за даты `2022-11-28` |
|
| поставить **Date Range → No filter** | dashboard filters | графики не скрываются из-за даты `2022-11-28` |
|
||||||
| поменять `row_limit` у `Top Pages` на `3` и запустить `make superset-dashboard` | chart `Top Pages` | не больше трёх страниц |
|
| поменять `row_limit` у `Page Funnel` на `3` и запустить `make superset-dashboard` | chart `Page Funnel` | не больше трёх страниц |
|
||||||
| вернуть `row_limit` на `20` и запустить `make superset-dashboard` | chart `Top Pages` | ограничение снова до 20 страниц |
|
| вернуть `row_limit` на `20` и запустить `make superset-dashboard` | chart `Page Funnel` | ограничение снова до 20 страниц |
|
||||||
|
|
||||||
Вопросы для созвона:
|
Вопросы для созвона:
|
||||||
|
|
||||||
@@ -378,7 +398,7 @@ dashboard metadata Superset.
|
|||||||
## 6. Что должно получиться
|
## 6. Что должно получиться
|
||||||
|
|
||||||
К концу урока у тебя должен быть открытый dashboard `E-commerce Analytics Dashboard` в Superset.
|
К концу урока у тебя должен быть открытый dashboard `E-commerce Analytics Dashboard` в Superset.
|
||||||
Сделай скриншот после временной правки `Top Pages`: на нём должно быть видно, что chart показывает
|
Сделай скриншот после временной правки `Page Funnel`: на нём должно быть видно, что chart показывает
|
||||||
не больше трёх страниц.
|
не больше трёх страниц.
|
||||||
|
|
||||||
Второй артефакт — короткий абзац своими словами:
|
Второй артефакт — короткий абзац своими словами:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Редизайн KPI-полосы и состава чартов дашборда «E-commerce Analytics»
|
# Редизайн KPI-полосы и состава чартов дашборда «E-commerce Analytics»
|
||||||
|
|
||||||
Дата: 2026-06-06
|
Дата: 2026-06-06
|
||||||
Статус: Accepted (не реализовано — код `superset/create_dashboard.py` правится
|
Статус: 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),
|
Связано: [`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)
|
урок [`docs/course/lessons/06_superset_bi.md`](../course/lessons/06_superset_bi.md)
|
||||||
|
|
||||||
@@ -141,11 +141,16 @@ KPI-полоса:
|
|||||||
|
|
||||||
## Open questions
|
## Open questions
|
||||||
|
|
||||||
- **Точная формула Conversion:** доля визитов с ≥1 pageview `/confirmation`, или
|
- **Resolved: точная формула Conversion.** Используем page-funnel conversion:
|
||||||
просмотры `/confirmation` / просмотры `/home`? Влияет на число (~8%) и на то,
|
`countIf(page_url_path = '/confirmation') / countIf(page_url_path = '/home')`.
|
||||||
что именно учим (визит-конверсия vs page-to-page).
|
На полном датасете это `35 / 426 = 8.2%`. Визитовая формула даёт 25.3% и
|
||||||
- **Events by Hour** — оставляем ли (зависит от проверки на пустоту на полных
|
отклонена, потому что KPI должен совпадать с логикой chart `Page Funnel`.
|
||||||
данных: разброс `event_ts` по часам).
|
- **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,
|
(разнообразие `event_type`, возвраты пользователей → sessions>users,
|
||||||
реалистичная воронка) — при возврате к генератору перенести в его
|
реалистичная воронка) — при возврате к генератору перенести в его
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ logger = logging.getLogger(__name__)
|
|||||||
sys.path.insert(0, '/app')
|
sys.path.insert(0, '/app')
|
||||||
|
|
||||||
|
|
||||||
|
OBSOLETE_CHART_NAMES = {
|
||||||
|
"🎯 Unique Sessions",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Конфигурация чартов
|
# Конфигурация чартов
|
||||||
CHARTS_CONFIG = [
|
CHARTS_CONFIG = [
|
||||||
# KPI блок
|
# KPI блок
|
||||||
@@ -63,32 +68,36 @@ CHARTS_CONFIG = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slice_name": "🎯 Unique Sessions",
|
"slice_name": "📈 Avg Events/Visit",
|
||||||
"viz_type": "big_number_total",
|
"previous_slice_names": ["📈 Avg Events/Session"],
|
||||||
"dataset_name": "v_events_enriched",
|
|
||||||
"params": {
|
|
||||||
"metric": {
|
|
||||||
"expressionType": "SQL",
|
|
||||||
"sqlExpression": "COUNT(DISTINCT click_id)",
|
|
||||||
"label": "Unique Sessions",
|
|
||||||
"optionName": "metric_3"
|
|
||||||
},
|
|
||||||
"y_axis_format": ",d",
|
|
||||||
"time_range": "No filter"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"slice_name": "📈 Avg Events/Session",
|
|
||||||
"viz_type": "big_number_total",
|
"viz_type": "big_number_total",
|
||||||
"dataset_name": "v_events_enriched",
|
"dataset_name": "v_events_enriched",
|
||||||
"params": {
|
"params": {
|
||||||
"metric": {
|
"metric": {
|
||||||
"expressionType": "SQL",
|
"expressionType": "SQL",
|
||||||
"sqlExpression": "COUNT(*) / COUNT(DISTINCT click_id)",
|
"sqlExpression": "COUNT(*) / COUNT(DISTINCT click_id)",
|
||||||
"label": "Avg Events/Session",
|
"label": "Avg Events/Visit",
|
||||||
"optionName": "metric_4"
|
"optionName": "metric_4"
|
||||||
},
|
},
|
||||||
"y_axis_format": ".2f",
|
"y_axis_format": ".1f",
|
||||||
|
"time_range": "No filter"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slice_name": "🎯 Conversion to /confirmation",
|
||||||
|
"viz_type": "big_number_total",
|
||||||
|
"dataset_name": "v_events_enriched",
|
||||||
|
"params": {
|
||||||
|
"metric": {
|
||||||
|
"expressionType": "SQL",
|
||||||
|
"sqlExpression": (
|
||||||
|
"if(countIf(page_url_path = '/home') = 0, 0, "
|
||||||
|
"countIf(page_url_path = '/confirmation') / countIf(page_url_path = '/home'))"
|
||||||
|
),
|
||||||
|
"label": "Conversion to /confirmation",
|
||||||
|
"optionName": "metric_5"
|
||||||
|
},
|
||||||
|
"y_axis_format": ".1%",
|
||||||
"time_range": "No filter"
|
"time_range": "No filter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -175,18 +184,32 @@ CHARTS_CONFIG = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slice_name": "📄 Top Pages",
|
"slice_name": "🪜 Page Funnel",
|
||||||
"viz_type": "dist_bar",
|
"previous_slice_names": ["📄 Top Pages"],
|
||||||
|
# В Superset 4.1.2 `funnel` есть в frontend-плагинах и примерах
|
||||||
|
# поставки, хотя legacy registry `superset.viz.viz_types` его не
|
||||||
|
# показывает. Параметры взяты из bundled Featured Charts/Funnel.yaml.
|
||||||
|
"viz_type": "funnel",
|
||||||
"dataset_name": "v_top_pages_daily",
|
"dataset_name": "v_top_pages_daily",
|
||||||
"params": {
|
"params": {
|
||||||
"groupby": ["page_url_path"],
|
"groupby": ["page_url_path"],
|
||||||
"metrics": [
|
"metric": {
|
||||||
{"expressionType": "SQL", "sqlExpression": "SUM(pageviews)", "label": "Pageviews"}
|
"expressionType": "SQL",
|
||||||
],
|
"sqlExpression": "SUM(pageviews)",
|
||||||
|
"label": "Pageviews"
|
||||||
|
},
|
||||||
"row_limit": 20,
|
"row_limit": 20,
|
||||||
"time_range": "No filter",
|
"time_range": "No filter",
|
||||||
"orientation": "vertical",
|
"sort_by_metric": True,
|
||||||
"show_legend": False
|
"percent_calculation_type": "first_step",
|
||||||
|
"color_scheme": "supersetColors",
|
||||||
|
"show_legend": True,
|
||||||
|
"legendOrientation": "top",
|
||||||
|
"legendMargin": 50,
|
||||||
|
"tooltip_label_type": 5,
|
||||||
|
"number_format": "SMART_NUMBER",
|
||||||
|
"show_labels": True,
|
||||||
|
"show_tooltip_labels": True
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
# Качество данных
|
# Качество данных
|
||||||
@@ -233,13 +256,13 @@ DASHBOARD_CONFIG = {
|
|||||||
DASHBOARD_ROWS = [
|
DASHBOARD_ROWS = [
|
||||||
# KPI-полоса: 4 числа в один ряд
|
# KPI-полоса: 4 числа в один ряд
|
||||||
[("📊 Total Events", 3), ("👤 Unique Users", 3),
|
[("📊 Total Events", 3), ("👤 Unique Users", 3),
|
||||||
("🎯 Unique Sessions", 3), ("📈 Avg Events/Session", 3)],
|
("📈 Avg Events/Visit", 3), ("🎯 Conversion to /confirmation", 3)],
|
||||||
# Динамика во времени + разрез по устройствам
|
# Динамика во времени + разрез по устройствам
|
||||||
[("📅 Events by Hour", 8), ("📱 Traffic by Device", 4)],
|
[("📅 Events by Hour", 8), ("📱 Traffic by Device", 4)],
|
||||||
# География + эффективность маркетинговых каналов
|
# География + эффективность маркетинговых каналов
|
||||||
[("🌍 Geography Map", 6), ("🔗 UTM Effectiveness Table", 6)],
|
[("🌍 Geography Map", 6), ("🔗 UTM Effectiveness Table", 6)],
|
||||||
# Популярные страницы + качество данных
|
# Популярные страницы + качество данных
|
||||||
[("📄 Top Pages", 6), ("🔍 Data Quality Summary", 6)],
|
[("🪜 Page Funnel", 6), ("🔍 Data Quality Summary", 6)],
|
||||||
]
|
]
|
||||||
|
|
||||||
# Высота строки в grid-units Superset (одинаковая для всех чартов строки —
|
# Высота строки в grid-units Superset (одинаковая для всех чартов строки —
|
||||||
@@ -410,13 +433,17 @@ def main() -> bool:
|
|||||||
params["viz_type"] = chart_config["viz_type"]
|
params["viz_type"] = chart_config["viz_type"]
|
||||||
serialized_params = json.dumps(params)
|
serialized_params = json.dumps(params)
|
||||||
|
|
||||||
# Проверяем, существует ли уже чарт
|
# Проверяем, существует ли уже чарт. Старые имена нужны для
|
||||||
existing = db.session.query(Slice).filter_by(
|
# идемпотентного rename без дублей в списке Charts.
|
||||||
slice_name=chart_config["slice_name"]
|
chart_names = [chart_config["slice_name"]]
|
||||||
).first()
|
chart_names.extend(chart_config.get("previous_slice_names", []))
|
||||||
|
existing = db.session.query(Slice).filter(
|
||||||
|
Slice.slice_name.in_(chart_names)
|
||||||
|
).order_by(Slice.id.asc()).first()
|
||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
# Синхронизируем параметры существующего чарта с конфигом.
|
# Синхронизируем параметры существующего чарта с конфигом.
|
||||||
|
existing.slice_name = chart_config["slice_name"]
|
||||||
existing.viz_type = chart_config["viz_type"]
|
existing.viz_type = chart_config["viz_type"]
|
||||||
existing.datasource_id = dataset.id
|
existing.datasource_id = dataset.id
|
||||||
existing.datasource_type = "table"
|
existing.datasource_type = "table"
|
||||||
@@ -456,6 +483,15 @@ def main() -> bool:
|
|||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
|
|
||||||
logger.info(f"Created/Found {len(created_charts)} charts")
|
logger.info(f"Created/Found {len(created_charts)} charts")
|
||||||
|
|
||||||
|
current_chart_names = {chart_config["slice_name"] for chart_config in CHARTS_CONFIG}
|
||||||
|
for obsolete_name in sorted(OBSOLETE_CHART_NAMES - current_chart_names):
|
||||||
|
obsolete_charts = db.session.query(Slice).filter_by(slice_name=obsolete_name).all()
|
||||||
|
for obsolete in obsolete_charts:
|
||||||
|
db.session.delete(obsolete)
|
||||||
|
logger.info("Deleted obsolete chart: %s (ID: %s)", obsolete_name, obsolete.id)
|
||||||
|
db.session.flush()
|
||||||
|
|
||||||
metadata_json = build_dashboard_metadata(datasets_by_name.get("v_events_enriched"))
|
metadata_json = build_dashboard_metadata(datasets_by_name.get("v_events_enriched"))
|
||||||
|
|
||||||
# Создаём позиции чартов для layout.
|
# Создаём позиции чартов для layout.
|
||||||
|
|||||||
Reference in New Issue
Block a user