Как создать дашборд в Superset: гайд для новичков и полезные лайфхаки

от автора

Superset не самый новый bi-инструмент, но его популярность продолжает расти. На мой взгляд, он достаточно прост в освоении, но все же требует некоторых усилий и времени, чтобы разобраться куда тыкать, чтобы загрузить данные и нарисовать красивый отчет. Лично я за свою карьеру в IT уже второй раз сталкиваюсь с необходимостью переезда на этот инструмент. Он снова новый для компании и многие сотрудники с ним раньше не работали. Поэтому я и решила написать статью-шпаргалку, которая поможет в несколько кликов создать работающий дашборд и всегда будет под рукой.

В дополнении к гайду напишу с какими подводными камнями мне приходилось сталкиваться, как их решить, а также покажу несколько полезных фичей Superset. Это поможет быстро разобраться в интерфейсе и знать, на что обратить внимание.

Дисклеймер: тут я не рассказываю о том, как настроить Superset или доступы к нему. Подразумевается, что у вас уже все настроено для работы. Если необходимо развернуть Superset с нуля, на Хабре и на других ресурсах есть несколько туториалов об этом.

Пару слов о принципах работы Superset

Дашборды в Superset строятся на основе чартов, чарты ‒ на основе датасетов, а датасеты ‒ на основе запросов к БД. При этом Superset не хранит результаты запросов в датасетах. Он сохраняет лишь запросы и структуру/формат колонок. Поэтому при открытии дашборда или создании чарта, Superset запускает сохранённый запрос и рисует визуализацию на основе полученных данных. Плюс такого подхода в том, что не требуется выделять много памяти для хранения данных, но есть минус ‒ при плохой архитектуре источника данных или большой нагрузке на него запросы могут работать медленно или вообще падать. Важно организовывать хранение данных в источнике, на основе которого строится дашборд, оптимально и не обращаться к тяжеловесным витринам.

Итак, чтобы создать отчет, нужно сделать три простых действия

  1. Создать датасет с данными.

  2. Построить визуализацию на этих данных.

  3. Отформатировать дашборд. 

Далее подробно о каждом этапе.

3 шага на пути к (мечте) отчету в Superset:

Шаг 1. Создаем датаcет

Создать датаcет можно несколькими способами:

  1. Заимпортить структуру существующей таблицы из БД. 

Для этого 

  • Кликаем «Datasets» -> «+ Dataset» или через «+» на главной -> «Data» -> «Create dataset».

  • Выбираем database, schema, table.

  • Нажимаем «Create dataset and create chart»*.

    *Дальше Superset автоматически перенесет нас создавать визуализацию, но к этому вернемся чуть позже.

Таким образом мы создали виртуальную таблицу со структурой и типами данных как у таблицы-источника из БД. В этом случае датасет будет иметь название таблицы-источника.

  1. Следующий способ создать датасет написать SQL запрос и сохранить результат запроса в датасете. 

По кнопкам «SQL» -> «SQL Lab» или через «+» на главной -> «SQL query» переходим в окно для работы с SQL запросами. 

  • Выбираем database, schema и по желанию table, к которой хотим подключиться, чтобы выполнить будущий запрос. 

  • Вводим запрос в окне, нажимаем «Run».

  • Если результаты запроса нас удовлетворяют, кликаем «Save» -> «Save dataset».

  • Вводим название для датасета -> «Save & Explore». 

Вот так в несколько кликов мы получили виртуальную таблицу (датасет), с которой уже можно работать и строить визуализацию. Я чаще использую второй вариант создания датасета, т.к. с помощью SQL-консоли можно сразу собрать данные из разных источников, применить необходимые фильтры или функции, создать новую колонку, а также сразу же нарисовать визуализацию, но об этом чуть позже. А первый вариант удобен тем, что для создания датасета требуется меньше действий + в качестве датасета используется продовая витрина, с которой (по хорошему) больше ничего не нужно делать.

Найти новые датасеты можно, перейдя по вкладке «Datasets», ввести название/владельца в поиске или посмотреть в списке.

Как работать c датасетами в Superset
  1. Редактирование запроса

Чтобы создать запрос к таблице (добавить/удалить колонку или изменить расчет существующей) нужно нажать «Edit» -> кликнуть на замок -> выбрать «Virtual (SQL)» (для датасета, созданного из SQL Lab этот вариант выбран автоматически) -> написать запрос и нажать «Save».

!WARNING! Чтобы увидеть изменения датасета, необходимо перейти на вкладку columns и нажать «Sync columns from source».

  1. Вкладка columns

На этой вкладке можно дописать читаемое название колонок, описание и поменять их дефолтные настройки.

  • Is temporal ‒ колонки, которые могут быть использованы для разрезов в графиках с датой/временем.

  • Default datetime ‒ настройка, позволяющая выбрать колонку типа date/datetime, которая будет по умолчанию применяться в графиках, в которых необходимо поле типа date/datetime, а также к ней будет применяться временной фильтр, созданный в дашборде.

  • Is filterable ‒ может ли столбец применяться как фильтр.

  • Is dimension ‒ флаг категориального поля, которое может применяться для группировки данных.

  1. Вкладка calculated columns

На вкладке calculated columns создаются виртуальные колонки, которые сохранятся в датасете и могут быть переиспользованы в любых визуализациях, построенных на его основе. Здесь можно привести колонку к новому типу или произвести с ней вычисления, чтобы получить новую. Основное назначение calculated columns — трансформация существующих колонок. 

Не используйте calculated columns для агрегаций! Созданные колонки будут восприниматься Superset как обычные колонки и к ним можно будет применять агрегатные функции. Например, если вы заведете в calculated columns новую колонку avg_sales = avg(sales), то при создании графика или таблицы, Superset может предложить взять среднее от нее или просуммировать значения в этой колонке, что может сильно запутать пользователя или исказить график. Агрегации с колонками лучше создать на вкладке metrics.

  1. Вкладка metrics

На вкладке metrics можно создать виртуальную метрику – колонку, которую можно будет использовать для метрик в графиках. Здесь удобно хранить поля, которые часто используются на дашборде и вычисляются либо на основе нескольких колонок, либо длинным запросом.

Подробнее о metrics и calculated colums — https://docs.preset.io/docs/using-metrics-and-calculated-columns 

Шаг 2. Рисуем визуализацию

Данные есть, нужно создать картинку. 

При первом создании датасета Superset автоматически переносит пользователя в окно редактирования визуализации. Чтобы самостоятельно попасть в редактор чартов нужно:

  • перейти во вкладку «Charts» -> «Create Chart»,

  • выбрать датасет

  • и визуализацию, которую хотим построить.

После этого мы попадаем в конструктор чарта. Нужно перенести используемые колонки, добавить фильтры (если вы планируете использовать фильтры на дашборде, то на этом этапе их можно не добавлять), при необходимости выбрать сортировку и отформатировать детали визуализации.

Популярные типы визуализаций в Superset

  1. Таблица

Подробнее о создании таблиц

Raw records

Первый вариант создания таблицы подразумевает табличное отображение только имеющихся в датасете колонок, без агрегаций.

Для создания таблицы я перенесла необходимые колонки, добавила сортировку по полю country и нажала «Update Chart».

Далее на вкладке Customize можно: 

  • выбрать формат отображения даты, 

  • добавить поисковую строку для поиска значения в таблице, 

  • добавить условное форматирование на числовое поле,

  • отредактировать формат отображения числовых полей

и многое другое.

Когда наша таблица стала выглядеть как мы хотим, нужно сохранить визуализацию по кнопке «Save». В появившемся окне вводим:

  • название чарта,

  • названия дашборда, в который мы хотим добавить чарт. 

Если дашборда с введенным названием еще не существует, Superset автоматически создаст его. 

Aggregate

Второй вариант создания таблицы ‒ таблица типа aggregate, которая позволяет помимо существующих колонок добавить агрегационную функцию.

Здесь в качестве поля, по которому происходит агрегация, выбран месяц заказа. Далее я добавила уже созданную в датасете метрику (см. пункт 1, вкладка метрики) кол-во уникальных заказов и создала новую ‒ общую стоимость заказа. 

!WARNING! Переиспользовать созданную метрику в других чартах нельзя, придется заново создавать эту метрику. Если хотите переиспользовать метрику в двух и более чартах, можно создать ее при редактировании датасета, тогда можно сразу добавить метрику в блок metrics на нескольких чартах (см. раздел «Как работать c датасетами в Superset», вкладка metrics).

В таблице типа aggregate можно добавить процентные метрики. В примере выше я считаю % от общей стоимости заказа.

Важно понимать, что это поле считает долю стоимости по строке от всей стоимости в таблице.

Если нужно считать доли в рамках отдельных групп, пропишите кастомизированную оконную функцию в блоке метрик.

Сохраняем визуализацию по кнопке «Save». В появившемся окне вводим:

  • название чарта,

  • название дашборда, в который мы хотим добавить чарт.

  1. График

Подробнее о создании графиков

Для создания графика я перенесла поле с датой на ось x, добавила метрику и фильтр на канал покупки (онлайн).

Для создания фильтра внутри чарта, нужно выбрать поле для фильтрации и условие фильтрации.

Сохраняем визуализацию по кнопке «Save». В появившемся окне вводим:

  • название чарта,

  • название дашборда, в который мы хотим добавить чарт.

Затем я создала аналогичный график, но с фильтром на офлайн-каналы.

При необходимости, можно перенести поле sales_channel в блок dimensions, тогда на одном графике будет две линии, одна ‒ онлайн-продажи, вторая ‒ офлайн-продажи.

Если мы добавили фильтры внутрь чарта, то они будут применяться к графику всегда, вне зависимости от фильтров, выбранных на дашборде.

Сохраняем визуализацию по кнопке «Save». В появившемся окне вводим:

  • название чарта,

  • название дашборда, в который мы хотим добавить чарт.

  1. Барчарт

Подробнее о барчартах

Для барчарта добавила: 

  • месяц заказа на ось x,

  • кол-во уникальных заказов в блок метрик

  • и категориальное поле региона в блок dimensions, по этому полю будут делиться столбцы.

Далее на вкладке кастомизации выбираю: 

  • формат stack, чтобы столбцы по всем регионам за 1 день были друг на друге в одном общем столбце,

  • data zoom, чтобы иметь возможность выбрать на графике, интересующий временной промежуток без применения дополнительных фильтров.

Сохраняю визуализацию по кнопке «Save». В появившемся окне ввожу:

  • название чарта,

  • название дашборда, в который мы хотим добавить чарт.

  1. Сводная таблица

Подробнее о сводных таблицах

Чтобы создать сводную таблицу нужно выбрать поля для колонок и строк, а также метрику.

Ниже можно добавить тоталы и сабтоталы при необходимости.

Сохраняем визуализацию по кнопке «Save». В появившемся окне вводим:

  • название чарта,

  • названия дашборда, в который мы хотим добавить чарт.

Шаг 3. Собираем и публикуем дашборд.

Перейти в дашборд можно при сохранении чарта по кнопке «Save & go to dashboard», или перейдя на вкладку «Dashboards» и выбрав нужный дашборд. А если дашборд не был создан ранее, то создать его можно по кнопке «+ Dashboard».

После добавления всех визуализаций наш дашборд выглядит примерно так. Кликнем «Edit» и приступим к редактированию.

Редактор 

Слева на панели редактирования находятся наши чарты, а справа ‒ вспомогательные блоки, которыми можно обогатить навигацию по дашборду и структурировать его. 

Также есть кнопка «Discard», которая отменяет все изменения и выходит из редактора, а стрелками можно откатить изменения назад или вернуть предыдущее изменение.

Первым делом я всегда добавляю блок текста. В нем можно коротко описать предназначение дашборда, указать частоту обновления данных, ваши контакты в случае вопросов и любую другую информацию, о которой важно знать пользователям дашборда.

  • Зажимаем блок «Text» и переносим его в нужное место.

  • Редактируем ширину и высоту блока.

  • Правим текст.

Составляющие дашборда удобно делить по смысловым частям с помощью блоков «Tabs». Я переношу этот блок под текст и добавляю несколько вкладок.

Теперь можно отредактировать положение чартов, перенести их в нужные вкладки и поменять высоту и ширину их блоков.

Если вы не прикрепите чарт ни к одной вкладке, то он будет показываться всегда, при переходе на любую из вкладок.

Внутри редактора можно менять название чарта. 

По окончанию редактирования нажимаем «Save». Если вы забудете нажать «Save», то изменения не сохранятся и не будет бэкапа.

Так выглядит дашборд после редактирования панелей:

Фильтры 

Панель фильтров находится слева от графиков. Для добавления фильтра нажимаем «Add/Edit filters» -> «Add filters and dividers» -> «Filter» (Divider позволяет разграничить фильтры между собой).

Для добавления фильтра необходимо выбрать его тип, название, источник данных и поле, по значениям которого будет происходить фильтрация.

Сначала добавляю фильтр на дату, к нему можно добавить описание, выбрать значение по умолчанию (filter has default value) и сделать фильтр обязательным (filter value is required).

На вкладке Scoping можно выбрать панели, на которые будет распространяться фильтр. В данном случае фильтр по дате будет действовать на все панели.

Затем я добавляю фильтр по значению и выбираю регион. Здесь список настроек фильтра гораздо больше:

  • Values are dependent on other filters ‒ значения фильтра зависят от других фильтров (в данном случае можно выбрать существующие фильтры от которых будет зависеть значение данного фильтра).

  • Pre-filter available values ‒ предфильтрация на доступные значения фильтра (помогает оптимизировать запрос путем выбора значений фильтра по заданным условиям/фильтрам). Идея в том, что если датасет очень большой, то предфильтрация доступных значений фильтра займет сильно меньше времени, если предварительно отфильтроваться по времени (или другим полям). Чтобы отобразить возможные значения фильтра регион, к БД пойдет запрос формата “select distinct region from..” c дополнительной фильтрацией по времени и запрос выполнится быстрее. Это удобно, если каждый день в базу попадают записи по всем возможным регионам. Тогда не нужно искать значения поля регион по всей таблице, можно взять лишь последний день.

  • Sort filter values ‒ сортировка значений фильтра.

  • Filter has default value ‒ фильтр имеет значение по умолчанию (задается сразу).

  • Filter value is required ‒ выбор значения фильтра ‒ обязателен.

  • Select first filter value by default ‒ по умолчанию выбирается первое значение фильтра из списка.

  • Can select multiple values ‒ можно задавать несколько значений фильтра.

  • Dynamically search all filter values ‒ по умолчанию прогружается лишь 1000 значений фильтра. Если их больше, то динамический поиск позволит динамически подгружать новые значения, пока пользователь вводит символы в поле фильтра.

  • Inverse selection ‒ выбранные в фильтре значения исключаются из данных.

Далее я выбираю панели, на которые будет распространяться данный фильтр (все, кроме графиков), и нажимаю кнопку «Save». 

!WARNING! Если забудете нажать «Save», то фильтры не добавятся и любые изменения не сохранятся!

Чтобы фильтрация применилась, необходимо не только выбрать интересующие фильтры, но и нажать «Apply filters». В правом верхнем углу на каждом графике можно увидеть примененные к нему фильтры и их значения. 

Финальная кнопка «Draft» превращается в «Published», и вуаля, ваш дашборд доступен для просмотра другим пользователям.

Настройки дашборда

В дашборде можно редактировать не только положение графиков, но и внутренние настройки. Например, кому он будет доступен для просмотра. 

Для этого кликаем по разделу Dashboards и находим карандаш напротив названия нашего дашборда. Так мы попадаем в окно редактирования свойств дашборда.

В этом окне можно задать:

  • Название дашборда

  • Кастомную URL дашборда

  • Владельцев дашборда, которые могут редактировать дашборд. Но не чарты и датасеты, которые относятся к дашборду!

  • Роли пользователей, которым дашборд будет доступен

  • Цветовую палитру

  • Людей/группу людей, которые могут делать ревью и апрув дашборда + детали (при необходимости)

  • Теги, по которым можно найти дашборд (удобно указывать тут название команды или проекта, к которым относится данный дашборд)

Ура! Дашборд создан, фильтры работают правильно, на всех графиках красивые подписи, а данные бьются. Казалось бы, мы со всем справились и можно закрывать задачу. Все так, но у меня остается козырь в рукаве, которым я бы хотела поделиться с вами, чтобы вы стали настоящими гуру Superset и могли реализовать все свои идеи в дашборде.

Кое-что на заметку

В этом разделе я поделюсь лайфхаками и фичами Superset, которые ответят на самые распространенные вопросы, возникающие в процессе работы с инструментом. Например:

  • Как бесшовно для пользователя внести изменения в опубликованный дашборд? 

  • Можно ли отправлять данные с дашборда в рассылку?

  • У меня не работают фильтры или фильтры применяются не везде, как быть?

  • Как настроить кастомное отображение дашборда и улучшить визуализацию графиков и таблиц?

  • Я не понимаю, почему графики или фильтры работают не так, как мне нужно, где искать проблему?

Дублирование дашборда 

В Superset есть возможность настроить кнопку сохранения (дублирования) дашборда. 

Жмем на три точки в правом верхнем углу дашборда -> «Save as» -> при необходимости переназываем -> ставим галочку для «also copy (duplicate) charts».

Так мы копируем существующий дашборд в новый со всеми имеющимися графиками и фильтрами. Это очень удобно, когда нужно заменить источник данных или внести другие изменения в дашборд бесшовно. На скопированном дашборде можно поменять все, что нужно, и сравнить изменения с продовым дашбордом. А когда все изменения сделаны и проверены, дубликат можно переназвать в продовый дашборд. Главное не забыть скопировать все свойства продового дашборда и удалить старый отчет.

Отправка слепка дашборда по почте

Еще в Superset можно настроить отправку слепка (скрина) отчета по почте. 

«Settings» -> «Alerts & Reports» -> «+Alert» -> заполняем информацию о работе алерта.

Как добавить алерт в Superset

Заполняем название, владельца и при необходимости описание отчета. Название алерта будет отображаться в теме письма.

Условие, по которому алерт будет отправляться (у меня это sql запрос, проверяющий, что последняя дата обновления данных в таблице это сегодня). 

Выбираем отчет, скрин которого будет отправляться на почту.

Задаем расписание отправки письма.

Указываем почтовые ящики, на которые хотим рассылать отчет.

Пример отправленного отчета:

Jinja

Это, наверное, самая нужная вещь, когда необходимо добавить в отчет график, который под капотом имеет подзапрос. Например, мы хотим показывать топ-3 региона по кол-ву заказов за определенный промежуток времени и по выбранным каналам продаж с разбивкой по странам внутри регионов. При этом временной промежуток и каналы продаж всегда разные и задаются пользователем с помощью фильтров. 

Вот так будет выглядеть график и sql-запрос, его формирующий.

Сейчас, если мы выберем временной промежуток, то он применится только ко второй части запроса и не войдет в подзапрос или вовсе не применится, потому что датасет этого графика не содержит полей, по которым можно отфильтроваться. То есть топ-3 региона считаются на данных за все время и по всем каналам продаж, а нам нужно протащить эти фильтры внутрь подзапроса.

Здесь на помощь и приходит jinja. 

Подробнее про применение jinja templates можно почитать тут.  В нашем случае переходим на вкладку «Dashboards» -> находим датасет для данного чарта -> нажимаем «Edit» -> снимаем замок и редактируем SQL запрос.

В первую и вторую часть запроса вставляем по два jinja templates:

  1. Фильтр на дату

and (     {% if from_dttm is not none %}         order_date >= toDate( '{{ from_dttm }}') AND     {% endif %}     {% if to_dttm is not none %}         order_date <= toDate('{{ to_dttm }}') AND     {% endif %}     true )

Здесь важно, чтобы переменная order_date  совпадала с названием переменной из датасета. 

Если выставлена дата начала периода (if from_dttm is not none), то преобразуем фильтр в формат Date (toDate( ‘{{ from_dttm }}’)) и сравниваем с полем order_date из датасета. Если выставлена дата окончания периода (if to_dttm is not none), то преобразуем фильтр в формат Date (toDate(‘{{ to_dttm }}’)) и сравниваем с полем order_date. Значение true важно оставить, чтобы запрос не сломался, если пользователь не выставит фильтр на временной период на дашборде.

  1. Фильтр на канал продаж

and (     {% if filter_values('sales_channel') %}         sales_channel in {{ filter_values('sales_channel')|where_in }} AND     {% endif %}     true )

Здесь тоже важно, чтобы переменная sales_channel называлась также в датасете и в фильтре на дашборде. 

При наличии выставленного фильтра if filter_values(‘sales_channel’) мы получаем список значений фильтра filter_values(‘sales_channel’) и применяем условие where_in для переменной sales_channel из датасета. Значение true важно оставить, чтобы запрос не сломался, если пользователь не выставит фильтр на канал продаж на дашборде.

После изменений в запросе не забываем нажать «Save» и вернувшись на дашборд мы увидим, что фильтры работают ожидаемым образом.

На дашборде выбран фильтр на дату и на канал продаж. В правом верхнем углу графика видим, что к нему применяется 2 фильтра. Если зайдем в запрос, формирующий график, то увидим, что оба фильтра протянулись в подзапрос и основной запрос.

Проверим, что фильтрация будет применяться, даже если выбрать только один из фильтров.

Небольшая ремарка: jinja-шаблоны добавляются в последнюю очередь, после того, как уже создан датасет, нарисован график и добавлены фильтры. Иначе, никакой график нарисовать не получится, данные просто-напросто не прогрузятся, потому что еще нет никакого фильтра и неоткуда брать его значения.

Кое-что про фильтры

Иногда, фильтр может показывать не все значения из датасета. Так происходит потому, что фильтр формируется на основе источника с ограниченным количеством строк на выходе. Например, внутри запроса происходит какая-то фильтрация или мы самостоятельно ограничиваем число строк на выходе.

В примере ниже значения фильтра «Регион» подгружаются из источника, в котором находится ограниченное кол-во записей по регионам. В фильтре их всего 3, а в целевой картине должно быть 7.

Или мы хотим иметь единый источник для фильтров дашборда. Это может быть полезно, если дашборд строится на нескольких разных источниках, в каждом из которых нет исчерпывающего набора вариантов значений для фильтра.

Для решения этих задач нам поможет создание отдельного датасета под фильтрацию. Например, словарь со всеми регионами, странами и населенными пунктами.

Так мы добавим на дашборд фильтры, которые будут забирать свои значения из этого словаря и не будут зависеть от расчетов на дашборде и значений в источниках, на которых строится дашборд.

А чтобы эти фильтры применились ко всем необходимым чартам, мы можем выбрать их в списке на вкладке Scoping.

Ниже видим, что значений в фильтре стало 7, проблема ушла.

CSS

Superset имеет множество вариантов для визуализации и кастомизации чартов, но этого не всегда хватает. Например, иногда необходимо выделить что-то цветом, добавить рамки или подписи. Здесь на помощь приходит CSS. Он позволяет дать волю творчеству и полностью кастомизировать свой отчет: добавить собственную палитру, стикеры, картинки. 

Добавлять CSS шаблоны можно двумя способами:

  • «Settings» -> «CSS Templates» -> «+ CSS Template» -> Добавляем название и новый css шаблон -> «Add». Теперь шаблон можно использовать в любом дашборде.

  • Или внутри дашборда: «Edit dashboard» -> три точки -> «Edit CSS» -> загрузить имеющийся шаблон или написать свой код. Тогда любые изменения применяются в лайв-режиме и будут действовать только в этом дашборде.

Я не буду подробно останавливаться на реализации, а просто обозначу такую возможность. Так как основательный рассказ про CSS выльется в целую статью, а может даже в несколько.

Про кастомизацию сводной таблицы есть здесь. А про использование CSS можно почитать тут и тут.

View query

Об этом было немного в пункте про Jinja и хочется подчеркнуть полезность этой фичи. Для любого графика на дашборде можно посмотреть запрос его формирующий:

  • наводим на три точки в правом верхнем углу графика,

  • нажимаем «View query».

Перед нами появляется окно с запросом, который формирует выбранный график. Virtual table – запрос, формирующий датасет для чарта. Расчеты, которые производятся поверх virtual table – все, что рассчитывается на основе датасета, те метрики, которые создавались на этапе создания чарта.

Ниже несколько примеров запросов и их расшифровка.

Эта фича позволяет заглянуть под капот графика и отладить его. Очень рекомендую использовать эту функцию на этапе тестирования фильтров и всегда, когда визуализация не соответствует ожиданиям.

Заключение

В этом туториале мы разобрались что нужно прокликать, чтобы нарисовать дашборд в Superset. Надеюсь, эта статья была вам полезна, и вы поделитесь ей с коллегами и друзьями. Я планирую дополнять эту статью новыми полезными лайфхаками, поэтому сохраняйте её, чтобы всегда иметь под рукой.

Не забывайте кликать sync columns при изменении датасета, cделайте кнопку save вашим самым главным другом при работе с суперестом и творите! 🙂 Всем понятных и не падающих отчетов!

Источник данных для дашборда


ссылка на оригинал статьи https://habr.com/ru/articles/859140/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *