Кроме текста и картинок страница портала умеет показывать ваши данные: остатки на складе, выручку по дням, статусы сервисов, загрузку склада. Данные приходят напрямую из ваших систем — базы данных или небольшого расчётного скрипта, — а не набираются руками. Так на портале появляются живые сводки: цифра на странице всегда та же, что в источнике.
Зачем это нужно
Обычная страница со сводкой быстро устаревает: цифры в ней кто-то должен обновлять. Здесь иначе. Вы один раз описываете, где взять данные, а портал сам обращается к источнику при каждом просмотре. Руководитель открывает страницу и видит текущее положение дел, а не вчерашнюю выгрузку.
Есть и второй способ взять данные — расчётный скрипт на Python (см. Динамические блоки на Python). Он хорош, когда данные нужно посчитать или собрать по кусочкам. Источники данных проще: если у вас есть база, из которой можно взять готовые строки, обходиться можно одним описанием запроса — без программы.
Где лежат источники
Источник — это обычный файл с описанием подключения. Все они лежат в папке sources/ рядом с контентом портала, по файлу на источник:
portal/
├── sources/
│ ├── warehouse.json склад
│ ├── billing.json выставленные счета
│ └── uptime.json доступность сервисов
├── scripts/ расчётные скрипты
└── news/ страницы портала
Файлы источников — часть контента: их правит управляющий агент, они попадают в историю версий и переносятся вместе с папкой портала на другой сервер. Отдельного экрана для источников в интерфейсе нет — как и для страниц, их описание ведёт агент.
Данные подключения (адрес базы, логин, пароль) записаны прямо в файле источника. Поэтому папка контента — это место для секретов: доступ к ней должен быть только у вас и у агента, а файл с паролем не стоит выкладывать в открытый репозиторий.
Четыре вида источников
- SQLite — база данных одним файлом. Подходит, когда данные уже лежат в файле на том же сервере; ничего дополнительно поднимать не нужно.
- PostgreSQL — распространённая база данных для приложений. Порталу нужны адрес сервера, имя базы, логин и пароль; защищённое соединение включено по умолчанию.
- ClickHouse — база для больших объёмов и быстрых сводок. Настройка такая же, как у PostgreSQL, отличается порт.
- Python-источник — когда готовой базы нет: данные собирает небольшой скрипт из папки
scripts/— например, обращается к внешнему сервису. Запрос к такому источнику становится входными данными скрипта, а не текстом запроса к базе.
У каждого источника можно задать предельное время ожидания ответа (по умолчанию 30 секунд). Если внешний сервис задумается, страница не будет ждать его вечно.
Что можно спросить у источника
Вопрос к источнику — это запрос: какие столбцы и строки взять. Запрос живёт в описании страницы рядом с визуализацией, читатель его не пишет и не видит. Отсюда и безопасность: посетитель публичного сайта не может попросить у базы чего-то лишнего — он видит только то, что заранее описал агент.
- Только чтение. Источник отвечает на выборку данных, а не на изменения: изменить или удалить что-то из портала нельзя. Запрос, который пытается что-то поменять, даже не доходит до базы.
- Не больше 30 секунд. Если данные не пришли вовремя, запрос останавливается, а читатель видит понятное сообщение вместо вечной загрузки.
- Не больше 10 000 строк. Слишком большой результат обрезается, и об этом честно сказано в ответе — портал не прячет, что показал не всё. Пределы можно изменить для отдельного запроса.
- Одна команда за раз. Запрос не может состоять из нескольких команд подряд — так случайная опечатка не превратится в опасное действие.
Если данных нужно несколько — например, сводка по дням и разбивка по регионам, — на странице ставятся несколько визуализаций, каждая со своим запросом. Все они смотрят в один и тот же источник.
Какие бывают визуализации
Результат запроса можно показать по-разному — всего доступно 33 вида. Вид выбирается при сборке страницы, и агент вставляет визуализацию как есть, а портал сам её рисует.
- Цифры и шкалы — плитка с показателем; датчик со стрелкой; полосовый датчик; шкала «план и факт» с отметкой цели.
- Таблицы и карточки — таблица данных; карточка с подробностями по одному объекту.
- Графики — линии, столбцы, точки, комбинированный график, кольцевая диаграмма, гистограмма, «ящик с усами», «скрипка», свечи, радар, параллельные координаты, наклон.
- Сравнение — «гантель» (два значения рядом), водопад (как менялась величина по шагам), Парето (что даёт основную часть), торнадо (что влияет сильнее), воронка (где теряются этапы).
- Время и порядок — лента состояний, история статусов, диаграмма Гантта (сроки этапов), календарь с тепловой раскраской дней.
- Связи, доли и карты — потоки Санкей, древовидная карта, граф связей, круги Венна, тепловая карта, географическая карта регионов.
Как и у любого блока, у визуализации есть заголовок, ширина и кнопки действий. Если порталу встретится вид, которого он не знает, читатель не увидит ошибку: на месте блока появится спокойная пометка с названием вида.
Переход по значению
Сводку можно сделать «живой» не только на обновление, но и на выбор. Если включён переход по значению, щелчок по столбцу, отметке или строке таблицы подставляет выбранное значение, и все блоки страницы, которые его используют, сразу пересчитываются. Так из общей картины получается разбор: сначала «продажи по регионам», щелчок по региону — и рядом появились цифры только по нему.
Выбранное значение видно сверху страницы отдельной пометкой, рядом есть кнопка Сбросить фильтр — она возвращает общую картину. Переход настраивает агент: он указывает, какую переменную подставлять и как её показывать читателю.
Свежесть и временное хранение
Под каждой визуализацией видно, когда данные обновились — например, Обновлено в 14:02, — и есть кнопка обновления. Данные обновляются сами, когда агент поправил страницу, когда сменилось выбранное значение или когда сработало действие с ответом «обновить».
Не всегда разумно тревожить базу при каждом просмотре: тяжёлый запрос может занимать ресурсы. Для этого у визуализации задаётся срок хранения результата — от нуля до суток. При сроке 300 секунд данные пересчитываются не чаще, чем раз в пять минут, а между этим показывается уже готовый результат. По умолчанию хранения нет: запрос выполняется при каждом просмотре. Ошибку портал не запоминает — неудачный запрос повторится при следующем обновлении, а правка источника или самого запроса сбрасывает сохранённый результат сразу.
Публичный сайт
Страница со сводкой может быть и публичной: посетители увидят те же цифры на сайте /p/, без входа. Это удобно для открытой статистики, каталога с остатками или страницы «как мы работаем». Каждый посетитель получает данные по своему обращению к порталу, а обратиться за чем-то другим не может: какие именно данные показывать, решает описание страницы, а не посетитель.
Черновик при этом остаётся закрытым: запрос по закрытой странице снаружи не выполняется вовсе, а посетитель видит ту же страницу «Страница не найдена», что и при опечатке в адресе. Публикуя страницу, вы публикуете и результаты её запросов.
Если что-то не получилось
Одна неудачная визуализация не ломает страницу — остальные блоки рисуются как обычно. Читатель видит понятную надпись: Не удалось выполнить запрос, Запрос не настроен или Нет данных. Подробности и подсказка, что поправить, показываются только агенту. Сломанный файл источника тоже не мешает остальным: портал назовёт именно этот файл и продолжит работать с теми источниками, которые читаются.
Что здесь делает агент
Всю работу с источниками ведёт управляющий агент — обычными файлами и обращениями к порталу:
- заводит источник — описывает подключение в новом файле и проверяет, что он читается;
- смотрит список источников — что подключено, что читается, а где ошибка в описании или в файле;
- проверяет запрос до записи в страницу — так агент убеждается, что запрос работает и колонки называются так, как нужно, ещё до того, как сводку увидят люди;
- собирает визуализацию — ставит блок на страницу, задаёт вид, запрос, срок хранения и переход по значению.
Дальше: как проверяются страницы и запросы — в разделе Проверки страниц; что ещё умеет агент — в разделе Управление агентом; как посчитать данные скриптом — в разделе Динамические блоки на Python.