Пара шаблонов кастомизированных элементов Handlebars для Apache Superset

от автора

Готовые шаблоны Handlebars для Apache Superset

Готовые шаблоны Handlebars для Apache Superset

Apache Superset всё чаще становится выбором для визуализации данных благодаря открытому коду. Но, увы, столкнувшись с его ограничениями и тонкостями, даже самые опытные пользователи могут столкнуться с трудностями. Есть много ограничений, которые требуют обращения за доработками к разработчикам, но с помощью шаблона Handlebars в сочетании с шаблонизацией jinja некоторые трудности можно обойти.

С его помощью можно внедрить web-верстку прямо в ваши дашборды, обходя множество подводных камней. Готовых шаблонов для handlebars (superset) мало, так как это довольно трудоемкая задача, часто выходящая за рамки работы с готовыми BI-системами.

Работая аналитиком данных и столкнувшись с ограничениями в вёрстке дашборда, пришлось разработать несколько кастомизированных шаблонов, которые можно быстро применить на практике. Если пользователь не знаком с html и css — поначалу это будет довольно сложно, но постараюсь как можно подробнее расписать значение классов и элементов шаблона для неопытных пользователей, заодно можно немного погрузиться в мир web-разработки :).

Общие правила использования Handlebars для Superset:

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

Использование графиков Handlebars (названия окон для кода могут различаться, в зависимости от версии)

Использование графиков Handlebars (названия окон для кода могут различаться, в зависимости от версии)
  • размещаем код HTML + jinja в первом окне, CSS во втором. Элементы jinja заключаются в двойные фигурные скобки {{ col1 }}, внутри ваш показатель, он автоматически изменяется по мере обновления данных,

Пара {{#each data}} … {{/each}}: блок шаблона Handlebars, который проходит по каждому элементу массива data и генерирует соответствующую разметку.

  • пользуясь подсказками к коду CSS, меняем цвета, размер полей и т.д. в соответствии со своими потребностями.

Первый шаблон

Набор карточек с общим показателем на 2 строки.

Набор карточек, с возможностью регулирования полей между всеми элементами, они автоматически растягиваются под размер контейнера

Набор карточек, с возможностью регулирования полей между всеми элементами, они автоматически растягиваются под размер контейнера

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

Готовый код HTML + jinja размещаемый в первом окне Handlebars Template:

<div class="f-section">   {{#each data}}   <div class="f-container">     <div class="f-item left-f">       <div class="glav">ВСЕГО</div>       <div class="all">{{ ppt_all }}</div>     </div>     <div class="right-column">       <div class="top-row">         <div class="f-item">           <h5>РЛС</h5>           <div class="number">{{ rls }}</div>         </div>         <div class="f-item">           <h5>ЛА</h5>           <div class="number">{{ la }}</div>         </div>         <div class="f-item">           <h5>ВО</h5>           <div class="number">{{ vo }}</div>         </div>         <div class="f-item">           <h5>ЛВО</h5>           <div class="number">{{ lvo }}</div>         </div>         <div class="f-item">           <h5>ТН</h5>           <div class="number">{{ tn }}</div>         </div>         <div class="f-item">           <h5>ОХО</h5>           <div class="number">{{ oho }}</div>         </div>         <div class="f-item">           <h5>КАК</h5>           <div class="number">{{ kak }}</div>         </div>         <div class="f-item">           <h5>ИСА</h5>           <div class="number">{{ isa }}</div>         </div>         <div class="f-item">           <h5>КХО</h5>           <div class="number">{{ kho }}</div>         </div>         <div class="f-item">           <h5>КП</h5>           <div class="number">{{ kp }}</div>         </div>       </div>       <div class="bottom-row">         <div class="f-item">           <div class="header1">СТИ ВСЕГО</div>           <div class = "spp">11 344 тыс. м.</div>         </div>         <div class="f-item">           <div class="header1">ГМС ВСЕГО</div>           <div class = "spp">2 479 тыс. м.</div>         </div>         <div class="f-item">           <div class="header1">ЦСМ ВСЕГО</div>           <div class = "spp">2 650 тыс. м.</div>         </div>         <div class="f-item">           <div class="header1">ГПК ВСЕГО</div>           <div class = "spp">3 345 тыс. м.</div>         </div>       </div>     </div>   </div>  {{/each}} </div>

Готовый код 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

<div class="hb-ppt-oc">   {{#each data}}   <div class="bar-container">     <div class="bars">       <div class="bar gray-bar"             style="flex-grow: {{ ppt_1 }};"             title="Общие: {{ ppt_1 }}">         <span class="bar-label">ОБЩИЕ: {{ ppt_1 }}</span>       </div>       <div class="bar red-bar"             style="flex-grow: {{ ppt_2 }};"             title="Сезонные: {{ ppt_2 }}">         <span class="bar-label">СЕЗОННЫЕ: {{ ppt_2 }} </span>       </div>       <div class="bar-summ">БЮДЖЕТ: {{ inv }}</div>     </div>   </div>   {{/each}} </div>

Готовый код 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).

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


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


Комментарии

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

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