💶 Трекер карманных расходов на базе Grafana

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

Для начала, пожалуй, скажу, что трекать вообще все расходы — занятие абсолютно неблагодарное. Если вы хотите заработать себе стойкое ощущение потраченного впустую времени, вряд ли для этого найдётся деятельность лучше, чем записывание каждой транзакции в бухгалтерскую книжку. Да, у меня были и GNUCash, и Ledger, и прочие средства под рукой, даже бумажная книжка, но всё это надоедало так же быстро, как я за это хватался.

Позже встал вопрос: если мне не нравится такой трекинг, то что я вообще хочу от этой задачи? В уме всплыла картинка с автомобильным индикатором топлива. Если уж я поставил себе задачу держать расходы под контролем, то мне нужна шкала, которая обозначает бюджет на определённые уязвимые категории. Для меня это уличная еда, разнообразное железо, а также музыка, софт и игры, всего три больших раздела. Также мне нужен интерфейс, который позволяет только вносить записи, но не править их. Если ты потратил деньги на что-то, то ты их потратил, никакого читерства.

Что у нас умеет красиво рисовать графики и шкалы? Grafana. Какой поддерживаемый формат базы данных со встроенной арифметикой можно добавить дешевле всего? SQLite. Базовый набор есть, теперь можно строить интерфейс.

Data Source для базы SQLite

Поддержка SQLite в Grafana требует установки модуля, однако никаких проблем это не вызывает, вопрос решается одним кликом. Важнее чтобы директория с плагинами была примонтирована в persistent storage, если используется инстанс в контейнере. Также в таком случае важно примонтировать внешний файл с БД. Инициализировать таблицу для расходов можно вот так:

sqlite> .schema expenses
CREATE TABLE expenses (time timestamp default (strftime('%s', 'now')), category text, amount real);

Уже после установки и настройки датасорса можно собирать свой дашборд. На всякий случай положу здесь туториал. В SQLite поддерживается функция strftime(), с помощью которой можно использовать удобную арифметику вычисления даты. Создаём новую панель в дашборде, выбираем нашу базку как источник данных, выбираем "Gauge" в качестве типа показометра и пишем следующее:

select sum(amount) from expenses where time > strftime('%s', date('now', 'start of month'));

Этот нехитрый запрос покажет сколько денег суммарно ушло на все три категории с начала месяца. Можно также сделать показометр для каждой категории отдельно, тогда запрос будет включать group by:

select category, sum(amount) from expenses where time > strftime('%s', date('now', 'start of month')) group by category;

Для таких запросов удобна панель формата "Bar Gauge". В каждом представлении хорошо указать Thresholds и обозначить их другим цветом: это и будет ваш бюджет. Если я перескочу, допустим, 50 евро в месяц на уличный хавчик, циферка возле шкалы сразу станет красненькой и это крайне наглядно. Также очень удобно и наглядно то, что что формат представления данных вообще никто не ограничивает. Можно постараться также нарисовать график динамики расходов, никто не мешает.

Редактирование панели Bar Gauge


Теперь стоит поговорить об интерфейсе для внесения записей. Три вышеупомянутые категории расходов у меня в подавляющем большинстве проходят когда мой телефон находится у меня в руках, поэтому записывать буду там же. В моём случае клиент был сделан с помощью приложения HTTP Shortcuts; возможно для Apple iOS есть нечто похожее, но я не проверял. Суть в том, что HTTP Shortcuts позволяет вынести на домашний экран ярлычок на воркфлоу, с помощью которого можно сконструировать нужный нам payload и отправить его по протоколу http на сервер. В своих воркфлоу можно использовать разнообразные формочки, а уже эти формочки после заполнения подставляются в нужный нам запрос. Очень рекомендую сперва прочитать документацию. Приложение очень мощное, в нём есть даже базовая поддержка скриптинга на подмножестве JavaScript.

Единственное, что я вряд ли здесь подскажу, — это какой middleware использовать для преобразования http-запросов в SQLite. Я написал свой мини-сервер на Go, но там уже и так всякой ad-hoc фигни понамешано. В конце концов, можно использовать что угодно, хоть inetd, главное лишь аутентификацию не забыть. Также могу порекомендовать Webdis (HTTP-фронтенд для Redis), который можно поместить за реверс-прокси и защитить какими угодно доступными методами аутентификации. Главное в этом случае — забирать данные из очереди и помещать их в базу, делается легко с помощью скрипта(пример) в CRON.


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

Mon, 1 May 2023 11:57:03 +0200


RSS // Telegram // Статистика