<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0">

<channel>

<title>Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: bootstrap</title>
<link>http://test.leftjoin.ru/tags/bootstrap/</link>
<description></description>
<generator>E2 (v3365; Aegea)</generator>

<item>
<title>Создаём дашборд на Bootstrap с нуля (Часть 1)</title>
<guid isPermaLink="false">55</guid>
<link>http://test.leftjoin.ru/all/untappd-bootstrap-dashboard-part-1/</link>
<comments>http://test.leftjoin.ru/all/untappd-bootstrap-dashboard-part-1/</comments>
<description>
&lt;p&gt;В прошлых материалах мы познакомились с фреймворком Plotly Dash, &lt;a href="http://test.leftjoin.ru/all/scatter-plot-untappd"&gt;научились строить scatter plots&lt;/a&gt; и &lt;a href="http://test.leftjoin.ru/all/plotly-russian-map/"&gt;визуализировать данные на карте&lt;/a&gt;. Сегодня мы попробуем объединить имеющиеся части в одном веб-приложении и расскажем как можно создать полноценный дашборд используя сетчатую структуру Bootstrap.&lt;br /&gt;
В этом нам поможет &lt;a href="https://dash-bootstrap-components.opensource.faculty.ai/"&gt;dash-bootstrap-components&lt;/a&gt;, эта библиотека позволяет строить дашборды по принципу  “plug-and-play”,  добавлять любые компоненты Bootstrap и стилизовать их используя грид-дизайн.&lt;/p&gt;
&lt;h2&gt;Подготовка макета&lt;/h2&gt;
&lt;p&gt;Перед созданием любого приложения, не только дашборда нам просто необходим план, черновой макет, следуя которому, мы бы видели общую картину и могли быстро внести корректировки в структуру. Для создания макета дашборда мы использовали приложение &lt;a href="https://app.diagrams.net/"&gt;draw.io&lt;/a&gt;, этот инструмент позволяет быстро создавать диаграммы, графики, блок-схемы, формы и т. д. Наш дашборд будет построен по следующему макету:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/1-10.png" width="927" height="1045" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Как и сам дашборд, шапка будет оформлена в главных цветах &lt;a href="https://untappd.com/"&gt;Untappd&lt;/a&gt; — белом и золотом. Ниже расположится раздел с пивоварнями, состоящий из scatter plot и панели с настройками. А в самом низу будет карта, показывающая средний рейтинг напитка по регионам России.&lt;/p&gt;
&lt;p&gt;Итак, приступим, для начала создадим файл application.py, в нем будут храниться все фронтенд элементы дашборда, и папку assets в той же директории, структура должна быть такой:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;- application.py
- assets/
    |-- typography.css
    |-- header.css
    |-- custom-script.js
    |-- image.png&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Импортируем нужные библиотеки и инициализируем приложение:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
import dash_core_components as dcc
import pandas as pd
from get_ratio_scatter_plot import get_plot
from get_russian_map import get_map
from clickhouse_driver import Client
from dash.dependencies import Input, Output

standard_BS = dbc.themes.BOOTSTRAP
app = dash.Dash(__name__, external_stylesheets=[standard_BS])&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Главные аргументы для app:&lt;br /&gt;
&lt;span class="inline-code"&gt;__name__&lt;/span&gt; — для активации cтатических элементов из папки assets (картинки, CSS и JS файлы)&lt;br /&gt;
&lt;span class="inline-code"&gt;external_stylesheets&lt;/span&gt; — внешний CSS для стилизации дашборда, здесь мы используем стандартный BootstrapCDN, однако вы можете создать свой или &lt;a href="https://www.bootstrapcdn.com/bootswatch/"&gt;воспользоваться одним из уже готовых стилей&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Включаем ещё  несколько параметров для работы с локальными файлами и подключаемся к ClickHouse:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;app.scripts.config.serve_locally = True
app.css.config.serve_locally = True

client = Client(host='',
                user='default',
                password='',
                port='9000',
                database='default')&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Добавим палитру цветов:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;colors = ['#ffcc00', 
          '#f5f2e8', 
          '#f8f3e3',
          '#ffffff', 
          ]&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Верстка макета&lt;/h2&gt;
&lt;p&gt;Все элементы дашборда будут помещены в Bootstrap Контейнер, который находится в блоке &amp;lt;div&amp;gt;:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;- app 
    |-- div
     |-- container
      |-- logo&amp;amp;header
     |-- container
      |-- div
       |-- controls&amp;amp;scatter
       |-- map&lt;/code&gt;&lt;/pre&gt;&lt;pre class="e2-text-code"&gt;&lt;code&gt;app.layout = html.Div(
                    [
                        dbc.Container(

                                         &amp;lt; шапка &amp;gt;
                         
                        dbc.Container(       
                            html.Div(
                                [
                        
                                    &amp;lt; основной код &amp;gt;
                        
                                ],
                            ),
                            fluid=False, style={'max-width': '1300px'},
                        ),
                    ],
                    style={'background-color': colors[1], 'font-family': 'Proxima Nova Bold'},
                )&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Здесь мы сразу задаем фиксированную ширину контейнера, цвет фона и стиль шрифта на странице, который берется из typography.css в папке assets. Стоит подробней остановится на первом элементе блока div, это и есть заголовок страницы, он включает в себя логотип Untappd:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;logo = html.Img(src=app.get_asset_url('logo.png'),
                        style={'width': &amp;quot;128px&amp;quot;, 'height': &amp;quot;128px&amp;quot;,
                        }, className='inline-image')&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;и главный заголовок:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;header = html.H3(&amp;quot;Статистика российских пивоварен в Untappd&amp;quot;, style={'text-transform': &amp;quot;uppercase&amp;quot;})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Чтобы расположить эти два элемента на одной строке мы воспользовались Bootstrap Формами:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;logo_and_header = dbc.FormGroup(
        [
            logo,
            html.Div(
                [
                    header
                ],
                className=&amp;quot;p-5&amp;quot;
            )
        ],
        className='form-row',
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;В блоке html.Div параметр ’p-5’ позволяет добавить отступы и вертикально выровнять заголовок,  а ’form-row’ поставить logo и header в один ряд. На данном этапе шапка дашборда вылгядит следующим образом:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/2-10.png" width="1347" height="128" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Нам осталось выровнять их по центру и добавить красок. Для этого создаем отдельный контейнер, который будет состоять из одного ряда. В параметре &lt;span class="inline-code"&gt;className&lt;/span&gt; указываем &lt;span class="inline-code"&gt;’d-flex justify-content-center’&lt;/span&gt;, чтобы выровнять элементы контейнера по центру.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;dbc.Container(
                    dbc.Row(
                        [
                            dbc.Col(
                                html.Div(
                                    logo_and_header,
                                ),
                            ),
                        ],
                        style={'max-height': '128px',
                               'color': 'white',
                       }

                    ),
                    className='d-flex justify-content-center',
                    style={'max-width': '100%',
                           'background-color': colors[0]},
                ),&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;На данном этапе шапка дашборда готова:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/3-9.png" width="1343" height="118" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Далее в следующий Bootstrap Контейнер добавим первый подзаголовок:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;dbc.Container(
                    html.Div(
                        [
                            html.Br(),
                            html.H5(&amp;quot;Пивоварни&amp;quot;, style={'text-align':'center', 'text-transform': 'uppercase'}),
                            html.Hr(), # разделительная линия&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Сам дашборд будет состоять из Bootstrap Карт, это обеспечит структурированное расположение всех элементов, придаст каждому элементу четкие границы и сохранит white space. Давайте создадим один из элементов дашборда, панель с настройками:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;slider_day_values = [1, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
slider_top_breweries_values = [5, 25, 50, 75, 100, 125, 150, 175, 200]

controls = dbc.Card(
    [
       dbc.CardBody(
           [
               dbc.FormGroup(
                    [
                        dbc.Label(&amp;quot;Временной период&amp;quot;, style={'text-align': 'center', 'font-size': '100%', 'text-transform': 'uppercase'}),
                        dcc.Slider(
                            id='slider-day',
                            min=1,
                            max=100,
                            step=10,
                            value=100,
                            marks={i: i for i in slider_day_values}
                        ),
                    ], style={'text-align': 'center'}
               ),
               dbc.FormGroup(
                    [
                        dbc.Label(&amp;quot;Количество пивоварен&amp;quot;, style={'text-align': 'center', 'font-size': '100%', 'text-transform': 'uppercase'}),
                        dcc.Slider(
                            id='slider-top-breweries',
                            min=5,
                            max=200,
                            step=5,
                            value=200,
                            marks={i: i for i in slider_top_breweries_values}
                        ),
                    ], style={'text-align': 'center'}
               ),
           ],
       )
    ],
    style={'height': '32.7rem', 'background-color': colors[3]}
)&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/4-5.png" width="357" height="179" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Панель включает два слайдера для управления scatter plot, они располагаются друг под другом в Bootstrap формах. Мы добавили слайдеры в один блок —  dbc.CardBody. остальные эдементы будут добавлены по такому же принципу, это позволяет поставить одинаковые  отступы со всех сторон . По умолчанию слайдеры оформлены в голубом цвете, для того чтобы изменить их стиль, воспользуйтесь файлом sliders.css, находящемся в папке assets.&lt;br /&gt;
Добавляем панель управления и scatter plot следующим блоком:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;dbc.Row(
                [
                    dbc.Col(controls, width={&amp;quot;size&amp;quot;: 4,
                                     &amp;quot;order&amp;quot;: 'first',
                                             &amp;quot;offset&amp;quot;: 0},
                     ),
                     dbc.Col(dbc.Card(
                                [
                                    dbc.CardBody(
                                        [
                                            html.H6(&amp;quot;Отношение количества отзывов к средней оценке пивоварни&amp;quot;,
                                                    className=&amp;quot;card-title&amp;quot;,
                                                    style={'text-transform': 'uppercase'}), 
                                            dcc.Graph(id='ratio-scatter-plot'),
                                        ],
                                    ),
                                ],
                                style={'background-color': colors[2], 'text-align':'center'}
                             ),
                     md=8),
                ],
                align=&amp;quot;start&amp;quot;,
                justify='center',
            ),
html.Br(),&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И в конце страницы расположим карту:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;html.H5(&amp;quot;Заведения и регионы&amp;quot;, style={'text-align':'center', 'text-transform': 'uppercase',}),
                            html.Hr(), #разделительная линия
                            dbc.Row(
                                [
                                    dbc.Col(
                                        dbc.Card(
                                            [
                                                dbc.CardBody(
                                                    [
                                                        html.H6(&amp;quot;Средний рейтинг пива по регионам&amp;quot;,
                                                                className=&amp;quot;card-title&amp;quot;,
                                                                style={'text-transform': 'uppercase'},
                                                        ),  
                                                        dcc.Graph(figure=get_map())
                                                    ],
                                                ),
                                            ],
                                        style={'background-color': colors[2], 'text-align': 'center'}
                                        ),
                                md=12),
                                ]
                            ),
                            html.Br(),&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Callback-функции в Dash&lt;/h2&gt;
&lt;p&gt;Callback-функции позволяют сделать элементы дашборда интерактивными, если меняется входной элемент (Input), то и выход (Output) тоже изменится.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;@app.callback(
    Output('ratio-scatter-plot', 'figure'),
    [Input('slider-day', 'value'),
     Input('slider-top-breweries', 'value'),
     ]
)
def get_scatter_plots(n_days=100, top_n=200):
    if n_days == 100 and top_n == 200:
        df = pd.read_csv('data/ratio_scatter_plot.csv')
        return get_plot(n_days, top_n, df)
    else:
        return get_plot(n_days, top_n)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Входные/ выходные  (Input/Output) значения это, проще говоря, параметр value элемента с определенным id. Например, входное значение верхнего слайдера с id=’slider-day’, показывающего временной период по умолчанию 100.  При изменении этого значения функция, обернутая в декоратор будет вызвана автоматически и output на графике обновится. Больше примеров представлено &lt;a href="https://dash.plotly.com/basic-callbacks/на"&gt;сайте plotly&lt;/a&gt;.&lt;br /&gt;
Важный момент, чтобы scatter plot при загрузке страницы отображал данные нам нужно сперва считать их из сохраненного датафрейма в папке data (указать начальное состояние), иначе scatter plot будет пустым. В дальнейшем при изменении параметров слайдера данные будут загружаться из ClickHouse таблиц.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="831" data-ratio="1.6011560693642"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/5-7.png" width="831" height="519" alt="" /&gt;
&lt;img src="http://test.leftjoin.ru/pictures/6-6.png" width="831" height="519" alt="" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Далее добавим пару строк отвечающих за поднятие локального сервера и приложение готово к запуску:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;application = app.server

if __name__ == '__main__':
    application.run(debug=True, port=8000)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь осталось только &lt;a href="http://test.leftjoin.ru/all/dashboard-python-2/"&gt;загрузить его на AWS с помощью BeansTalk&lt;/a&gt; и наш &lt;a href="http://unappd-part-1-ru.us-east-2.elasticbeanstalk.com/"&gt;дашборд на Bootstrap готов&lt;/a&gt;:&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-4by3" style="min-width:500"&gt;&lt;p&gt;&lt;iframe id="igraph" scrolling="yes" style="border:none;"seamless="seamless" src='http://unappd-part-1-ru.us-east-2.elasticbeanstalk.com/' height="1360px" width="1100px"&lt;/p&gt;
&lt;/iframe&gt;&lt;/div&gt;&lt;p&gt;Это была первая часть нашего выпуска, в следующей мы добавим больше новых bootstrap компонентов, callback-ов и расскажем про стилизацию таблиц.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/valiotti/leftjoin/tree/master/untappd_dashboard%20(part%201)/"&gt;Полный код проекта на GitHub&lt;/a&gt;&lt;/p&gt;
</description>
<pubDate>Thu, 13 Aug 2020 10:20:03 +0300</pubDate>
</item>


</channel>
</rss>