— Я зделяль. ©
Итак, прошу любить и жаловать — INat::Get — софтина для получения и обработки данных с iNaturalist. Основное изначальное предназначение — подбивать всякую статистику для проектов на том же iNaturalist’е, но варианты использования гораздо шире.
Первым делом хочу отметить, что текущее состояние — это ранняя альфа. Я не рекомендую никому этим пользоваться иначе как из любопытства и желания поучаствовать. Тем не менее делаю пост уже сейчас в надежде, что любопытные желающие найдутся. Со своей стороны готов подробно отвечать на вопросы и учитывать пожелания.
Зачем?
iNaturalist предоставляет открытый доступ к огромному массиву наблюдений, а также по сути к постоянно актуализируему таксономическому справочнику (тут можно обсуждать нюансы, но для любительских целей это очень хорошие данные). Интерфейс самого сайта не покрывает и, конечно, не может покрывать все возможные варианты запросов и выборок, но мы можем получить сами данные через механизм выгрузок или посредством открытого API, и второй вариант богаче, гибче и вообще интересней.
В качестве примера таких отчетов, которые нельзя получить просто из интерфейса, приведу свои посты в проекте «Биоразнообразие Артинского района». Не потому, что они представляют собой что-то особо ценное, а именно как демонстрацию:
-
«Неитоги 2023» (как бы итоги сезона, только подведенные в его середине). Здесь интересны «Новинки» и «Потеряшки».
-
«Сравнение с соседями». Не просто количественное сравнение, а сравнение видового состава.
Эти посты были сформированы по данным выгрузок, а не API, посредством мною же написанного inat-script, в процессе работы с которым (и над которым) я осознал все недостатки механизма выгрузок:
- ограниченность данных;
- необходимость ручного создания процесса выгрузки и, потом, загрузки файла;
- и главное — неизвестный заранее срок готовности — некоторые выгрузки делались вовсе несколько дней.
Кроме того, сам скрипт, как всякий первый блин, требовал существенной переработки для того, чтобы удобно внедрять в него новые варианты выборок и отчетов, и я решил написать с нуля новый инструмент, работающий непосредственно с API.
Принцип действия
На самом деле я сначала пытался сделать как-то так, чтобы отчеты формировались через конфигурационные файлы. Однако гибкости в таком подходе никакой (ну или потребуется senior-yaml-developer для использования, ЕВПОЧЯ). В итоге пришел к выводу, что проще предположить в продвинутом пользователе базовые знания Ruby…
В общем, программа запускает ruby-скрипты, называемые задачами, которые работают на уровне абстрактных выборок и списков,
оставляя все обращения к API и кэширование ответов под капотом. По большому счету пользователь имеет дело (помимо объектов,
представляющих собственно данные) с двумя классами: DataSet
, который представляет собой набор наблюдений, уже отфильтрованный
тем или иным образом; и List
— по сути датасет, сгруппированный по неким объектам, как правило — таксонам.
Для формирования вывода имеется специальный объект Table
, который сначала определяется, т.е. задаются колонки с заголовками,
шириной и выравниванием, а затем наполняется данными, которые должны представлять собой массив хэш-таблиц… Звучит страшно,
но в действительности скрипты могут быть совсем простые. Например, давайте получим список видов в Артинском районе, которых
я никогда не наблюдал.
Файл я поместил в каталог примеров под именем notmy.inat
, теперь мы можем его запустить командой:
И через некоторое время получим результат.
Результат можно увидеть в моем посте на iNaturalist. Да, на данный момент, все форматирование рассчитано именно и только на посты в iNat, активно используя тамошние стили. Есть планы расширить данный момент, но об этом позже.
Что важно, если мы тут же запустим ту же команду, то результат получим практически мгновенно, причем идентичный. А если выждем сутки, то некоторое дополнительное время понадобится, но существенно меньшее, чем при первом запуске. Это первый ключевой момент — данные кэшируются.
Кроме того, следует обратить внимание на то, что на самом-то деле API отдает не более 200 наблюдений за один запрос. Здесь же этого ограничения мы не видим и работаем с полными датасетами (объемом 3k+ и 4k+) — организация последовательной постраничной загрузки так же находится под капотом.
Некоторые детали
-
Метод
select
выполняет запрос и возвращает объект классаDataSet
. Именованные параметры данного метода примерно соответствуют параметрам API, правда, реализованы не все. -
Класс
DataSet
инкапсулирует набор наблюдений, плюс опционально ассоциирует его с некоторым объектом. В основном его поведение определяется включенным модулемEnumerable
, и бинарными операциями:-
|
— объединение; -
&
— пересечение; -
-
— разность.
Также у класса
DataSet
имеется важный методto_list
, который создает объект классаList
, группируя наблюдения по тому или иному параметру. Для группировки используетсяproc
-объект, который должен выдавать по наблюдению собственно ключ группировки. Наиболее полезные (на мой взгляд) группировки уже определены как константы модуляListers
:-
Listers::SPECIES
возвращает таксон, «приведенный к виду». В кавычках потому, что результатом может быть как вид, так и гибрид или комплекс. -
Listers::YEAR
возвращает год. - И так далее.
Если вызвать
to_list
без параметров, то по умолчанию будет использованListers::SPECIES
. -
-
Класс
List
представляет собой список датасетов с ассоциированными значениями. Также реализует модульEnumerable
, только итерируемыми элементами будут объекты классаDataSet
, а неObservation
. Для списков также определены некоторые бинарные операции:-
+
— объединение; -
*
— пересечение; -
-
— разность (что и использовано в примере).
Объединенный датасет из списка можно получить посредством метода
to_dataset
. -
-
Классы данных:
Observation
,Taxon
,Place
,User
и так далее предоставляют собственно данные. Свойств там много, их следует отдокументировать, но пока руки не дошли. Впрочем, это тот случай, когда код действительно является документацией, так что см. каталогentity
.Отмечу, что все они имеют метод класса
by_id
для получения соответствующего объекта, и в дополнение классыPlace
иProject
имеют методby_slug
, а классUser
— методby_login
.
Установка
Установка максимально проста:
Правда, на данный момент все это гарантированно работает только под Linux. Тестирование под Windows в планах есть, но скорее ближе к весне.
Планы
Вообще, на гитхабе есть такой замечательный раздел «Issues», где можно посмотреть процесс планирования в реальном времени. Здесь постараюсь дать сводную картину.
К версии 1.0
- Документация
-
На первом этапе — полноценное руководство пользователя на русском языке. Затем расширенное руководство как на русском, так и на английском.
- Данные
-
Сейчас многие поля, в том числе такие важные, как охранный статус, просто игнорируются. Это категорически неправильно и, естественно, будет исправлено уже в бета-версии.
Сюда же отнесу поддержку всех ключей запроса доступных в API. Тут есть некоторые нюансы, которые следует продумать, почему собственно на данном этапе их и нет, но все решаемо.
- Доделки и исправления
-
Уже замеченные баги есть, и их, само собой, исправлять нужно, причем в первую очередь. К счастью, критичный, т.е. приводящий к вылету, только один и возникает он редко.
Есть запланированная, но пока нереализованная базовая функциональность, такая как чистка устаревших данных.
- Оптимизация запросов
-
Есть мысли, как можно уменьшить количество запросов к API, что существенно ускорит работу в целом.
Дальнейшее развитие
- Разнообразные возможности вывода
-
Тут с одной стороны, явно нужно сделать поддержку не только упрощенной разметки, используемой в журналах iNaturalist, но и форматов, которые можно использовать в различных местах независимо.
С другой стороны, надо бы добавить что-то типа вывода иерархических списков, а возможно и каких-то еще вариантов оформления.
Все это пока на уровне исследования и формулирования задачи.
- Оптимизация кэширования
-
Подробно расписывать не буду, но там есть над чем работать. В первую очередь это касается разбора и трансляции условий проектов.
Обратная связь
Буду рад вопросам, замечаниям и предложениям, как в комментариях к этому посту, так и в соответствующем разделе на GitHub. Можно так же писать в личные сообщения на iNaturalist, хотя предпочтительно все же общаться в открытых комментариях, чтобы не возникало дублирования.