Считалка статистики для Табуна. На вход (который нужно написать самостоятельно) принимает пользователей, блоги, посты и комменты, а на выходе даёт пачку файлов (в основном csv) с разными интересными числами и иногда словами.
Посты на Табуне:
- 2014: https://tabun.everypony.ru/blog/uniblog/104628.html
- 2016: https://tabun.everypony.ru/blog/uniblog/159457.html
- 2018: https://tabun.everypony.ru/blog/uniblog/182474.html
- 2021: https://tabun.everypony.ru/blog/uniblog/202274.html
- 2023: https://tabun.everypony.ru/blog/uniblog/213822.html
Стандартно, скачав и запустив в каталоге tabun_stat (рекомендуется предварительно создать и активировать виртуальное окружение):
pip install -e .
После этого появятся команды tabun_stat и tabun_stat_graph.
Для работы требуется конфиг; по умолчанию есть config.toml. Укажите конфиг
при запуске tabun_stat:
tabun_stat -vv -c config.toml
Опция -vv включает подробный вывод с прогресс-барами.
Для работы tabun_stat требуется какой-то источник данных. Подразумевается, что он у вас есть и вы его можете подключить самостоятельно. В репозитории лежит демонстрационный пример данных для sqlite3 базы данных; чтобы воспользоваться им, создайте базу из прилагаемого sql-дампа:
$ sqlite3 demo.sqlite3 < demo.sql
Создайте конфиг для этого источника в файле datasource.toml:
name = ":sqlite3.Sqlite3DataSource"
path = "demo.sqlite3"
(двоеточие в начале name — сокращение для поиска модуля внутри пакета
tabun_stat.datasource)
и подключите этот файл в основном конфиге (в стандартном файле config.toml
уже прописано по умолчанию):
datasource_file = "datasource.toml"
Источник данных — это класс-потомок tabun_stat.datasource.BaseDataSource.
Чтобы создать свой источник, унаследуйтесь от него, реализуйте все абстрактные
методы:
from tabun_stat.datasource import BaseDataSource
from tabun_stat.stat import TabunStat
class CustomDataSource(BaseDataSource):
def __init__(self, *, option1: str, option2: int):
self.option1 = option1
self.option2 = option2
# и так далее по вкусу
def start(self, stat: TabunStat) -> None:
pass # по вкусу
def destroy(self) -> None:
pass # по вкусу
# и так далее
и включите его использование в конфиге (datasource.toml):
name = "ваш_модуль.CustomDataSource"
option1 = "значение опции 1"
option2 = 777
Все ключи из конфига, кроме name, передаются в __init__ как аргументы.
Кратко о том, какие абстрактные методы нужно переопределить:
-
get_user_by_id— получение одного пользователя; -
get_users_limits— получение статистики по пользователям; -
get_blog_by_id— получение одного блога; -
get_blogs_limits— получение базовой статистики по блогам; -
get_post_by_id— получение одного поста; -
get_posts_limits— получение базовой статистики по постам; -
get_comment_by_id— получение одного коммента; -
get_comments_limits— получение базовой статистики по комментам.
Для источников на базе популярных СУБД почти всё это сводится к несложным SQL-запросам.
Также есть ещё методы, имеющие реализацию по умолчанию, но крайне неэффективную (в сотни раз медленнее эффективных реализаций на SQL), поэтому их тоже крайне желательно переопределить:
-
get_blog_statuses_by_idsиget_blog_ids_of_posts— для этих методов можно применить какое-нибудь кэширование; -
iter_users— получение пользователей пачками; -
iter_blogs— получение блогов пачками; -
iter_posts— получение постов пачками; -
iter_comments— получение комментов пачками.
iter-методы — это генераторы, которые выдают запрашиваемые объекты через yield. Если указаны фильтры, то должны применяться прописанные в них ограничения. Сортировка возвращаемых значений не определена. В целях оптимизации yield'ятся не отдельно объекты по одному, а списки объектов. (В стандартной неэффективной реализации этот список состоит из одного элемента, а лучше бы сотни.)
Есть ещё несколько методов, стандартная реализация которых достаточно эффективна, но иногда может быть полезно переопределить и их тоже (например, чтобы прикрутить кэширование):
-
get_username_by_user_id— получить имя пользователя по его id; -
get_blog_status_by_id— статус блога по его id; -
get_blog_id_of_post— получение блога поста.
Подробнее о том, как это всё реализовывать, читайте в docstring'ах в файле
tabun_stat/processors/base.py. И вообще, код — лучшая документация,
читайте пример для sqlite3 ;)
Обработчик — это класс-потомок tabun_stat.processors.BaseProcessor.
Чтобы создать свой источник, унаследуйтесь от него и реализуйте нужные вам
методы на свой вкус.
Каждый обработчик считает полную статистику ровно один раз, после чего уничтожается. Использование одного и того же обработчика для подсчёта полной статистики несколько раз не предусмотрено.
Жизненный цикл обработчика (какие методы вызываются):
-
сперва все обработчики создаются;
-
start; -
begin_users; -
process_userвызывается столько раз, сколько есть пользователей (сортировка пользователей не определена); -
end_users; -
begin_blogs; -
process_blogвызывается столько раз, сколько есть блогов (сортировка блогов не определена); -
end_blogs; -
begin_messages; -
process_postиprocess_comment— вызываются гарантированно с сортировкой постов и комментов по времени, что позволяет сделать некоторые оптимизации; -
end_messages; -
stop; -
после всего этого обработчики уничтожаются.
Во все методы передаётся stat — объект TabunStat, через который вы можете
получить доступ к источнику данных (stat.source), каталогу для вывода
(self.destination) и функции для записи лога (stat.log).
Не забывайте про super:
from datetime import datetime
from tabun_stat import types
from tabun_stat.processors import BaseProcessor
from tabun_stat.stat import TabunStat
class CustomProcessor(BaseProcessor):
def __init__(self, *, option1: str, option2: int):
super().__init__()
self.option1 = option1
self.option2 = option2
# и так далее по вкусу
def start(self, stat: TabunStat) -> None:
super().start(stat)
# что-то там
def process_user(self, stat: TabunStat, user: types.User) -> None:
... # что-то там
def stop(self, stat: TabunStat) -> None:
# что-то там
with (stat.destination / "custom.csv").open("w", encoding="utf-8") as fp:
fp.write("Я сделяль статистику\n")
super().stop(stat)
# и так далее
Включается в конфиге (config.toml) аналогично источнику. Обратите внимание,
что processors это массив, и поэтому в синтаксисе TOML используются две пары
квадратных скобок:
[[processors]]
name = "custom_module.CustomProcessor"
option1 = "hello"
option2 = 4
[[processors]]
name = ":registrations.RegistrationsProcessor"
# ...и другие стандартные обработчики по желанию
Все ключи из конфига, кроме name, передаются в __init__ как аргументы.
В комплекте также присутствует рисовалка графиков из csv-файлов, сделанная на основе библиотеки svg.charts. По умолчанию она не установлена, доустановите зависимости перед запуском:
pip install -e ".[graph]"
Рисовалке нужен конфиг, содержащий информацию об источнике данных, каталоге
для сохранения готовых графиков и параметров рисования. Есть стандартный
конфиг graph_config.toml, читающий csv-файлы из каталога stat и сохраняющий
графики в папку graphs. Запускается вот так:
tabun_stat_graph -c graph_config.toml
Подробнее о параметрах рисования можно почитать в документации к функции
render_plot в файле tabun_stat/graph/renderers.py.