Пара шаблонов кастомизированных элементов Handlebars для Apache Superset
Готовые шаблоны Handlebars для Apache Superset
Apache Superset всё чаще становится выбором для визуализации данных благодаря открытому коду. Но, увы, столкнувшись с его ограничениями и тонкостями, даже самые опытные пользователи могут столкнуться с трудностями. Есть много ограничений, которые требуют обращения за доработками к разработчикам, но с помощью шаблона Handlebars в сочетании с шаблонизацией jinja некоторые трудности можно обойти.
С его помощью можно внедрить web-верстку прямо в ваши дашборды, обходя множество подводных камней. Готовых шаблонов для handlebars (superset) мало, так как это довольно трудоемкая задача, часто выходящая за рамки работы с готовыми BI-системами.
Работая аналитиком данных и столкнувшись с ограничениями в вёрстке дашборда, пришлось разработать несколько кастомизированных шаблонов, которые можно быстро применить на практике. Если пользователь не знаком с html и css — поначалу это будет довольно сложно, но постараюсь как можно подробнее расписать значение классов и элементов шаблона для неопытных пользователей, заодно можно немного погрузиться в мир web-разработки :).
Общие правила использования Handlebars для Superset:
сначала создаём необходимые показатели, которые будут отображаться в карточках,
Использование графиков Handlebars (названия окон для кода могут различаться, в зависимости от версии)
размещаем код HTML + jinja в первом окне, CSS во втором. Элементы jinja заключаются в двойные фигурные скобки {{ col1 }}, внутри ваш показатель, он автоматически изменяется по мере обновления данных,
Пара {{#each data}} … {{/each}}: блок шаблона Handlebars, который проходит по каждому элементу массива data и генерирует соответствующую разметку.
пользуясь подсказками к коду CSS, меняем цвета, размер полей и т.д. в соответствии со своими потребностями.
Первый шаблон
Набор карточек с общим показателем на 2 строки.
Набор карточек, с возможностью регулирования полей между всеми элементами, они автоматически растягиваются под размер контейнера
Иногда нужны довольно мелкие карточки для показателей и не всегда возможно много разместить на дашборде. Некоторым неудобством является, что для каждой карточки нужно прописать отдельный показатель если хочется разные сущности отобразить в одном контейнере, но это иногда и упрощает задачу по поиску нарушений в данных.
Готовый код HTML + jinja размещаемый в первом окне Handlebars Template:
{{#each data}}
ВСЕГО
{{ ppt_all }}
РЛС
{{ rls }}
ЛА
{{ la }}
ВО
{{ vo }}
ЛВО
{{ lvo }}
ТН
{{ tn }}
ОХО
{{ oho }}
КАК
{{ kak }}
ИСА
{{ isa }}
КХО
{{ kho }}
КП
{{ kp }}
СТИ ВСЕГО
11 344 тыс. м.
ГМС ВСЕГО
2 479 тыс. м.
ЦСМ ВСЕГО
2 650 тыс. м.
ГПК ВСЕГО
3 345 тыс. м.
{{/each}}
Готовый код CSS для второго окна:
.f-container {
display: flex;
gap: 14px;
}
.left-f {
flex: 0.4;
width: 160px;
}
.right-column {
flex: 3;
display: grid;
flex-direction: column;
gap: 10px;
}
.top-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
margin-bottom: 14px;
gap: 10px;
}
.bottom-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 10px;
}
.f-item {
font-weight:bolder;
border: solid 1px lightgray;
background: #fff;
padding: 10px 8px 6px 8px;
border-radius: 12px;
text-align: center;
box-shadow: 0 4px 4px rgba(0,0,0,0.2);
transition: transform 0.3s, box-shadow 0.3s;
}
.f-item:hover {
transform: translateY(-5px);
box-shadow: 0 8px 10px rgba(0,0,0,0.2);
}
.f-item h5 {
margin: 0 0 8px 0;
font-weight:bolder;
}
.glav {
font-weight:bolder;
padding-top: 30px;
font-size: 22px;
color: #c90808;
}
.all {
font-size: 26px;
color: #c90808;
}
.number {
font-weight:bolder;
font-size: 20px;
color: #2e75b6;
}
.header1 {
font-weight:bolder;
color: #2e75b6;
}
.spp {
font-style: italic;
}
Подробное объяснение css для тех, кто web-вёрсткой обычно не занимается, но общее представление имеет
.f-container { /*общий контейнер*/
display: flex; /*позволяет элементы в строку или столбец с гибким расположением.*/
gap: 14px; /*промежуток между элементами внутри flex-контейнера*/
}
.left-f { /*большая карточка*/
/*коэффициент гибкости для элемента внутри flex-контейнера*/
/*в данном случае элемент будет занимать 0.4 доли доступного пространства*/
flex: 0.4;
width: 160px; /*фиксированная ширина элемента в пикселях*/
}
.right-column { /*колонка с двумя строками карточек*/
/*коэффициент гибкости для элемента, позволяя ему занимать*/
/*в 3 раза больше пространства по сравнению с элементами с меньшим значением*/
flex: 3;
display: grid; /*создание сеточной компоновки*/
flex-direction: column; /*определяет направление расположения элементов внутри flex-контейнера по вертикали (в столбец)*/
gap: 10px;
}
.top-row { /*верхняя строка*/
display: grid;
/*определяет шаблон столбцов сетки, автоматически подгоняя количество столбцов*/
/*под доступное пространство с минимальной шириной 70 пикселей*/
/*и распределением оставшегося пространства поровну (1fr).*/
grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
margin-bottom: 14px; /* внешний отступ под элементом*/
gap: 10px; /*промежуток между элементами сетки*/
}
.bottom-row { /*нижняя строка*/
display: grid;
/*настраивает шаблон столбцов сетки с автоматическим подбором количества столбцов,*/
/*минимальная ширина каждого столбца 120 пикселей, а оставшееся пространство*/
/*распределяется равномерно (1fr)*/
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 10px;
}
.f-item { /*внешний вид всех карточек*/
font-weight:bolder; /*более жирное начертание шрифта*/
border: solid 1px lightgray; /*рамка карточек с шириной и серым цветом*/
background: #fff; /*белый фон для элемента*/
/*внутренние отступы: сверху 10px, справа 8px, снизу 6px, слева 8px*/
padding: 10px 8px 6px 8px;
border-radius: 12px; /*закругляет углы элемента радиусом 12 пикселей*/
text-align: center; /*выравнивает текст внутри элемента по центру*/
/*добавляет тень к элементу с отступом по оси X: 0, по оси Y: 4px,*/
/*размытием: 4px и цветом с прозрачностью 20%*/
box-shadow: 0 4px 4px rgba(0,0,0,0.2);
/*определяет переходные эффекты для свойств transform*/
/*и box-shadow с длительностью 0.3 секунды*/
transition: transform 0.3s, box-shadow 0.3s;
}
.f-item:hover { /*анимация карточек*/
/*при наведении курсора элемент сдвигается вверх на 5 пикселей*/
transform: translateY(-5px);
/*усиливается тень элемента: вертикальный отступ 8px, размывание 10px*/
/*с тем же цветом и прозрачностью.*/
box-shadow: 0 8px 10px rgba(0,0,0,0.2);
}
.f-item h5 { /*внешние отступы для заголовков верхних карточек*/
margin: 0 0 8px 0;
font-weight:bolder;
}
.glav { /*текст на большой карточке*/
font-weight:bolder;
padding-top: 30px; /*внутренний отступ*/
font-size: 22px; /*размер шрифта*/
color: #c90808; /*цвет текста*/
}
.all { /*показатель на большой карточке*/
font-size: 26px;
color: #c90808; /*цвет текста - оттенок красного*/
}
.number { /*стилизация цифр карточек в верхней строке*/
font-weight:bolder;
font-size: 20px;
color: #2e75b6; /*цвет текста - оттенок синего*/
}
.header1 { /*заголовки карточек нижней строки*/
font-weight:bolder;
color: #2e75b6;
}
.spp {
font-style: italic; /*стиль текста - курсив*/
}
Второй шаблон
Имитация горизонтального бара
График с надписями на цветовых элементах и показателем с общей суммой со всплывающими подсказками. Цветовые шкалы меняются в процентном соотношении друг к другу.
Лучше всего выглядит для двух качественных значений, с уже накопленными показателями.
Готовый код HTML + jinja размещаемый в первом окне Handlebars Template
{{#each data}}
{{/each}}
Готовый код CSS для второго окна:
.hb-ppt-oc {
width: 100%;
margin: 0 auto;
}
.bar-container {
width: 100%;
height: 50px;
}
.bars {
display: flex;
align-items: center;
height: 100%;
width: calc(100% - 150px);
}
.bar-summ {
margin-left: 20px;
font-size: 18px;
font-weight: bolder;
flex: 0 0 150px;
text-align: right;
}
.bar {
display: flex;
align-items: center;
justify-content: center;
height: 95%;
position: relative;
color: white;
font-weight: bold;
}
.gray-bar {
margin-left: 20px;
background-color: #9c9c9b;
padding: 0 5px;
border-radius: 6px 0px 0px 6px;
margin-right: 0;
}
.red-bar {
background-color: #e06969;
border-radius: 0px 6px 6px 0px;
}
.bar-label {
position: absolute;
width: 100%;
text-align: center;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Подробное объяснение css для тех, кто вёрсткой обычно не занимается, но общее представление имеет
.hb-ppt-oc { /*общий контейнер по центру и занимает всю доступную ширину*/
width: 100%; /*занимает всю доступную ширину родительского контейнера*/
margin: 0 auto; /*центрирует контейнер по горизонтали*/
}
/*Внутри .hb-ppt-oc, для каждого элемента данных ({{#each data}}),*/
/*создаётся .bar-container с фиксированной высотой.*/
.bar-container {
width: 100%;
height: 50px; /*фиксированная высота для строки баров*/
}
.bars { /*Flex-контейнер внутри .bar-container*/
display: flex; /*Flexbox для горизонтального расположения*/
align-items: center; /*вертикальное выравнивание элементов по центру*/
height: 100%; /*высота равна высоте .bar-container*/
width: calc(100% - 150px); /*занимает всю ширину минус 150px для .bar-summ*/
}
.bar-summ { /*контейнер для подписи БЮДЖЕТ и числового показателя суммы*/
margin-left: 20px; /*отступ слева для отделения от бара*/
font-size: 18px; /*размер шрифта*/
font-weight: bolder; /*более жирный шрифт*/
flex: 0 0 150px; /*фиксированная ширина элемента с суммой*/
text-align: right; /*выравнивание текста по правому краю*/
}
.bar {
display: flex;
align-items: center;
justify-content: center; /*центрирование по горизонтали*/
height: 95%; /*уменьшенние высоты для визуального отступа*/
position: relative; /*позиционирование дочерних элементов .bar-label*/
color: white; /*белый цвет текста на цветных барах*/
font-weight: bold;
}
.gray-bar { /*серый бар*/
margin-left: 20px; /*отступ слева для отделения от предыдущего элемента*/
background-color: #9c9c9b; /*серый цвет фона бара*/
padding: 0 5px; /*внутренние отступы слева и справа*/
border-radius: 6px 0px 0px 6px; /*скругление левых углов баров*/
margin-right: 0; /*без отступа справа*/
}
.red-bar { /*красный бар*/
background-color: #e06969;
border-radius: 0px 6px 6px 0px;
}
.bar-label { /*текстовые описания и значение для баров*/
position: absolute; /*абсолютное позиционирование относительно .bar*/
width: 100%;
text-align: center;
font-size: 14px;
white-space: nowrap; /*запрет переноса текста на новую строку*/
overflow: hidden; /*скрывает содержимое, выходящее за пределы блока*/
text-overflow: ellipsis; /*добавляет троеточие при обрезке текста*/
}
В завершение, несколько подсказок:
нужно аккуратно относиться к форматированию кода в окнах, он может не срабатывать и выдавать ошибки если есть подсказки или пробелы в строках;
элементы классов и элементов css лучше для каждого графика называть индивидуально, т.к. они будут влиять на все графики на дашборде с одинаковыми именами классов, вы будете менять что-то в коде, а это не будет срабатывать или наоборот срабатывать не там :);
осторожнее с body, его включать в код не стоит, он будет влиять на весь дашборд;
можно было бы уменьшить код, и не прописывать каждый показатель отдельно в первом случае, а просто поместить колонку с названием показателей в код html, но карточки с нулевыми значениями показателя не появлялись, даже если в вычислении использовался SQL-запрос: COALESCE (COUNT (col1), 0).
Данные полностью выдуманы, никаких инсайтов можно не искать, если есть конструктивные предложения по улучшению кода, делитесь подсказками.