feat(superset): датасеты и дашборд «Бизнес-сюжеты» (этап 8) #198
No files matched your search
@@ -47,7 +47,8 @@
|
||||
|
||||
Дашборд этапа 8 стоит только на витринах стенда: приёмка этапа не зависит
|
||||
от выполненных домашек. Расширение дашборда визуализациями по своим
|
||||
витринам — отдельная домашка после этапа 8.
|
||||
витринам — отдельная домашка после этапа 8. Устройство самого дашборда — в
|
||||
[доке зоны Superset](superset.md).
|
||||
|
||||
## Ритм пересборки
|
||||
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
# Superset: витрины глазами
|
||||
|
||||
Документ описывает зону BI: куда Superset ходит за данными и за своими
|
||||
метаданными, из чего собран стендовый дашборд, как снять свежий экспорт после
|
||||
правки в интерфейсе и почему правка руками живет только до перезапуска.
|
||||
|
||||
Состав сюжетов и колонок витрин здесь не повторяется: он живет в
|
||||
[спеке](../specs/2026-07-30-stand-v2-realism.md), раздел 7, и в
|
||||
[доке слоя DM](dm.md).
|
||||
|
||||
## Устройство зоны
|
||||
|
||||
Superset читает ClickHouse пользователем `bi` **на ноде 2** — так задано
|
||||
подключением `ClickHouse` (`clickhousedb://bi@clickhouse-02:8123`). Нода 2
|
||||
здесь — ловушка правильных ошибок из
|
||||
[спеки](../specs/2026-07-30-stand-v2-realism.md), раздел «Роли нод»: DDL идет
|
||||
с ноды 1, и забытый `ON CLUSTER` виден в дашборде.
|
||||
|
||||
Пароля в подключении нет. Его отдает штатное хранилище паролей Superset
|
||||
(`SQLALCHEMY_CUSTOM_PASSWORD_STORE` в `infra/superset/superset_config.py`),
|
||||
которое читает `CLICKHOUSE_BI_PASSWORD` из окружения. Поэтому файл подключения
|
||||
безопасно лежит в git, и экспорт из интерфейса не должен возвращать пароль
|
||||
обратно в YAML.
|
||||
|
||||
Свои метаданные — подключения, датасеты, графики, дашборды, пользователи —
|
||||
Superset держит в базе `superset` служебного `postgres-metadata`, рядом с базой
|
||||
Airflow. В ClickHouse он не пишет ничего.
|
||||
|
||||
## Дерево импорта
|
||||
|
||||
`infra/superset/import/` — это дашборд стенда в виде файлов. Каталог
|
||||
смонтирован в контейнер как `/app/import`.
|
||||
|
||||
| Файл | Что описывает |
|
||||
|---|---|
|
||||
| `metadata.yaml` | версия формата, `1.0.0`; больше в нем ничего нет |
|
||||
| `databases/clickhouse.yaml` | подключение к ClickHouse: URI без пароля и постоянный uuid |
|
||||
| `datasets/ClickHouse/*.yaml` | три витрины: `revenue_daily_v`, `purchase_vs_orders_v`, `daily_traffic_v` |
|
||||
| `charts/*.yaml` | три графика: столбики выручки, сводная таблица сверки, линии трафика |
|
||||
| `dashboards/business_stories.yaml` | дашборд «Бизнес-сюжеты» и раскладка панелей |
|
||||
|
||||
Связаны файлы не путями, а uuid. Датасет называет `database_uuid`, график —
|
||||
`dataset_uuid`, а дашборд не называет графики вовсе: единственная связь с ними
|
||||
лежит в его `position`, где узел `CHART-*` несет `meta.uuid` нужного графика.
|
||||
График, которого нет в `position`, импортируется молча и не появляется нигде.
|
||||
|
||||
Uuid у стенда розданы рукой: общее начало
|
||||
`19600000-0000-4000-8000-`, дальше номер объекта в последнем блоке — `1`–`3`
|
||||
у датасетов, `101`–`103` у графиков, `201` у дашборда
|
||||
(`…-000000000101`). Экспорт из интерфейса приносит свои, сгенерированные;
|
||||
их заменяют на эти, иначе повторный импорт заведет вторую копию объекта вместо
|
||||
обновления первой.
|
||||
|
||||
Датасет несет русские имена колонок, формат денег и метрику — а график только
|
||||
ссылается на них по имени. Значит, подпись или формат правятся в одном месте, а
|
||||
не в каждом графике.
|
||||
|
||||
Датасеты объявлены физическими (`schema: dm`, `sql: null`), хотя за именем стоит
|
||||
представление ClickHouse. Импорт в источник не ходит и разницы между таблицей и
|
||||
представлением не знает, а виртуальный датасет со своим SQL просто повторил бы
|
||||
запрос, который уже живет в `sql/ddl/60-dm-views.sql`.
|
||||
|
||||
## Импорт при каждой инициализации
|
||||
|
||||
Последняя строка `infra/superset/init.sh`:
|
||||
|
||||
```
|
||||
superset import-directory /app/import --overwrite
|
||||
```
|
||||
|
||||
Служба `superset-init` отрабатывает перед подъемом самого Superset, то есть при
|
||||
каждом `make up`. Без `--overwrite` второй прогон падал бы на первом же
|
||||
знакомом uuid; с ним объект обновляется на месте, и повторная инициализация
|
||||
проходит без ошибок.
|
||||
|
||||
Отсюда правило зоны: **стендовый дашборд не расширяют руками в интерфейсе.**
|
||||
Правка живет до следующей инициализации, а потом молча заменяется тем, что
|
||||
лежит в git. Пробовать интерфейс менти никто не мешает — но все, что должно
|
||||
пережить перезапуск, приезжает файлом.
|
||||
|
||||
## Как снять свежий экспорт
|
||||
|
||||
Маршрут один: правишь на живом стенде, забираешь экспорт, кладешь дерево в git.
|
||||
|
||||
1. Собрать или поправить график и дашборд в интерфейсе — на стенде, а не в
|
||||
файлах. Ключи `params` графиков велики, недокументированы и меняются от
|
||||
версии к версии; неверный ключ не ошибается, а рисует пустоту. Интерфейс
|
||||
здесь единственный надежный оракул.
|
||||
2. Снять экспорт и достать его из контейнера:
|
||||
|
||||
```
|
||||
docker compose exec superset superset export-dashboards -f /tmp/export.zip
|
||||
docker compose cp superset:/tmp/export.zip /tmp/export.zip
|
||||
unzip -d /tmp/superset-export /tmp/export.zip
|
||||
```
|
||||
|
||||
Внутри архива — один каталог `dashboard_export_<дата>` ровно той же формы,
|
||||
что и дерево импорта. Команда выгружает все дашборды сразу; на стенде он
|
||||
один.
|
||||
3. Вычистить экспортный сор перед коммитом:
|
||||
- выбросить экспортный `databases/` — наш `clickhouse.yaml` остается как
|
||||
есть, с постоянным uuid и без пароля;
|
||||
- оставить в `metadata.yaml` одну строку `version: 1.0.0`;
|
||||
- убрать из графиков `params.datasource`, `params.slice_id` и
|
||||
`params.dashboards` — это идентификаторы строк того Superset, где
|
||||
снимали, а импорт выводит связи из uuid;
|
||||
- убрать `query_context` — снимок запроса оттуда же. Импорт его не
|
||||
требует и обратно не заполняет: после переимпорта он остается пустым, на
|
||||
отрисовку это не влияет;
|
||||
- перезаписать русские строки читаемыми. Экспорт кодирует их
|
||||
`\uXXXX`-последовательностями, и после каждого круга их приходится
|
||||
раскодировать заново: имена графиков и подписи колонок читает менти, и в
|
||||
файле, и в диффе. Годится любой повторный дамп YAML с
|
||||
`allow_unicode=True`;
|
||||
- вернуть постоянные uuid из схемы выше.
|
||||
4. Скопировать вычищенное дерево в `infra/superset/import/` и проверить
|
||||
импортом **на пустых метаданных** — иначе прогон проверит только обновление
|
||||
уже стоящих объектов, а молчаливый пропуск нового спрячется за старым.
|
||||
Активы Superset лежат в базе `superset` службы `postgres-metadata`, поэтому
|
||||
адресный сброс — эта база:
|
||||
|
||||
```
|
||||
docker compose stop superset
|
||||
docker compose exec postgres-metadata sh -c \
|
||||
'psql -U "$POSTGRES_USER" -d postgres \
|
||||
-c "DROP DATABASE superset WITH (FORCE)" \
|
||||
-c "CREATE DATABASE superset OWNER \"$SUPERSET_METADATA_USER\""'
|
||||
docker compose run --rm superset-init
|
||||
docker compose up -d superset
|
||||
```
|
||||
|
||||
Базу приходится создавать тут же: скрипт `infra/postgres/init/` заводит ее
|
||||
один раз, при первой инициализации тома Postgres. `make rebuild-storage`
|
||||
для этой проверки не годится — он гоняет `world_recreate` в ClickHouse и
|
||||
метаданных Superset не касается. `make clean` сбросит и их, но заодно снесет
|
||||
весь стенд, включая метаданные Airflow в той же службе.
|
||||
|
||||
После импорта дашборд смотрят глазами: это единственное доказательство —
|
||||
см. ловушку ниже.
|
||||
|
||||
## Ловушки
|
||||
|
||||
**Импорт молчит.** График, чей `dataset_uuid` не разрешился, отбрасывается без
|
||||
единого слова; дашборд, у которого не сошлись ссылки на графики,
|
||||
пропускается. Ошибки уходят в журнал, и `init.sh`
|
||||
завершается нулем в любом случае. Поэтому импорт проверяют глазами или
|
||||
запросом к API — но не тем, что команда отработала.
|
||||
|
||||
**`--overwrite` синхронизирует датасет разрушительно.** Колонки и метрики
|
||||
берутся из YAML целиком: добавленная в интерфейсе метрика или подпись колонки
|
||||
исчезнет при следующем старте контейнера. Правка датасета — это правка файла.
|
||||
|
||||
## Что проверяет стенд
|
||||
|
||||
`make check-services` входит в Superset, находит подключение `ClickHouse`,
|
||||
сверяет его uuid и безопасный URI и требует схему `dm` — то есть подтверждает
|
||||
живое подключение и хранилище пароля. Сами графики и дашборд ни одна цель не
|
||||
проверяет: их смотрят глазами после переинициализации. Полная карта целей — в
|
||||
[доке проверок](testing.md).
|
||||
@@ -0,0 +1,31 @@
|
||||
slice_name: Сверка purchase и заказов
|
||||
description: null
|
||||
certified_by: null
|
||||
certification_details: null
|
||||
viz_type: pivot_table_v2
|
||||
params:
|
||||
viz_type: pivot_table_v2
|
||||
groupbyRows:
|
||||
- order_day
|
||||
groupbyColumns:
|
||||
- mismatch_class
|
||||
metrics:
|
||||
- order_count
|
||||
adhoc_filters:
|
||||
- expressionType: SIMPLE
|
||||
subject: order_day
|
||||
operator: TEMPORAL_RANGE
|
||||
comparator: No filter
|
||||
clause: WHERE
|
||||
aggregateFunction: Sum
|
||||
metricsLayout: COLUMNS
|
||||
rowOrder: key_a_to_z
|
||||
colOrder: key_a_to_z
|
||||
order_desc: true
|
||||
row_limit: 10000
|
||||
valueFormat: SMART_NUMBER
|
||||
date_format: smart_date
|
||||
cache_timeout: null
|
||||
uuid: 19600000-0000-4000-8000-000000000102
|
||||
version: 1.0.0
|
||||
dataset_uuid: 19600000-0000-4000-8000-000000000002
|
||||
@@ -0,0 +1,30 @@
|
||||
slice_name: Выручка по дням и категориям
|
||||
description: null
|
||||
certified_by: null
|
||||
certification_details: null
|
||||
viz_type: echarts_timeseries_bar
|
||||
params:
|
||||
viz_type: echarts_timeseries_bar
|
||||
x_axis: report_date
|
||||
metrics:
|
||||
- revenue
|
||||
groupby:
|
||||
- product_category
|
||||
adhoc_filters:
|
||||
- expressionType: SIMPLE
|
||||
subject: report_date
|
||||
operator: TEMPORAL_RANGE
|
||||
comparator: No filter
|
||||
clause: WHERE
|
||||
row_limit: 10000
|
||||
stack: Stack
|
||||
orientation: vertical
|
||||
show_legend: true
|
||||
rich_tooltip: true
|
||||
show_value: false
|
||||
y_axis_format: ',.2f'
|
||||
x_axis_time_format: smart_date
|
||||
cache_timeout: null
|
||||
uuid: 19600000-0000-4000-8000-000000000101
|
||||
version: 1.0.0
|
||||
dataset_uuid: 19600000-0000-4000-8000-000000000001
|
||||
@@ -0,0 +1,29 @@
|
||||
slice_name: Посетители и известные пользователи
|
||||
description: null
|
||||
certified_by: null
|
||||
certification_details: null
|
||||
viz_type: echarts_timeseries_line
|
||||
params:
|
||||
viz_type: echarts_timeseries_line
|
||||
x_axis: report_date
|
||||
metrics:
|
||||
- visitors
|
||||
- known_users
|
||||
groupby: []
|
||||
adhoc_filters:
|
||||
- expressionType: SIMPLE
|
||||
subject: report_date
|
||||
operator: TEMPORAL_RANGE
|
||||
comparator: No filter
|
||||
clause: WHERE
|
||||
row_limit: 10000
|
||||
seriesType: echarts_timeseries_line
|
||||
show_legend: true
|
||||
rich_tooltip: true
|
||||
show_value: false
|
||||
y_axis_format: ',d'
|
||||
x_axis_time_format: smart_date
|
||||
cache_timeout: null
|
||||
uuid: 19600000-0000-4000-8000-000000000103
|
||||
version: 1.0.0
|
||||
dataset_uuid: 19600000-0000-4000-8000-000000000003
|
||||
@@ -0,0 +1,105 @@
|
||||
dashboard_title: Бизнес-сюжеты
|
||||
description: null
|
||||
css: null
|
||||
slug: business-stories
|
||||
certified_by: null
|
||||
certification_details: null
|
||||
published: true
|
||||
uuid: 19600000-0000-4000-8000-000000000201
|
||||
position:
|
||||
DASHBOARD_VERSION_KEY: v2
|
||||
ROOT_ID:
|
||||
id: ROOT_ID
|
||||
type: ROOT
|
||||
children:
|
||||
- GRID_ID
|
||||
GRID_ID:
|
||||
id: GRID_ID
|
||||
type: GRID
|
||||
parents:
|
||||
- ROOT_ID
|
||||
children:
|
||||
- ROW-revenue
|
||||
- ROW-pvo
|
||||
- ROW-traffic
|
||||
ROW-revenue:
|
||||
id: ROW-revenue
|
||||
type: ROW
|
||||
parents:
|
||||
- ROOT_ID
|
||||
- GRID_ID
|
||||
children:
|
||||
- CHART-revenue
|
||||
meta:
|
||||
background: BACKGROUND_TRANSPARENT
|
||||
CHART-revenue:
|
||||
id: CHART-revenue
|
||||
type: CHART
|
||||
parents:
|
||||
- ROOT_ID
|
||||
- GRID_ID
|
||||
- ROW-revenue
|
||||
children: []
|
||||
meta:
|
||||
chartId: 1
|
||||
height: 50
|
||||
width: 12
|
||||
sliceName: Выручка по дням и категориям
|
||||
uuid: 19600000-0000-4000-8000-000000000101
|
||||
ROW-pvo:
|
||||
id: ROW-pvo
|
||||
type: ROW
|
||||
parents:
|
||||
- ROOT_ID
|
||||
- GRID_ID
|
||||
children:
|
||||
- CHART-pvo
|
||||
meta:
|
||||
background: BACKGROUND_TRANSPARENT
|
||||
CHART-pvo:
|
||||
id: CHART-pvo
|
||||
type: CHART
|
||||
parents:
|
||||
- ROOT_ID
|
||||
- GRID_ID
|
||||
- ROW-pvo
|
||||
children: []
|
||||
meta:
|
||||
chartId: 2
|
||||
height: 50
|
||||
width: 12
|
||||
sliceName: Сверка purchase и заказов
|
||||
uuid: 19600000-0000-4000-8000-000000000102
|
||||
ROW-traffic:
|
||||
id: ROW-traffic
|
||||
type: ROW
|
||||
parents:
|
||||
- ROOT_ID
|
||||
- GRID_ID
|
||||
children:
|
||||
- CHART-traffic
|
||||
meta:
|
||||
background: BACKGROUND_TRANSPARENT
|
||||
CHART-traffic:
|
||||
id: CHART-traffic
|
||||
type: CHART
|
||||
parents:
|
||||
- ROOT_ID
|
||||
- GRID_ID
|
||||
- ROW-traffic
|
||||
children: []
|
||||
meta:
|
||||
chartId: 3
|
||||
height: 50
|
||||
width: 12
|
||||
sliceName: Посетители и известные пользователи
|
||||
uuid: 19600000-0000-4000-8000-000000000103
|
||||
metadata:
|
||||
color_scheme: supersetColors
|
||||
cross_filters_enabled: false
|
||||
chart_configuration: {}
|
||||
expanded_slices: {}
|
||||
default_filters: '{}'
|
||||
native_filter_configuration: []
|
||||
theme_uuid: null
|
||||
version: 1.0.0
|
||||
@@ -0,0 +1,106 @@
|
||||
table_name: daily_traffic_v
|
||||
main_dttm_col: report_date
|
||||
currency_code_column: null
|
||||
description: null
|
||||
default_endpoint: null
|
||||
offset: 0
|
||||
cache_timeout: null
|
||||
catalog: null
|
||||
schema: dm
|
||||
sql: null
|
||||
params: null
|
||||
template_params: null
|
||||
filter_select_enabled: true
|
||||
fetch_values_predicate: null
|
||||
extra: null
|
||||
normalize_columns: false
|
||||
always_filter_main_dttm: false
|
||||
folders: null
|
||||
uuid: 19600000-0000-4000-8000-000000000003
|
||||
metrics:
|
||||
- metric_name: known_users
|
||||
verbose_name: Известные пользователи
|
||||
metric_type: null
|
||||
expression: SUM(known_users)
|
||||
description: null
|
||||
d3format: ',d'
|
||||
currency: null
|
||||
extra: null
|
||||
warning_text: null
|
||||
- metric_name: visitors
|
||||
verbose_name: Посетители
|
||||
metric_type: null
|
||||
expression: SUM(visitors)
|
||||
description: null
|
||||
d3format: ',d'
|
||||
currency: null
|
||||
extra: null
|
||||
warning_text: null
|
||||
columns:
|
||||
- column_name: _load_ts
|
||||
verbose_name: Время загрузки
|
||||
is_dttm: true
|
||||
is_active: true
|
||||
type: DATETIME64(3, 'UTC')
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: known_users
|
||||
verbose_name: Известные пользователи
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: UINT64
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: _load_id
|
||||
verbose_name: Запуск загрузки
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: STRING
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: visitors
|
||||
verbose_name: Посетители
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: UINT64
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: report_date
|
||||
verbose_name: Дата
|
||||
is_dttm: true
|
||||
is_active: true
|
||||
type: DATE
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
version: 1.0.0
|
||||
database_uuid: 4b8f2c6e-1d3a-4f5b-9c7d-2e8a1f0b3c5d
|
||||
@@ -0,0 +1,136 @@
|
||||
table_name: purchase_vs_orders_v
|
||||
main_dttm_col: order_day
|
||||
currency_code_column: null
|
||||
description: null
|
||||
default_endpoint: null
|
||||
offset: 0
|
||||
cache_timeout: null
|
||||
catalog: null
|
||||
schema: dm
|
||||
sql: null
|
||||
params: null
|
||||
template_params: null
|
||||
filter_select_enabled: true
|
||||
fetch_values_predicate: null
|
||||
extra: null
|
||||
normalize_columns: false
|
||||
always_filter_main_dttm: false
|
||||
folders: null
|
||||
uuid: 19600000-0000-4000-8000-000000000002
|
||||
metrics:
|
||||
- metric_name: order_count
|
||||
verbose_name: Количество заказов
|
||||
metric_type: null
|
||||
expression: COUNT(order_id)
|
||||
description: null
|
||||
d3format: ',d'
|
||||
currency: null
|
||||
extra: null
|
||||
warning_text: null
|
||||
columns:
|
||||
- column_name: items_total
|
||||
verbose_name: Сумма позиций заказа
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: NULLABLE(DECIMAL(18, 2))
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: _load_ts
|
||||
verbose_name: Время загрузки
|
||||
is_dttm: true
|
||||
is_active: true
|
||||
type: DATETIME64(3, 'UTC')
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: declared_revenue
|
||||
verbose_name: Выручка purchase
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: NULLABLE(DECIMAL(18, 2))
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: status
|
||||
verbose_name: Статус заказа
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: LOWCARDINALITY(NULLABLE(STRING))
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: mismatch_class
|
||||
verbose_name: Класс расхождения
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: LOWCARDINALITY(STRING)
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: order_day
|
||||
verbose_name: День заказа
|
||||
is_dttm: true
|
||||
is_active: true
|
||||
type: DATE
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: _load_id
|
||||
verbose_name: Запуск загрузки
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: STRING
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: order_id
|
||||
verbose_name: Заказ
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: STRING
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
version: 1.0.0
|
||||
database_uuid: 4b8f2c6e-1d3a-4f5b-9c7d-2e8a1f0b3c5d
|
||||
@@ -0,0 +1,138 @@
|
||||
table_name: revenue_daily_v
|
||||
main_dttm_col: report_date
|
||||
currency_code_column: null
|
||||
description: null
|
||||
default_endpoint: null
|
||||
offset: 0
|
||||
cache_timeout: null
|
||||
catalog: null
|
||||
schema: dm
|
||||
sql: null
|
||||
params: null
|
||||
template_params: null
|
||||
filter_select_enabled: true
|
||||
fetch_values_predicate: null
|
||||
extra: null
|
||||
normalize_columns: false
|
||||
always_filter_main_dttm: false
|
||||
folders: null
|
||||
uuid: 19600000-0000-4000-8000-000000000001
|
||||
metrics:
|
||||
- metric_name: revenue
|
||||
verbose_name: Выручка
|
||||
metric_type: null
|
||||
expression: SUM(revenue)
|
||||
description: null
|
||||
d3format: ',.2f'
|
||||
currency:
|
||||
symbol: ₽
|
||||
symbolPosition: suffix
|
||||
extra: null
|
||||
warning_text: null
|
||||
columns:
|
||||
- column_name: _load_ts
|
||||
verbose_name: Время загрузки
|
||||
is_dttm: true
|
||||
is_active: true
|
||||
type: DATETIME64(3, 'UTC')
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: aov
|
||||
verbose_name: Средний чек
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: DECIMAL(18, 2)
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: revenue
|
||||
verbose_name: Выручка
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: DECIMAL(18, 2)
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: product_category
|
||||
verbose_name: Категория товара
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: LOWCARDINALITY(STRING)
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: _load_id
|
||||
verbose_name: Запуск загрузки
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: STRING
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: orders
|
||||
verbose_name: Заказы
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: UINT64
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: units
|
||||
verbose_name: Товары
|
||||
is_dttm: false
|
||||
is_active: true
|
||||
type: UINT64
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
- column_name: report_date
|
||||
verbose_name: Дата
|
||||
is_dttm: true
|
||||
is_active: true
|
||||
type: DATE
|
||||
advanced_data_type: null
|
||||
groupby: true
|
||||
filterable: true
|
||||
expression: null
|
||||
description: null
|
||||
python_date_format: null
|
||||
datetime_format: null
|
||||
extra: null
|
||||
version: 1.0.0
|
||||
database_uuid: 4b8f2c6e-1d3a-4f5b-9c7d-2e8a1f0b3c5d
|
||||
@@ -1,3 +1 @@
|
||||
version: 1.0.0
|
||||
type: Database
|
||||
timestamp: '2026-07-30T00:00:00+00:00'
|
||||
Reference in new issue
Block a user