diff --git a/README.md b/README.md index 8cb4a34..3177034 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ curl http://localhost:8088/health # должно вернуть 200 Что создаётся автоматически: - **Подключение к ClickHouse**: `clickhouse_dwh` (URI: `clickhousedb://default:123456@clickhouse:8123/default`) - **Датасеты** (6 шт.): `v_events_enriched`, `v_daily_traffic`, `v_utm_effectiveness`, `v_top_pages_daily`, `v_session_overview`, `dq_summary` -- **Чарты** (10 шт.): KPI метрики, графики трафика, география, UTM-эффективность, качество данных +- **Чарты** (10 шт.): KPI метрики, графики трафика, география, UTM-эффективность, прохождение строк по слоям - **Дашборд**: "🛒 E-commerce Analytics Dashboard" ### Ручная инициализация (если автоматика не сработала) @@ -270,11 +270,11 @@ make superset-dashboard | Блок | Чарты | Датасет | |------|-------|---------| -| **KPI** | Total Events, Unique Users, Unique Sessions, Avg Events/Session | `v_events_enriched` | +| **KPI** | Total Events, Unique Users, Avg Events/Visit, Conversion to /confirmation | `v_events_enriched` | | **Динамика** | Events by Hour (timeline), Traffic by Device (pie) | `v_events_enriched` | -| **География** | World Map по странам | `v_events_enriched` | -| **Маркетинг** | UTM Effectiveness Table, Top Pages | `v_utm_effectiveness`, `v_top_pages_daily` | -| **Quality** | Data Quality Summary | `dq_summary` | +| **География** | Geography Map по странам | `v_events_enriched` | +| **Маркетинг** | UTM Effectiveness Table, Page Funnel | `v_utm_effectiveness`, `v_top_pages_daily` | +| **Слои** | Rows by Layer (event) — прохождение строк STG→ODS→DDS→DM | `dq_summary` | Фильтр `Date Range` по умолчанию открыт как `No filter`, потому что демо-данные лежат в историческом диапазоне (`2022-11-28`). diff --git a/docs/SUPERSET_DASHBOARD.md b/docs/SUPERSET_DASHBOARD.md index b6552d1..aa802f2 100644 --- a/docs/SUPERSET_DASHBOARD.md +++ b/docs/SUPERSET_DASHBOARD.md @@ -52,7 +52,7 @@ make superset-dashboard | **UTM Effectiveness** | `dm.v_utm_effectiveness` | Эффективность маркетинговых каналов | | **Top Pages** | `dm.v_top_pages_daily` | Популярность страниц | | **Session Overview** | `dm.v_session_overview` | Анализ сессий | -| **DQ Summary** | `dm.dq_summary` | Качество данных | +| **DQ Summary** | `dm.dq_summary` | Метрики по слоям (строки, ошибки, сироты) | ### Чарты (Charts) @@ -83,8 +83,26 @@ KPI разложены в одну строку по 12-колоночной с > `Featured Charts/Funnel.yaml` и frontend assets: для воронки используется > `viz_type: funnel`, поэтому dashboard создаёт именно funnel chart. -#### Качество данных -- **🔍 Data Quality Summary** — статистика по слоям STG/ODS/DDS +#### Прохождение строк по слоям +- **🧱 Rows by Layer (event)** — `dist_bar` по `dm.dq_summary`: сколько строк + одного **event-зерна** в каждом слое конвейера `STG → ODS → DDS → DM`. + +> **Почему именно одно зерно, а не сумма по слою.** Чарт берёт по одной +> канонической таблице на слой (`browser_raw → browser_event → event → +> v_events_enriched`). Если суммировать `total_rows` по всем таблицам слоя, +> в один столбец складываются таблицы разного зерна (события `1000` + визиты `99` +> + пустые error-таблицы) и получается **ложная «воронка потерь»**, которой нет. +> На одном зерне убывание становится настоящим: видимый шаг **1050 → 1000** — +> это дедупликация at-least-once потока по `event_id` в ODS +> (`ReplacingMergeTree`), а дальше число стабильно до витрины. +> +> Настоящие сигналы качества (`rows_with_errors` в ODS, `orphan_events` в DDS) +> на чистых демо-данных равны нулю и живут в `dm.dq_summary` отдельными +> `check_name` — их разбирают уроки 3–4, а не этот чарт. + +> **Порядок столбцов.** В groupby подпись слоя получает числовой префикс +> (`1 · stg`, `2 · ods`, …), а `order_bars` сортирует бары по подписи — иначе +> `dist_bar` ставит их по убыванию значения, а не по порядку конвейера. ### Фильтры (Native Filters) diff --git a/docs/course/lessons/06_superset_bi.md b/docs/course/lessons/06_superset_bi.md index 3d60cb5..6f0c759 100644 --- a/docs/course/lessons/06_superset_bi.md +++ b/docs/course/lessons/06_superset_bi.md @@ -121,12 +121,35 @@ http://localhost:8088/superset/dashboard/1/ - динамика: `Events by Hour`, `Traffic by Device`; - география: `Geography Map`; - маркетинг: `UTM Effectiveness Table`, `Page Funnel`; -- качество данных: `Data Quality Summary`. +- прохождение строк по слоям: `Rows by Layer (event)`. `Conversion to /confirmation` считается как просмотры `/confirmation` / просмотры `/home`. Это page-funnel метрика, а не доля визитов: она совпадает с тем, как ниже устроен chart `Page Funnel`. +`Rows by Layer (event)` показывает, сколько строк доходит до каждого слоя конвейера +`STG → ODS → DDS → DM`. Прежде чем читать чарт, договоримся про одно слово. + +> **Зерно (grain), он же уровень гранулярности — это что считается одной строкой +> таблицы.** У события зерно +> «одно событие = одна строка» (ключ `event_id`), у визита — «один визит = одна +> строка» (ключ `click_id`). Это разные зёрна: событий 1000, а визитов 99, потому +> что в одном визите много событий. Складывать строки разного зерна в одно число +> бессмысленно — это всё равно что сложить «штуки яблок» и «корзины яблок». + +Поэтому чарт держит **одно зерно — event**: берёт по одной канонической таблице +событий на слой (`browser_raw → browser_event → event → v_events_enriched`), а не сумму +по слою. Если просуммировать все таблицы слоя, в один столбец попадут таблицы разного +зерна (события 1000 + визиты 99 + пустые error-таблицы) и получится «воронка потерь», +которой на самом деле нет. + +Шаг **1050 → 1000** на первом переходе — это не потеря данных, а дедупликация +at-least-once потока по `event_id` в ODS (`ReplacingMergeTree`): в STG приехало 1050 строк, +но уникальных `event_id` среди них — 1000 (часть событий Kafka доставила повторно). Дальше +число стабильно. Настоящие проблемы качества (ошибки парсинга, осиротевшие события) на +чистых демо-данных равны нулю и лежат в `dm.dq_summary` отдельными `check_name` — их +разбирали уроки 3–4. + ### Фильтр даты В демо-данных события датированы `2022-11-28`. В текущей конфигурации dashboard фильтр даты @@ -139,7 +162,7 @@ http://localhost:8088/superset/dashboard/1/ - или ручной диапазон вокруг `2022-11-28`. После этого нажми **Apply filters**. Теперь смотри на dashboard как аналитик: какие графики -отвечают на бизнес-вопросы, а какие только показывают техническое качество данных. +отвечают на бизнес-вопросы, а какие только показывают техническое устройство конвейера. ### Проверяем данные напрямую в ClickHouse @@ -201,11 +224,12 @@ LEFT JOIN dds.click AS c ON c.click_id = e.click_id; | `dm.v_utm_effectiveness` | таблица по UTM-каналам | | `dm.v_top_pages_daily` | популярные страницы | | `dm.v_session_overview` | обзор сессий | -| `dm.dq_summary` | сводка качества данных по слоям | +| `dm.dq_summary` | метрики по слоям: строки (`total_rows`), ошибки, сироты | Открой [`sql/dm/40_dds_to_dm.sql`](../../../sql/dm/40_dds_to_dm.sql). Этот файл не пересчитывает -все `dm.v_*`: VIEW создаются в DDL. Здесь batch-часть наполняет `dm.dq_summary`, чтобы dashboard -мог показать качество данных после каждого прогона. +все `dm.v_*`: VIEW создаются в DDL. Здесь batch-часть наполняет `dm.dq_summary` метриками по +слоям (включая строку для слоя `dm`), чтобы dashboard мог показать прохождение строк по +конвейеру после каждого прогона. ### `init_superset.py`: подключение и datasets @@ -380,7 +404,7 @@ metadata Superset. | `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, маркетинг, география и качество данных | +| открыть dashboard | Superset UI | видны KPI, маркетинг, география и прохождение строк по слоям | | поставить **Date Range → No filter** | dashboard filters | графики не скрываются из-за даты `2022-11-28` | | поменять `row_limit` у `Page Funnel` на `3` и запустить `make superset-dashboard` | chart `Page Funnel` | не больше трёх страниц | | вернуть `row_limit` на `20` и запустить `make superset-dashboard` | chart `Page Funnel` | ограничение снова до 20 страниц | diff --git a/docs/specs/2026-06-06-superset-dashboard-redesign.md b/docs/specs/2026-06-06-superset-dashboard-redesign.md index 9351792..ce13449 100644 --- a/docs/specs/2026-06-06-superset-dashboard-redesign.md +++ b/docs/specs/2026-06-06-superset-dashboard-redesign.md @@ -63,7 +63,7 @@ geo_country: 40 | device_type: 2 (Mobile/Computer) | utm_source: 5 | utm_medium: | Geography Map | world_map | оставить (40 стран; тип модернизировать отдельно) | | Traffic by Device | pie | оставить | | Events by Hour | line | **проверить на пустоту**, иначе дропнуть | -| Data Quality Summary | dist_bar | оставить | +| Data Quality Summary | dist_bar | оставить (⚠️ позже пересмотрено — см. ниже) | ## Options considered @@ -155,3 +155,19 @@ KPI-полоса: (разнообразие `event_type`, возвраты пользователей → sessions>users, реалистичная воронка) — при возврате к генератору перенести в его `KNOWN_ISSUES.md`. + +## Пересмотр после приёмки (2026-06-06) + +Решение «Data Quality Summary | dist_bar | оставить» **пересмотрено** при визуальной +проверке дашборда. Чарт суммировал `total_rows` по всем таблицам слоя, складывая +таблицы разного зерна (события 1000 + визиты 99 + пустые error-таблицы) в один столбец, +и рисовал убывающую «воронку потерь» (stg≈4250 → ods≈2198 → dds≈1099), которой в данных +нет. Для учебного стенда это активно вводит в заблуждение. + +Чарт переделан в честный row-lineage **одного event-зерна** +(`browser_raw → browser_event → event → v_events_enriched`) и переименован в +`🧱 Rows by Layer (event)`. Теперь убывание настоящее: шаг `1050 → 1000` — это +дедупликация at-least-once потока по `event_id` в ODS (`ReplacingMergeTree`). +В `dm.dq_summary` добавлена строка для слоя `dm` (`sql/dm/40_dds_to_dm.sql`), чтобы +цепочка замыкалась до витрины. Настоящие сигналы качества (`rows_with_errors`, +`orphan_events`) на чистых демо-данных = 0 и остаются отдельными `check_name`.