feat: добавлен дашборд Superset для e-commerce аналитики

- Добавлен сервис superset-init в docker-compose для автоматической инициализации
- Созданы Python-скрипты для инициализации подключения ClickHouse и создания датасетов
- Создан скрипт для автоматического создания дашборда с 10 чартами
- Создан скрипт экспорта дашборда в JSON
- Добавлен экспортируемый JSON дашборда (ecommerce_analytics.zip.json)
- Обновлен Makefile с командами superset-init, superset-dashboard, superset-export
- Добавлена документация docs/SUPERSET_DASHBOARD.md

Дашборд включает:
- KPI блок (Total Events, Unique Users, Sessions, Avg/Session)
- Динамика трафика (Events by Hour, Traffic by Device)
- География (World Map)
- Маркетинг (UTM Effectiveness Table, Top Pages)
- Качество данных (DQ Summary)
- Native Filters (Date Range, Country, Device, Browser)
This commit is contained in:
2026-02-10 20:48:05 +03:00
parent 21fd58dc9d
commit 2e48f6065a
7 changed files with 1351 additions and 2 deletions
+221
View File
@@ -0,0 +1,221 @@
#!/usr/bin/env python3
"""
================================================================================
Скрипт инициализации Superset для проекта ClickHouse Mini DWH
================================================================================
Назначение:
- Создание подключения к ClickHouse (Database connection)
- Импорт датасетов из витрин DM-слоя
- Импорт чартов и дашбордов
Запуск:
Внутри контейнера superset:
python /app/superset_init/init_superset.py
Требования:
- Запущенный ClickHouse с созданными витринами в схеме dm
- Superset инициализирован (superset db upgrade, admin создан)
================================================================================
"""
import os
import sys
import json
import logging
from typing import Optional
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Добавляем путь к superset
sys.path.insert(0, '/app')
try:
from superset.app import create_app
from superset.extensions import db
from superset.models.core import Database
from superset.connectors.sqla.models import SqlaTable, TableColumn
from superset.charts.data_access_layer import ChartDAO
from superset.dashboards.data_access_layer import DashboardDAO
from superset.commands.dataset.create import CreateDatasetCommand
from sqlalchemy.exc import IntegrityError
except ImportError as e:
logger.error(f"Failed to import Superset modules: {e}")
sys.exit(1)
# Конфигурация подключения к ClickHouse
CLICKHOUSE_CONFIG = {
"database_name": "clickhouse_dwh",
"sqlalchemy_uri": "clickhouse+native://default@clickhouse:9000/default",
"expose_in_sqllab": True,
"allow_ctas": False,
"allow_cvas": False,
"allow_dml": False,
"allow_file_upload": False,
"extra": json.dumps({
"engine_params": {},
"metadata_params": {},
"schemas_allowed_for_file_upload": []
})
}
# Датасеты для импорта из DM-слоя
DATASETS = [
{
"table_name": "v_events_enriched",
"schema": "dm",
"database_name": "clickhouse_dwh",
"description": "Полная обогащённая витрина событий (event + click)"
},
{
"table_name": "v_daily_traffic",
"schema": "dm",
"database_name": "clickhouse_dwh",
"description": "Агрегация трафика по дням и измерениям"
},
{
"table_name": "v_utm_effectiveness",
"schema": "dm",
"database_name": "clickhouse_dwh",
"description": "Эффективность UTM-кампаний"
},
{
"table_name": "v_top_pages_daily",
"schema": "dm",
"database_name": "clickhouse_dwh",
"description": "Популярность страниц по дням"
},
{
"table_name": "v_session_overview",
"schema": "dm",
"database_name": "clickhouse_dwh",
"description": "Обзор сессий пользователей"
},
{
"table_name": "dq_summary",
"schema": "dm",
"database_name": "clickhouse_dwh",
"description": "Сводка по качеству данных"
}
]
def create_clickhouse_connection(app) -> Optional[Database]:
"""Создание подключения к ClickHouse"""
with app.app_context():
logger.info("Creating ClickHouse database connection...")
# Проверяем, существует ли уже подключение
existing = db.session.query(Database).filter_by(
database_name=CLICKHOUSE_CONFIG["database_name"]
).first()
if existing:
logger.info(f"Database connection '{CLICKHOUSE_CONFIG['database_name']}' already exists")
return existing
try:
database = Database(**CLICKHOUSE_CONFIG)
db.session.add(database)
db.session.commit()
logger.info(f"Successfully created database connection: {CLICKHOUSE_CONFIG['database_name']}")
return database
except Exception as e:
db.session.rollback()
logger.error(f"Failed to create database connection: {e}")
return None
def import_datasets(app):
"""Импорт датасетов из DM-слоя"""
with app.app_context():
logger.info("Importing datasets...")
# Получаем ID базы данных
database = db.session.query(Database).filter_by(
database_name="clickhouse_dwh"
).first()
if not database:
logger.error("ClickHouse database connection not found")
return False
imported_count = 0
for dataset_config in DATASETS:
try:
# Проверяем, существует ли датасет
existing = db.session.query(SqlaTable).filter_by(
table_name=dataset_config["table_name"],
schema=dataset_config["schema"]
).first()
if existing:
logger.info(f"Dataset '{dataset_config['table_name']}' already exists")
continue
# Создаём датасет
dataset = SqlaTable(
table_name=dataset_config["table_name"],
schema=dataset_config["schema"],
database_id=database.id,
database=database,
description=dataset_config["description"],
is_sqllab_view=False
)
db.session.add(dataset)
db.session.flush()
# Fetch columns from database
dataset.fetch_metadata()
db.session.commit()
logger.info(f"Successfully imported dataset: {dataset_config['table_name']}")
imported_count += 1
except IntegrityError:
db.session.rollback()
logger.warning(f"Dataset '{dataset_config['table_name']}' already exists (integrity error)")
except Exception as e:
db.session.rollback()
logger.error(f"Failed to import dataset '{dataset_config['table_name']}': {e}")
logger.info(f"Imported {imported_count} new datasets")
return True
def main():
"""Главная функция инициализации"""
logger.info("=" * 60)
logger.info("Superset Initialization for ClickHouse Mini DWH")
logger.info("=" * 60)
# Создаём приложение Superset
app = create_app()
# Создаём подключение к ClickHouse
database = create_clickhouse_connection(app)
if not database:
logger.error("Failed to create ClickHouse connection")
sys.exit(1)
# Импортируем датасеты
if not import_datasets(app):
logger.error("Failed to import datasets")
sys.exit(1)
logger.info("=" * 60)
logger.info("Superset initialization completed successfully!")
logger.info("=" * 60)
logger.info("Available datasets:")
for ds in DATASETS:
logger.info(f" - {ds['schema']}.{ds['table_name']}")
logger.info("=" * 60)
if __name__ == "__main__":
main()