fix(superset): исправлен BI-дашборд и добавлен урок 6
- Зачем: - Superset dashboard открывался с ошибками datasources и неудобным layout, а курс не содержал готового урока по BI-витрине. - Что: - добавлен урок 6 про Superset поверх ClickHouse DM-витрин. - исправлена раскладка dashboard и дефолтный фильтр даты для исторических демо-данных. - добавлено восстановление metadata колонок датасетов при обновлении dashboard. - Проверка: - make superset-dashboard. - /api/v1/dashboard/1/datasets и /superset/explore_json для chart 10 возвращают 200. - python3 -m py_compile superset/create_dashboard.py; git diff --cached --check.
This commit is contained in:
@@ -179,7 +179,7 @@ flowchart LR
|
|||||||
| `make ddl` | Применить DDL в ClickHouse (вне Airflow) |
|
| `make ddl` | Применить DDL в ClickHouse (вне Airflow) |
|
||||||
| `make transform` | Запустить batch-процесс `STG -> ODS -> DDS -> DM` (вне Airflow) |
|
| `make transform` | Запустить batch-процесс `STG -> ODS -> DDS -> DM` (вне Airflow) |
|
||||||
| `make superset-init` | Подключение к ClickHouse + импорт датасетов |
|
| `make superset-init` | Подключение к ClickHouse + импорт датасетов |
|
||||||
| `make superset-dashboard` | Создание дашборда с чартами |
|
| `make superset-dashboard` | Создание дашборда с чартами, обновление layout/metadata |
|
||||||
| `make superset-ui` | Показать URL Superset |
|
| `make superset-ui` | Показать URL Superset |
|
||||||
| `make superset-restart` | Перезапуск Superset |
|
| `make superset-restart` | Перезапуск Superset |
|
||||||
|
|
||||||
@@ -276,6 +276,9 @@ make superset-dashboard
|
|||||||
| **Маркетинг** | UTM Effectiveness Table, Top Pages | `v_utm_effectiveness`, `v_top_pages_daily` |
|
| **Маркетинг** | UTM Effectiveness Table, Top Pages | `v_utm_effectiveness`, `v_top_pages_daily` |
|
||||||
| **Quality** | Data Quality Summary | `dq_summary` |
|
| **Quality** | Data Quality Summary | `dq_summary` |
|
||||||
|
|
||||||
|
Фильтр `Date Range` по умолчанию открыт как `No filter`, потому что демо-данные лежат в
|
||||||
|
историческом диапазоне (`2022-11-28`).
|
||||||
|
|
||||||
### Архитектура Superset
|
### Архитектура Superset
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ make transform
|
|||||||
# Автоматическая инициализация (создание подключения и датасетов)
|
# Автоматическая инициализация (создание подключения и датасетов)
|
||||||
make superset-init
|
make superset-init
|
||||||
|
|
||||||
# Создание дашборда с чартами
|
# Создание дашборда с чартами; при необходимости обновляет metadata колонок датасетов
|
||||||
make superset-dashboard
|
make superset-dashboard
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -62,6 +62,8 @@ make superset-dashboard
|
|||||||
- **🎯 Unique Sessions** — уникальные сессии (click_id)
|
- **🎯 Unique Sessions** — уникальные сессии (click_id)
|
||||||
- **📈 Avg Events/Session** — среднее количество событий на сессию
|
- **📈 Avg Events/Session** — среднее количество событий на сессию
|
||||||
|
|
||||||
|
KPI разложены в одну строку по 12-колоночной сетке Superset: четыре блока по 3 колонки.
|
||||||
|
|
||||||
#### Динамика трафика
|
#### Динамика трафика
|
||||||
- **📅 Events by Hour** — линейный график событий по часам
|
- **📅 Events by Hour** — линейный график событий по часам
|
||||||
- **📱 Traffic by Device** — pie chart распределения по устройствам
|
- **📱 Traffic by Device** — pie chart распределения по устройствам
|
||||||
@@ -80,7 +82,7 @@ make superset-dashboard
|
|||||||
|
|
||||||
| Фильтр | Поле | Тип | Применение |
|
| Фильтр | Поле | Тип | Применение |
|
||||||
|--------|------|-----|------------|
|
|--------|------|-----|------------|
|
||||||
| 📅 Date Range | `event_date` | Time Range | Все чарты |
|
| 📅 Date Range | `event_date` | Time Range | Все чарты; по умолчанию `No filter`, чтобы демо-данные 2022 года не скрывались |
|
||||||
| 🌍 Country | `geo_country` | Multi-select | Все чарты |
|
| 🌍 Country | `geo_country` | Multi-select | Все чарты |
|
||||||
| 📱 Device Type | `device_type` | Multi-select | Все чарты |
|
| 📱 Device Type | `device_type` | Multi-select | Все чарты |
|
||||||
| 🌐 Browser | `browser_name` | Multi-select | Все чарты |
|
| 🌐 Browser | `browser_name` | Multi-select | Все чарты |
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Курс «Кликстрим на ClickHouse» (со звёздочкой)
|
# Курс «Кликстрим на ClickHouse» (со звёздочкой)
|
||||||
|
|
||||||
Продвинутый курс для менти, уже прошедших базовую программу: основные паттерны
|
Продвинутый курс для менти, уже прошедших базовую программу: основные паттерны
|
||||||
инженерии данных на стенде Kafka + ClickHouse + Airflow + мониторинг. Менти проходит
|
инженерии данных на стенде Kafka + ClickHouse + Airflow + мониторинг + BI. Менти проходит
|
||||||
материал самостоятельно, а на еженедельном созвоне с ментором разбирает затыки и отвечает
|
материал самостоятельно, а на еженедельном созвоне с ментором разбирает затыки и отвечает
|
||||||
на вопросы по теме — чтобы проверить глубину понимания.
|
на вопросы по теме — чтобы проверить глубину понимания.
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
| [`PRD.md`](./PRD.md) | Рамка: зачем курс, цели, аудитория, скоуп, критерии успеха | Чтобы понять «что и зачем». Замороженный документ |
|
| [`PRD.md`](./PRD.md) | Рамка: зачем курс, цели, аудитория, скоуп, критерии успеха | Чтобы понять «что и зачем». Замороженный документ |
|
||||||
| [`LEARNING_PLAN.md`](./LEARNING_PLAN.md) | План обучения: карта уроков, маршрут, аудит эталонных путей | Чтобы понять «в каком порядке и из чего» |
|
| [`LEARNING_PLAN.md`](./LEARNING_PLAN.md) | План обучения: карта уроков, маршрут, аудит эталонных путей | Чтобы понять «в каком порядке и из чего» |
|
||||||
| [`LESSON_STANDARD.md`](./LESSON_STANDARD.md) | Стандарт уроков: шаблон урока, качество кода, самопроверка | Рабочий чеклист при написании каждого урока |
|
| [`LESSON_STANDARD.md`](./LESSON_STANDARD.md) | Стандарт уроков: шаблон урока, качество кода, самопроверка | Рабочий чеклист при написании каждого урока |
|
||||||
| [`lessons/`](./lessons/) | Сами уроки, по одному файлу (есть: уроки 0–5) | Прохождение курса менти |
|
| [`lessons/`](./lessons/) | Сами уроки, по одному файлу (есть: уроки 0–6) | Прохождение курса менти |
|
||||||
|
|
||||||
## Порядок чтения
|
## Порядок чтения
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,400 @@
|
|||||||
|
# Урок 6. BI-витрина в Superset
|
||||||
|
|
||||||
|
> Формат: **практика** — будешь запускать Superset поверх готовых витрин ClickHouse,
|
||||||
|
> смотреть дашборд и делать маленькую обратимую правку в конфигурации чарта.
|
||||||
|
> Пререквизит: пройдены уроки 4–5 (ты уже запускал `etl_pipeline`, видел DM-слой в конце
|
||||||
|
> пайплайна и понимаешь разницу между мониторингом стенда и данными для анализа).
|
||||||
|
> Эталонные пути:
|
||||||
|
> [`sql/ddl/dm/40_dm.sql`](../../../sql/ddl/dm/40_dm.sql),
|
||||||
|
> [`sql/dm/40_dds_to_dm.sql`](../../../sql/dm/40_dds_to_dm.sql),
|
||||||
|
> [`superset/init_superset.py`](../../../superset/init_superset.py),
|
||||||
|
> [`superset/create_dashboard.py`](../../../superset/create_dashboard.py).
|
||||||
|
>
|
||||||
|
> Поток данных одной строкой:
|
||||||
|
> `DDS → DM views / dm.dq_summary → Superset datasets → charts → dashboard → native filters`
|
||||||
|
>
|
||||||
|
> О чём урок простыми словами: ClickHouse уже подготовил таблицы и представления для
|
||||||
|
> потребления. Superset превращает их в экран для аналитика: датасеты, графики, фильтры
|
||||||
|
> и один общий дашборд.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Зачем и где в проде
|
||||||
|
|
||||||
|
После уроков 1–4 у нас есть данные: поток приземлился в STG, разобрался в ODS, собрался в DDS,
|
||||||
|
а в конце появился DM-слой. После урока 5 у нас есть мониторинг: он отвечает, жив ли стенд и
|
||||||
|
не сломался ли пайплайн.
|
||||||
|
|
||||||
|
Теперь нужен другой взгляд — **BI** (Business Intelligence, «аналитический интерфейс для
|
||||||
|
бизнес-вопросов»). BI отвечает не «жив ли ClickHouse», а:
|
||||||
|
|
||||||
|
- сколько событий пришло;
|
||||||
|
- какие устройства чаще встречаются;
|
||||||
|
- из каких стран пришёл трафик;
|
||||||
|
- какие UTM-каналы дают больше кликов;
|
||||||
|
- какие страницы самые популярные;
|
||||||
|
- есть ли видимые проблемы качества данных.
|
||||||
|
|
||||||
|
**Superset** — BI-инструмент. Он не заменяет ClickHouse, Airflow или Grafana. Он сидит поверх
|
||||||
|
готовых данных и даёт интерфейс для просмотра, фильтрации и сборки графиков.
|
||||||
|
|
||||||
|
В нашем стенде роли такие:
|
||||||
|
|
||||||
|
| Слой | Что делает |
|
||||||
|
|------|------------|
|
||||||
|
| `dds.click`, `dds.event` | хранит собранные сущности после ODS |
|
||||||
|
| `dm.v_*`, `dm.dq_summary` | готовит поверхность потребления для аналитики |
|
||||||
|
| Superset dataset | регистрирует таблицу или VIEW из ClickHouse в Superset |
|
||||||
|
| Superset chart | сохраняет один график или KPI на базе dataset |
|
||||||
|
| Superset dashboard | собирает charts в один экран |
|
||||||
|
| Native filters | фильтруют dashboard по дате, стране, устройству, браузеру |
|
||||||
|
|
||||||
|
Граница урока: **витрина DM и BI-экран — не одно и то же**.
|
||||||
|
|
||||||
|
DM-витрина — это SQL-объект в ClickHouse. Она задаёт форму данных: какие поля есть, на какой
|
||||||
|
гранулярности лежит агрегат, какие joins уже сделаны. BI-экран — это способ показать эту
|
||||||
|
витрину человеку: график, таблица, фильтр, порядок блоков на странице.
|
||||||
|
|
||||||
|
> **В проде иначе.** Superset обычно подключают к нескольким хранилищам, заводят роли и права,
|
||||||
|
> разделяют черновые и опубликованные дашборды, а тяжёлые витрины материализуют. Но базовая
|
||||||
|
> схема та же: хранилище готовит данные, BI даёт удобную точку потребления.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Руки: запускаем Superset и смотрим дашборд
|
||||||
|
|
||||||
|
Подними стенд и прогони маленький срез:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make up
|
||||||
|
make ddl
|
||||||
|
LIMIT=50 make data
|
||||||
|
make transform
|
||||||
|
```
|
||||||
|
|
||||||
|
`make transform` прогоняет цепочку STG → ODS → DDS → DM вне Airflow. Для этого урока так
|
||||||
|
быстрее: нам нужен готовый DM-слой, а не разбор DAG.
|
||||||
|
|
||||||
|
Теперь инициализируй Superset:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make superset-init
|
||||||
|
```
|
||||||
|
|
||||||
|
Эта команда создаёт или обновляет:
|
||||||
|
|
||||||
|
- подключение `clickhouse_dwh`;
|
||||||
|
- 6 datasets поверх `dm.*`;
|
||||||
|
- 10 charts;
|
||||||
|
- dashboard `E-commerce Analytics Dashboard`.
|
||||||
|
|
||||||
|
Открой Superset: `http://localhost:8088` (логин `admin`, пароль `admin`).
|
||||||
|
|
||||||
|
Если Superset предлагает сменить пароль после первого входа, для учебного стенда можно нажать
|
||||||
|
**Skip**. В проде так не делают, но локальный курс держит одинаковые инструкции для всех.
|
||||||
|
|
||||||
|
### Открываем dashboard
|
||||||
|
|
||||||
|
Открой готовый dashboard:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8088/superset/dashboard/1/
|
||||||
|
```
|
||||||
|
|
||||||
|
Если URL не открылся, зайди через меню **Dashboards** и найди `E-commerce Analytics Dashboard`.
|
||||||
|
|
||||||
|
На экране должны быть блоки:
|
||||||
|
|
||||||
|
- KPI сверху: `Total Events`, `Unique Users`, `Unique Sessions`, `Avg Events/Session`;
|
||||||
|
- динамика: `Events by Hour`, `Traffic by Device`;
|
||||||
|
- география: `Geography Map`;
|
||||||
|
- маркетинг: `UTM Effectiveness Table`, `Top Pages`;
|
||||||
|
- качество данных: `Data Quality Summary`.
|
||||||
|
|
||||||
|
### Фильтр даты
|
||||||
|
|
||||||
|
В демо-данных события датированы `2022-11-28`. В текущей конфигурации dashboard фильтр даты
|
||||||
|
открывается как `No filter`. Если у тебя осталась старая metadata Superset и native filter
|
||||||
|
**Date Range** стоит в значении `Last week`, часть графиков может быть пустой, хотя данные есть.
|
||||||
|
|
||||||
|
Для этого урока поставь в фильтре даты одно из двух:
|
||||||
|
|
||||||
|
- `No filter`;
|
||||||
|
- или ручной диапазон вокруг `2022-11-28`.
|
||||||
|
|
||||||
|
После этого нажми **Apply filters**. Теперь смотри на dashboard как аналитик: какие графики
|
||||||
|
отвечают на бизнес-вопросы, а какие только показывают техническое качество данных.
|
||||||
|
|
||||||
|
### Проверяем данные напрямую в ClickHouse
|
||||||
|
|
||||||
|
Открой ClickHouse play-консоль: `http://localhost:9123/play`.
|
||||||
|
|
||||||
|
Проверь, что основной dataset Superset не пустой:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT count() AS events
|
||||||
|
FROM dm.v_events_enriched;
|
||||||
|
```
|
||||||
|
|
||||||
|
И посмотри, откуда берётся график `Top Pages`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT page_url_path, sum(pageviews) AS pageviews
|
||||||
|
FROM dm.v_top_pages_daily
|
||||||
|
GROUP BY page_url_path
|
||||||
|
ORDER BY pageviews DESC
|
||||||
|
LIMIT 20;
|
||||||
|
```
|
||||||
|
|
||||||
|
Запомни эту связку: Superset показывает график, но данные и логика агрегации живут в ClickHouse.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Загляни внутрь
|
||||||
|
|
||||||
|
Разберём три места: DM-витрины в ClickHouse, регистрацию datasets в Superset и сборку charts /
|
||||||
|
dashboard.
|
||||||
|
|
||||||
|
### DM: поверхность потребления
|
||||||
|
|
||||||
|
Открой [`sql/ddl/dm/40_dm.sql`](../../../sql/ddl/dm/40_dm.sql).
|
||||||
|
|
||||||
|
В начале файла написано, почему DM сейчас сделан через `VIEW`:
|
||||||
|
|
||||||
|
- логику можно менять без пересоздания таблиц;
|
||||||
|
- нет копии данных поверх DDS;
|
||||||
|
- для демо производительности достаточно.
|
||||||
|
|
||||||
|
Основная витрина для dashboard — `dm.v_events_enriched`. Она соединяет `dds.event` и `dds.click`
|
||||||
|
через `click_id`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
FROM dds.event AS e
|
||||||
|
LEFT JOIN dds.click AS c ON c.click_id = e.click_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
`LEFT JOIN` здесь осознанный: событие может существовать без части контекста из клика. Для BI это
|
||||||
|
значит: график событий не исчезает только потому, что у части строк нет устройства или географии.
|
||||||
|
|
||||||
|
Другие VIEW дают более узкие поверхности:
|
||||||
|
|
||||||
|
| VIEW / таблица | Для чего нужна в Superset |
|
||||||
|
|----------------|---------------------------|
|
||||||
|
| `dm.v_events_enriched` | KPI, динамика, устройства, география, фильтры |
|
||||||
|
| `dm.v_daily_traffic` | готовая дневная агрегация трафика |
|
||||||
|
| `dm.v_utm_effectiveness` | таблица по UTM-каналам |
|
||||||
|
| `dm.v_top_pages_daily` | популярные страницы |
|
||||||
|
| `dm.v_session_overview` | обзор сессий |
|
||||||
|
| `dm.dq_summary` | сводка качества данных по слоям |
|
||||||
|
|
||||||
|
Открой [`sql/dm/40_dds_to_dm.sql`](../../../sql/dm/40_dds_to_dm.sql). Этот файл не пересчитывает
|
||||||
|
все `dm.v_*`: VIEW создаются в DDL. Здесь batch-часть наполняет `dm.dq_summary`, чтобы dashboard
|
||||||
|
мог показать качество данных после каждого прогона.
|
||||||
|
|
||||||
|
### `init_superset.py`: подключение и datasets
|
||||||
|
|
||||||
|
Открой [`superset/init_superset.py`](../../../superset/init_superset.py).
|
||||||
|
|
||||||
|
Сначала скрипт собирает URI ClickHouse:
|
||||||
|
|
||||||
|
```python
|
||||||
|
return f"clickhousedb://{user}:{password}@{host}:{port}/{database}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Внутри Docker-сети Superset ходит в ClickHouse по HTTP-порту `8123`, поэтому URI использует
|
||||||
|
`clickhousedb://...@clickhouse:8123/default`.
|
||||||
|
|
||||||
|
Потом скрипт создаёт подключение `clickhouse_dwh` и регистрирует datasets:
|
||||||
|
|
||||||
|
```python
|
||||||
|
datasets = [
|
||||||
|
{
|
||||||
|
"table_name": "v_events_enriched",
|
||||||
|
"schema": "dm",
|
||||||
|
"database_name": "clickhouse_dwh",
|
||||||
|
"description": "Полная обогащённая витрина событий (event + click)"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dataset** в Superset — это не копия данных. Это запись в metadata Superset: какая таблица или
|
||||||
|
VIEW есть в ClickHouse, какие у неё колонки и как её можно использовать в графиках.
|
||||||
|
|
||||||
|
Metadata Superset живёт в PostgreSQL, а сами данные остаются в ClickHouse. Поэтому после полного
|
||||||
|
сброса volumes нужно заново создать metadata Superset, а после пересчёта данных — сами charts
|
||||||
|
обычно остаются теми же.
|
||||||
|
|
||||||
|
### `create_dashboard.py`: charts, dashboard, filters
|
||||||
|
|
||||||
|
Открой [`superset/create_dashboard.py`](../../../superset/create_dashboard.py).
|
||||||
|
|
||||||
|
В `CHARTS_CONFIG` лежит список charts. Один элемент списка — один график:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{
|
||||||
|
"slice_name": "📄 Top Pages",
|
||||||
|
"viz_type": "dist_bar",
|
||||||
|
"dataset_name": "v_top_pages_daily",
|
||||||
|
"params": {
|
||||||
|
"groupby": ["page_url_path"],
|
||||||
|
"metrics": [
|
||||||
|
{"expressionType": "SQL", "sqlExpression": "SUM(pageviews)", "label": "Pageviews"}
|
||||||
|
],
|
||||||
|
"row_limit": 20,
|
||||||
|
"time_range": "No filter",
|
||||||
|
"orientation": "vertical",
|
||||||
|
"show_legend": False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Здесь видно четыре идеи:
|
||||||
|
|
||||||
|
- `slice_name` — имя chart в Superset;
|
||||||
|
- `viz_type` — тип визуализации;
|
||||||
|
- `dataset_name` — на каком dataset строится chart;
|
||||||
|
- `params` — настройка запроса и отображения.
|
||||||
|
|
||||||
|
Ниже `DASHBOARD_CONFIG` задаёт сам dashboard:
|
||||||
|
|
||||||
|
```python
|
||||||
|
DASHBOARD_CONFIG = {
|
||||||
|
"dashboard_title": "🛒 E-commerce Analytics Dashboard",
|
||||||
|
"description": "...",
|
||||||
|
"published": True,
|
||||||
|
"slug": "ecommerce-analytics",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Dashboard находится по `slug`, а charts добавляются в layout. Если dashboard уже существует,
|
||||||
|
скрипт обновляет metadata, layout и список charts. На этом держится управляемая правка: можно
|
||||||
|
поменять параметр chart, запустить `make superset-dashboard` и увидеть результат в UI.
|
||||||
|
|
||||||
|
Native filters создаются в `build_dashboard_metadata`. Там есть фильтры:
|
||||||
|
|
||||||
|
- `Date Range` по `event_date`;
|
||||||
|
- `Country` по `geo_country`;
|
||||||
|
- `Device Type` по `device_type`;
|
||||||
|
- `Browser` по `browser_name`.
|
||||||
|
|
||||||
|
> **Что проверили по API.** Перед уроком Superset сверили через MCP Context7 (`/apache/superset`):
|
||||||
|
> в Superset есть отдельные сущности charts и dashboards, metadata хранится отдельно от
|
||||||
|
> подключаемых источников данных, а row limit — часть настройки запросов и конфигурации.
|
||||||
|
> Поэтому в уроке не лезем в REST API Superset, а работаем через уже существующий скрипт стенда.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Управляемая правка: уменьшаем Top Pages
|
||||||
|
|
||||||
|
Сейчас chart `Top Pages` показывает до 20 страниц:
|
||||||
|
|
||||||
|
```python
|
||||||
|
"row_limit": 20,
|
||||||
|
```
|
||||||
|
|
||||||
|
Сделай маленькую видимую правку: временно покажи только топ-3 страницы.
|
||||||
|
|
||||||
|
Открой [`superset/create_dashboard.py`](../../../superset/create_dashboard.py), найди chart
|
||||||
|
`Top Pages` и поменяй:
|
||||||
|
|
||||||
|
```python
|
||||||
|
"row_limit": 20,
|
||||||
|
```
|
||||||
|
|
||||||
|
на:
|
||||||
|
|
||||||
|
```python
|
||||||
|
"row_limit": 3,
|
||||||
|
```
|
||||||
|
|
||||||
|
Запусти обновление dashboard:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make superset-dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
Вернись в Superset и обнови страницу dashboard. В chart `Top Pages` должно остаться не больше
|
||||||
|
трёх страниц. Если фильтр даты снова скрыл данные, поставь **Date Range → No filter** и нажми
|
||||||
|
**Apply filters**.
|
||||||
|
|
||||||
|
Почему это хорошая маленькая правка:
|
||||||
|
|
||||||
|
- мы не меняем SQL-витрину в ClickHouse;
|
||||||
|
- не создаём новый dataset;
|
||||||
|
- не трогаем подключение к ClickHouse;
|
||||||
|
- меняем только BI-представление уже готовых данных.
|
||||||
|
|
||||||
|
### Верни как было
|
||||||
|
|
||||||
|
Верни в [`superset/create_dashboard.py`](../../../superset/create_dashboard.py):
|
||||||
|
|
||||||
|
```python
|
||||||
|
"row_limit": 20,
|
||||||
|
```
|
||||||
|
|
||||||
|
И снова запусти:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make superset-dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
После обновления страницы chart `Top Pages` снова может показывать до 20 страниц.
|
||||||
|
|
||||||
|
Если после экспериментов Superset выглядит странно, самый простой учебный возврат dashboard
|
||||||
|
metadata к конфигурации из репозитория:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make superset-init
|
||||||
|
```
|
||||||
|
|
||||||
|
Данные в ClickHouse эта команда не удаляет. Она повторно применяет подключение, datasets и
|
||||||
|
dashboard metadata Superset.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Проверь себя
|
||||||
|
|
||||||
|
| Действие | Где смотреть | Что ожидать |
|
||||||
|
|----------|--------------|-------------|
|
||||||
|
| `make transform` | ClickHouse `dm.v_events_enriched` | `count() > 0` |
|
||||||
|
| `make superset-init` | Superset → **Settings → Database Connections** | есть подключение `clickhouse_dwh` |
|
||||||
|
| открыть **Datasets** | Superset UI | есть datasets `v_events_enriched`, `v_top_pages_daily`, `dq_summary` |
|
||||||
|
| открыть dashboard | Superset UI | видны KPI, маркетинг, география и качество данных |
|
||||||
|
| поставить **Date Range → No filter** | dashboard filters | графики не скрываются из-за даты `2022-11-28` |
|
||||||
|
| поменять `row_limit` у `Top Pages` на `3` и запустить `make superset-dashboard` | chart `Top Pages` | не больше трёх страниц |
|
||||||
|
| вернуть `row_limit` на `20` и запустить `make superset-dashboard` | chart `Top Pages` | ограничение снова до 20 страниц |
|
||||||
|
|
||||||
|
Вопросы для созвона:
|
||||||
|
|
||||||
|
- чем DM-витрина отличается от Superset dataset;
|
||||||
|
- почему Superset не должен ходить напрямую в сырые STG-таблицы;
|
||||||
|
- зачем dashboard нужен `Date Range`, если SQL-витрина уже агрегирована;
|
||||||
|
- почему изменение `row_limit` — это BI-правка, а не изменение модели данных;
|
||||||
|
- где хранятся данные, а где metadata Superset.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Что должно получиться
|
||||||
|
|
||||||
|
К концу урока у тебя должен быть открытый dashboard `E-commerce Analytics Dashboard` в Superset.
|
||||||
|
Сделай скриншот после временной правки `Top Pages`: на нём должно быть видно, что chart показывает
|
||||||
|
не больше трёх страниц.
|
||||||
|
|
||||||
|
Второй артефакт — короткий абзац своими словами:
|
||||||
|
|
||||||
|
> DM-слой в ClickHouse готовит данные для потребления, dataset в Superset регистрирует эту
|
||||||
|
> витрину, chart задаёт один график, dashboard собирает charts в экран, а native filters дают
|
||||||
|
> аналитику быстрый способ менять срез данных.
|
||||||
|
|
||||||
|
После этого обязательно верни `row_limit` на `20`, чтобы следующий урок или следующий прогон
|
||||||
|
стенда начинался с исходной конфигурации.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Мост после курса
|
||||||
|
|
||||||
|
Теперь у тебя есть сквозная цепочка: Kafka → ClickHouse STG → ODS → DDS → DM → мониторинг →
|
||||||
|
Superset. Следующий честный вопрос уже не про этот стенд, а про продакшен: какие витрины стоит
|
||||||
|
материализовать, какие права дать BI-пользователям и как не превратить dashboard в единственный
|
||||||
|
источник правды вместо версионированного SQL в репозитории.
|
||||||
@@ -114,7 +114,7 @@ CHARTS_CONFIG = [
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"groupby": [],
|
"groupby": [],
|
||||||
"time_range": "Last week",
|
"time_range": "No filter",
|
||||||
"adhoc_filters": [],
|
"adhoc_filters": [],
|
||||||
"row_limit": 10000
|
"row_limit": 10000
|
||||||
}
|
}
|
||||||
@@ -231,6 +231,20 @@ DASHBOARD_CONFIG = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CHART_LAYOUT_BY_TITLE = {
|
||||||
|
"📊 Total Events": {"width": 3, "height": 32, "x": 0, "y": 0},
|
||||||
|
"👤 Unique Users": {"width": 3, "height": 32, "x": 3, "y": 0},
|
||||||
|
"🎯 Unique Sessions": {"width": 3, "height": 32, "x": 6, "y": 0},
|
||||||
|
"📈 Avg Events/Session": {"width": 3, "height": 32, "x": 9, "y": 0},
|
||||||
|
"📅 Events by Hour": {"width": 8, "height": 60, "x": 0, "y": 32},
|
||||||
|
"📱 Traffic by Device": {"width": 4, "height": 60, "x": 8, "y": 32},
|
||||||
|
"🌍 Geography Map": {"width": 6, "height": 60, "x": 0, "y": 92},
|
||||||
|
"🔗 UTM Effectiveness Table": {"width": 6, "height": 60, "x": 6, "y": 92},
|
||||||
|
"📄 Top Pages": {"width": 6, "height": 60, "x": 0, "y": 152},
|
||||||
|
"🔍 Data Quality Summary": {"width": 6, "height": 60, "x": 6, "y": 152},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def sync_query_context(chart, params: dict, dataset_id: int) -> None:
|
def sync_query_context(chart, params: dict, dataset_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
Синхронизирует сохраненный query_context с обновленными params чарта.
|
Синхронизирует сохраненный query_context с обновленными params чарта.
|
||||||
@@ -302,7 +316,7 @@ def build_dashboard_metadata(filter_dataset_id: int | None) -> str:
|
|||||||
"name": "📅 Date Range",
|
"name": "📅 Date Range",
|
||||||
"filterType": "filter_time",
|
"filterType": "filter_time",
|
||||||
"targets": [{"datasetId": filter_dataset_id, "column": {"name": "event_date"}}],
|
"targets": [{"datasetId": filter_dataset_id, "column": {"name": "event_date"}}],
|
||||||
"defaultValue": "Last week",
|
"defaultValue": "No filter",
|
||||||
"scope": {"rootPath": ["ROOT_ID"], "excluded": []},
|
"scope": {"rootPath": ["ROOT_ID"], "excluded": []},
|
||||||
"cascadeParentIds": [],
|
"cascadeParentIds": [],
|
||||||
"isInstant": True
|
"isInstant": True
|
||||||
@@ -377,6 +391,14 @@ def main() -> bool:
|
|||||||
if not dataset:
|
if not dataset:
|
||||||
logger.warning(f"Dataset '{chart_config['dataset_name']}' not found, skipping chart")
|
logger.warning(f"Dataset '{chart_config['dataset_name']}' not found, skipping chart")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if not dataset.columns:
|
||||||
|
# Если dataset создали до DDL/DM, в metadata Superset нет колонок.
|
||||||
|
# Обновляем их здесь, чтобы dashboard восстанавливался через make superset-dashboard.
|
||||||
|
dataset.fetch_metadata()
|
||||||
|
db.session.commit()
|
||||||
|
logger.info("Refreshed dataset metadata: %s", dataset.table_name)
|
||||||
|
|
||||||
datasets_by_name[chart_config["dataset_name"]] = dataset.id
|
datasets_by_name[chart_config["dataset_name"]] = dataset.id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -452,13 +474,15 @@ def main() -> bool:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Добавляем чарты в layout (grid: 12 columns)
|
# Раскладываем dashboard вручную по 12-колоночной сетке:
|
||||||
y_position = 0
|
# KPI в одну строку, затем аналитические блоки парами.
|
||||||
chart_index = 0
|
|
||||||
|
|
||||||
for chart in created_charts:
|
for chart in created_charts:
|
||||||
if chart:
|
if chart:
|
||||||
chart_component_id = f"CHART-{chart['id']}"
|
chart_component_id = f"CHART-{chart['id']}"
|
||||||
|
layout = CHART_LAYOUT_BY_TITLE.get(
|
||||||
|
chart["title"],
|
||||||
|
{"width": 6, "height": 50, "x": 0, "y": 212},
|
||||||
|
)
|
||||||
positions[chart_component_id] = {
|
positions[chart_component_id] = {
|
||||||
"id": chart_component_id,
|
"id": chart_component_id,
|
||||||
"type": "CHART",
|
"type": "CHART",
|
||||||
@@ -467,16 +491,13 @@ def main() -> bool:
|
|||||||
"meta": {
|
"meta": {
|
||||||
"chartId": chart['id'],
|
"chartId": chart['id'],
|
||||||
"sliceName": chart['title'],
|
"sliceName": chart['title'],
|
||||||
"height": 50,
|
"height": layout["height"],
|
||||||
"width": 4 if chart_index < 4 else 6,
|
"width": layout["width"],
|
||||||
"x": (chart_index % 3) * 4 if chart_index < 4 else (chart_index % 2) * 6,
|
"x": layout["x"],
|
||||||
"y": y_position,
|
"y": layout["y"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
positions["GRID_ID"]["children"].append(chart_component_id)
|
positions["GRID_ID"]["children"].append(chart_component_id)
|
||||||
chart_index += 1
|
|
||||||
if chart_index % 4 == 0:
|
|
||||||
y_position += 50
|
|
||||||
|
|
||||||
# Создаём дашборд
|
# Создаём дашборд
|
||||||
if created_charts:
|
if created_charts:
|
||||||
|
|||||||
Reference in New Issue
Block a user