Bonsai: фамильный вики-движок +64



Лирическое вступление


Как-то вечером, наводя порядок в стенном шкафу, я наткнулся на большую картонную коробку. Она пережила два переезда и не открывалась уже столько лет, что я напрочь забыл, что именно в ней хранилось. Оказалось, там лежали фотографии — в альбомах, в конвертах из фотоателье, а часть просто так.

Многие фотографии были сделаны более семидесяти лет назад. На одной был дедушка — в студенческие годы, еще молодой и статный, в абсолютно ломовейших очках. «Надо же, мой дед носил хипстерские шмотки еще до того, как это стало мейнстримом», подумал я, и невольно улыбнулся. Его я узнал сразу, но дальше пошли фотографии людей, о которых я ничего не помню. В чертах лица можно смутно угадать родство — и все.



Когда мне было пятнадцать, бабушка неоднократно показывала эти карточки и рассказывала о тех, кто на них изображен. К сожалению, ценность подобных историй понимаешь только тогда, когда рассказывать их становится некому. На тот момент мне было абсолютно неинтересно по десятому разу слушать какие-то замшелые байки про довоенные годы, я отмахивался от них и пропускал мимо ушей. Теперь же, внезапно в полной мере осознав, что часть семейной истории безвозвратно утеряна, я загорелся идеей систематизировать и сохранить то, что осталось.

Идеальным решением для хранения семейных данных мне представлялся гибрид вики-движка и фотоальбома. Готовых подходящих решений не оказалось, поэтому пришлось написать собственный. Он называется Bonsai и доступен с открытым кодом по лицензии MIT. Дальше будет история о том, как он устроен и как им пользоваться, а также история его разработки и немного ДРАМЫ.



Очередной велосипед?


На сегодняшний день существует масса средств, позволяющих составлять фамильные деревья и каталогизировать информацию о родственниках. Они условно делятся на две большие категории — онлайн-сервисы и десктопные приложения.

В случае с десктопным приложением база обычно хранится в виде файла на диске. Вы открываете приложение и пополняете ее в однопользовательском режиме. При необходимости данные можно экспортировать для бэкапа или переноса в другую систему (например, в формате GEDCOM). Из тех, что я смотрел, самыми приятными в использовании показались Gramps (бесплатный) и отечественное Древо жизни (требует одноразовой покупки).

Противоположная сторона спектра — это веб-сервисы. Они хранят ваши данные на удаленных серверах и берут периодическую плату за использование. Поскольку это коммерческий продукт с централизованной базой и неплохой монетизацией, сервисы такого плана дают вам возможность, например, искать потерянных родственников по ДНК-тесту или архивным записям.

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

Если учесть недостатки обоих этих подходов, можно сформулировать список требований к «идеальному» движку:

  1. Веб-приложение, размещаемое на собственном сервере
  2. Создание статей о людях, питомцах, местах, событиях и т.д. наподобие вики
  3. Загрузка медиафайлов
  4. Отметки людей на фото и видео
  5. Автоматическое построение генеалогического дерева
  6. Календарь со всеми важными датами
  7. Инструменты для совместного редактирования и заливки

Справедливости ради, мне удалось найти несколько проектов с self-hosted-реализацией, но они были в плачевном состоянии: внешний вид застыл на уровне середины двухтысячных, полного набора нужного функционала нет, а копаться в легаси-скриптах на PHP не хотелось. Кроме того, предыдущий pet project закончился и было желание взяться за что-то новое.

Золотое правило гласит: хочешь сделать хорошо — сделай это сам!

Используемые технологии выбирались по трем критериям: мой опыт работы с ними, популярность и открытость-бесплатность. Вот что получилось в результате:

  • Рантайм: .NET Core 2.1
  • Бэкенд: ASP.NET Core MVC
  • База данных: PostgreSQL
  • Логика фронтенда: частично Vue, частично jQuery.
  • Стили фронтенда: Bootstrap + Sass

Во второстепенных ролях — Elasticsearch для полнотекстового поиска и ffmpeg для получения скриншотов из видео.

Схема данных


Основными объектами в базе Bonsai являются страница и медиа-файл. Они связаны соотношением «много ко многим» через отметки. Отметка может иметь заголовок без ссылки — например, если нужно отметить кого-то на фотографии, но информации на полноценную страницу о нем нет.



Помимо текста в свободной форме на странице могут быть указаны факты, которые вводятся в специальные поля в админке. По фактам вычисляются дополнительные данные: например, если указать дату рождения человека, то она отметится в календаре, а на его странице будет указан текущий возраст (или продолжительность жизни, если дата смерти тоже указана), по полу можно определить корректное название отношений («отец» или «мать» вместо общего «родители»), и так далее. В базе факты хранятся как JSON-документ.

На выбор доступно пять типов страниц: человек, питомец, событие, место и «прочее». От типа страницы зависит список доступных фактов: например, «образование» актуально только для человека, «дата рождения» — для человека и животного, а «адрес» — только для места.

Страницы связаны между собой отношениями: «родитель», «супруг», «друг», «владелец», «житель» и многими другими. Некоторые отношения могут быть ограничены по времени (супруг, владелец, житель), другие считаются постоянными.

При сохранении любой страницы или отношения получаемая модель проверяется на непротиворечивость. Например, года жизни супругов должны пересекаться, у человека не может быть больше одного биологического родителя каждого пола, и стать собственным папой тоже нельзя. Однополые браки, впрочем, допустимы.

Редактирование страницы, медиа-файла или отношения сохраняет в базу изменение. Это позволяет хранить историю правок и при необходимости их откатывать.

Отношения


Родственные отношения — одна из самых древних концепций в обществе. Уже в праиндоевропейском языке существовало множество названий для них, которые в несколько измененном виде перекочевали в современные языки самых разных групп: слово «мама» поймет и русский, и англичанин, и китаец.

Вариантов родственных отношений очень много, однако базовыми из них являются три: родитель, ребенок и супруг. Они позволяют построить из семьи направленный граф, где эти отношения являются ребрами, а люди — узлами. На таком графе можно выразить любое другое отношение, зная путь между участникам и их пол: например, чтобы определить чьего-то дедушку, нужно найти сначала его родителя (любого пола), а потом родителя этого родителя (мужского пола), и так далее.

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



Для вычисления отношений используется элементарный обход графа, а названия родства задаются в виде специального DSL:

public static RelationDefinition[] ParentRelations =
{
	new RelationDefinition("Parent:m", "Отец"),
	new RelationDefinition("Parent:f", "Мать"),
	new RelationDefinition("Parent Child:m", "Брат", "Братья"),
	new RelationDefinition("Parent Child:f", "Сестра", "Сестры"),
	new RelationDefinition("Parent Parent:m", "Дедушка", "Дедушки"),
	new RelationDefinition("Parent Parent:f", "Бабушка", "Бабушки")
};

Даже прямых родственников у человека может быть много. Bonsai делит связи на следующие группы:

  1. Ближайшее кровное родство — семья, в которой человек вырос: мать и отец, бабушки и дедушки, братья и сестры. Если смотреть на граф, то это путь на 1-2 шага вверх и на 1 в стороны.
  2. Собственная семья: по одной группе на каждого супруга и детей от него. Сюда также попадают родственники супруга — тещи, шурины и тому подобное.
  3. Прочее: более дальние родственники (внуки, дяди-тёти) и не-родственные связи (друзья, коллеги).

Иногда одного пути для определения принадлежности к группе недостаточно. Данные могут быть неполными, но их все равно нужно показать максимально адекватно. Рассмотрим следующий родственный граф:



Как мы видим, для Александра указаны две жены (Вера и Галина) и сын (Борис), но мы не знаем, кто из жен является матерью ребенка — может быть, это вообще какая-то третья женщина, но она пока не добавлена. Для таких случаев может быть указано несколько путей, которые должны существовать или не существовать, и они помечаются знаками + и - соответственно:

new RelationDefinition("Spouse Child+Child", "Сын|Дочь|Ребенок", "Дети")
new RelationDefinition("Spouse Child-Child:m", "Пасынок")
new RelationDefinition("Spouse Child-Child:f", "Падчерица")

Генеалогическое древо


Любой приличный генеалогический движок должен уметь строить фамильное древо. Это самый наглядный способ показать общую информацию о людях и их родственных связях. Данные хранятся в базе в виде направленного графа и, в теории, его должно быть несложно визуализировать. На практике же именно с отображением дерева возникло больше всего сложностей.

Вот несколько примеров того, как могут выглядеть генеалогические деревья:



Родословная Таргариенов. Очень компактно, потому что сделано вручную. Сгенерировать такое дерево из произвольных данных автоматически будет крайне сложно.



Греческие боги. Графическое представление генерируется из специального markdown-синтаксиса, в котором все равно нужно вручную расставить все блоки и прочертить связи между ними. Немного похоже на ASCII-art.



Представление дерева в виде полукруговой диаграммы. Легко генерируется автоматически, но учитывает только прямых предков.

Я просмотрел множество вариантов. Самый эстетически приятный был на сайте MyHeritage:



Отрисовку такого дерева можно разбить на три условных шага: получение данных из базы, расстановка блоков/соединительных линий, и непосредственно отображение их на странице. Если с первым и третьим шагами все было тривиально, то на втором я споткнулся.

Попытки набросать самопальное решение на скорую руку закончились полным фиаско. Грамотная расстановка элементов графа — настолько сложная область, что по ней пишут диссертации, а готовые компоненты стоят как квартира в Москве. Ладно, самому написать не получится, но ведь наверняка есть приличные бесплатные решения?

Больше всего надежд я возлагал на библиотеку D3.js. Пожалуй, это первое, что приходит в голову при необходимости нарисовать график или диаграмму на веб-странице. Увы, среди более трех сотен (!) примеров на wiki не оказалось ни одного более-менее похожего на дерево с MyHeritage.

Следующим шагом было погружение в библиотеки, занимающиеся не отрисовкой, а именно вычислением оптимального расположения элементов в графе. Большинство из них предлагают так называемый Force layout. Это очень простой подход, который основан на физических формулах: узлы графа представляются упругими телами, а соединительные линии — пружинами. Его можно легко узнать по характерной анимации — граф как бы «расправляется» на ходу, и это не дополнительная фича, а неизбежное следствие симуляционной природы алгоритма. Подход force-layout хорош для визуализации данных без четкой иерархи (например, связей в соцсетях), но фамильное древо в таком виде выглядит ущербно.

Еще один рассмотренный вариант — библиотека Graphviz. Результат ее работы можно легко узнать по характерным стрелкам. Для описания графа используется специальный язык DOT. Тестовые примеры выглядят еще более-менее, а вот с реальными данными возникают проблемы: стрелки «заламываются» и соединяются под странными углами, граф расползается, и это уже никак не настроишь и не обойдешь.

Не найдя подходящего решения самостоятельно, я решил заказать его на фрилансе, и тут началась та самая ДРАМА.

Заказ был размещен утром 22 октября и уже за час на него поступило несколько откликов. Одного из откликнувшихся звали Владислав; он прислал пример аналогичного решения и пообещал сделать задачу за один день. Такая скорость показалась мне подозрительной, но я понадеялся на его опыт и про себя дал парню погрешность в неделю. Первые пару дней Владислав задавал дополнительные вопросы, не переставая приятно удивлять глубокой погруженностью в проект и внимательным отношением к деталям, а потом пропал. Очнулся он 1 ноября, извинился за вынужденное исчезновение по семейным обстоятельствам и прислал ссылку с бета-версией, которая выглядела довольно похоже на желаемое, если бы не узел в соединительных линиях в центре:



Исчезновение исполнителя — всегда тревожный звонок, но мало ли, всякое бывает, ведь сделал же что-то. Пусть продолжает! Я отправил предоплату и стал ждать доработок. Через пару дней Владислав написал, что исправить проблему он не может, а потом пропал снова — на этот раз на три недели. За это время он ничего не сделал и предоплату возвращать отказался, потому что «задачу на самом деле делал глупый бывший друг, который подвел его и деньги не отдает». После пары уточняющих вопросов горе-делегатор перестал пытаться отмазываться и просто замолчал. Так теперь и живем — периодически я напоминаю ему про долг, а он в ответ присылает скриншот из банковского приложения — дескать, «денег нет, но как только — так сразу». Владиславу я желаю успехов в бизнесе и побыстрее разбогатеть!

Кинул пацана — минус в карму на!

Потерять деньги было не так досадно, но прошел целый месяц, а задача не сдвинулась с мертвой точки, и ждать помощи теперь было неоткуда. В первую очередь я был зол на самого себя: пошел по пути наименьшего сопротивления, нарушил золотое правило — и вот результат. Исполнившись праведным гневом, я повторно засел за изучение библиотек для отрисовки графов и — о чудо! — внезапно нашел именно то, что нужно.

Библиотека называлась Eclipse Layout Kernel, сокращенно ELK. Как можно догадаться, она применяется для отображения диаграмм в IDE Eclipse, но может быть использована и автономно. Вообще она написана на Java, но существует версия, транслированная в JS. Да, ее код кошмарен и весит полтора мегабайта, но эти недостатки можно простить за то, что она просто работает и делает именно то и именно так, как нужно. Интерфейс элементарный: на вход передаются узлы, ребра и настройки, а на выходе получаем координаты. Отрисовать дерево по ним можно любым удобным способом: я выбрал SVG для соединительных линий и div'ы с абсолютным позиционированием для блоков.

Интеграция библиотеки и подбор оптимальных настроек занял от силы два вечера. Это, конечно, не «одни сутки», как обещал мой незадачливый и самонадеянный фрилансер, но довольно близко. В результате Bonsai смог отображать дерево примерно в таком виде:



Теперь осталась единственная проблема — время обработки. ELK использует итеративный алгоритм: можно получить более близкое к оптимальному размещение, потратив дополнительное время. На дереве в 20-30 элементов хороший результат требует порядка 5 секунд. Из-за этого страница с деревом каждый раз открывается долго, и это быстро начинает раздражать. К следующей версии вычисление будет перенесено на бэкенд, чтобы его можно было сделать один раз при изменении страницы и закешировать.

Полнотекстовый поиск


Система для хранения текстовой информации была бы бесполезной без удобного полнотекстового поиска. В Bonsai используется база данных PostgreSQL, поэтому первым делом я решил проверить, что она может предложить из коробки. Очередное разочарование: tsvector справляется с обычными словами, но искать самое главное — имена и фамилии — напрочь отказывается:

SELECT
   to_tsvector('Проверки') @@ to_tsquery('Проверка'),            -- true
   to_tsvector('Иванов') @@ to_tsquery('Иван'),                  -- false
   to_tsvector('Иванова') @@ to_tsquery('Иван'),                 -- false
   to_tsvector('Иванова') @@ to_tsquery('Иванов'),               -- false
   to_tsvector('Иванов Иван Иванович') @@ to_tsquery('Иванова')  -- false

Триграммы тоже не дали ничего хорошего. В итоге я остановился на довольно ожидаемом варианте: ElasticSearch + Russian Morphology. Работать с ним из .NET оказалось очень неудобно, однако с поиском по ФИО он справляется на твердую пятерку.

Сознательное несовершенство


При работе над проектом регулярно возникали ситуации, когда от выбранного решения внутренний перфекционист приходил в бешенство. Предметная область довольно нестандартная и общепринятые «правила хорошего тона» работают не всегда.

Вот например, что происходит, когда мы открываем любую страницу?

  1. Текст страницы компилируется из Markdown в HTML. Если в тексте есть ссылки на другие страницы и медиафайлы, придется сходить за дополнительной информацией в базу.
  2. Факты десериализуются из JSON, в котором они хранятся в базе, во вьюмодели.
  3. Определяются родственные связи. Для этого из многострадальной базы необходимо достать весь граф связей и найти в нем узлы по заранее известному списку путей.

На первый взгляд кажется, что это ужасно тяжелая операция, но на самом деле это не так из-за относительно малого объема данных. Сколько родственников вы можете вспомнить и захотите записать? Попробуйте ради интереса их пересчитать и обнаружите, что набрать хотя бы сотню будет очень сложно. А скольким людям захотите дать доступ? Даже астрономически большое для семьи число — тысяча человек! — по меркам современных баз данных остается смехотворным.

Конечно, скомпилированная вьюмодель страницы все равно кешируется при первом открытии и переиспользуется при последующих — в первую очередь потому, что это было очень просто реализовать. Правило инвалидации кеша при изменениях в админке тоже взято максимально простое: если мы меняем только текст и некоторые локальные факты (список языков, группа крови, цвет волос и т.д.), то достаточно сбросить эту конкретную страницу. При любом другом изменении — название страницы, дата рождения или пол, добавление или изменение любой связи — кеш сбрасывается полностью. Да, это не самый умный способ очистки. Да, наверняка можно было бы написать сложный алгоритм, который сбрасывал бы только то, что нужно — но для данного проекта он не оправдал бы затрат.

Проект не поддерживает локализацию и смену внешнего вида, авторизация работает по OAuth на Facebook\Google, а админка сделана на обычных формах, а не каком-нибудь SPA-фреймворке по последнему писку моды. Все это можно было бы реализовать или улучшить, но оно не решило бы никакой проблемы, а следовательно время было бы потрачено зря.

С надеждой в будущее


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

Движок придется регулярно переписывать с нуля — точно так же, как на протяжении тысяч лет монахи усердно переписывали тексты из обветшалых книг в новые. Разница только в том, что книга может и сто лет пролежать при должном обращении, а приложение — от силы 15-20 лет. Надеюсь, что через двадцать лет я еще буду в состоянии сделать это самостоятельно, а вот еще через двадцать это придется делать моим детям или внукам. Хочется оставить им простой, понятный и документированный исходник.

На самых первых этапах проектирования мне хотелось встроить в движок некий SQL-подобный язык, с помощью которого можно было бы получать ответы на конкретные вопросы: «какой процент моих предков с голубыми глазами», «когда Иван купил первый автомобиль» и так далее. От данной идеи пришлось отказаться, потому что она потребовала бы вместо обычного текста заносить всю информацию в неком формализованном виде, и только описание этого вида заняло бы годы. С другой стороны, Natural Language Understanding сейчас набирает обороты. Не удивлюсь, если через десяток-другой лет можно будет попросить Siri прочитать текст за тебя, походить по ссылкам и в итоге представить выжимку из фактов. Ребята, поднажмите!

Как попробовать?


К сожалению, я не могу предоставить ссылку на готовую демку: нет сервера, который бы выдержал хабраэффект. Зато есть несколько наглядных скриншотов (картинки кликабельны).



Если Bonsai показался вам полезным и вы захотите запустить его самостоятельно, исходный код можно скачать с Гитхаба:

https://github.com/impworks/bonsai

Подробная инструкция по установке указана в Readme. Вам понадобится вот что:

  1. .NET Core 2.1+
  2. PostgreSQL 10+
  3. ElasticSearch 5.x и плагин Russian Morphology
  4. Приложение в Facebook или Google для авторизации по oAuth

После первого запуска в базе создается несколько тестовых страниц и фотографий. Для продакшена это поведение не нужно и отключается флагом в настройках.

Буквально месяц назад я запустил собственный инстанс и начал обкатывать его, заводя настоящие данные. Некоторые шероховатости встречаются, но в остальном я полностью доволен результатом. Теперь проект будет постепенно развиваться и дорабатываться. Первоочередные задачи — ускорить отображение дерева, разрешить загрузку документов в виде PDF и добавить тонкую настройку прав доступа. Было бы хорошо улучшить юзабилити админки в некоторых местах или сделать автоматическое распознавание лиц на фото — но это не точно.

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (37):

  1. xakep2011
    /#19741172

    Отличный пост и идея, спасибо! Есть планы по монетизации?

    • impwx
      /#19741436

      Планов по монетизации нет: идея как раз в том, чтобы пользователь мог никому не платить и ни от кого не зависеть.

      • gekt0r
        /#19746140

        Спасибо за рассказ!
        Вы меня опередили, я планировал сделать точно то же самое на C#, теперь знаю с какими сложностями могу столкнуться, и кроме того теперь можно воспользоваться уже написанным Вами кодом

  2. evorios
    /#19741486

    Мне кажется, тут не хватает возможности экспортировать все персональные данные в полу-машинный формат, из которого даже после третьей мировой ваши потомки смогли бы достать ценную информацию. Такой возможностью хранить одновременно и текст, и медиа-файлы обладает bencode-формат.

    • impwx
      /#19741688

      Для генеалогических данных есть специальный формат GEDCOM. Поддержка его при импорте и экспорте — одна из актуальных задач.

      Касательно bencode: наверное в него очень просто записывать, но расковырять назначение полей в таком файле без подробной документации будет сложно. Пока что был вариант сделать экспорт в zip-архив: медиа-файлы лежат как есть, данные экспортируются в виде JSON/XML.

  3. Vitaly83vvp
    /#19741666

    Это desktop проект на C#, так? Если да, то, как по мне, много зависимостей: PostgreSQL, Elastic, приложение для OAuth.
    Я, как пользователь MyHeritage, предпочитаю работать с самодостаточной копией программы (имею в виду не online). С 8-ой версии, они перешли от ZIP'ованного текстового формата к SQLite базе.
    У вас PG был выбран по причине бОльшей функциональности? Почему не SQLite? Или эти зависимости нужны только для серверной стороны? Этот момент немного непонятен. Обычный пользователь, врядли, поставит себе указанные зависимости.

    • impwx
      /#19741748

      Не совсем: это веб-проект, который нужно поставить на собственный веб-сервер. Аргументация такого подхода и преимущества перед десктопным приложением описаны в самом начале.

      Согласен, что установка требует больше усилий, чем хотелось бы (хотя у меня скачать два инсталлятора и прощелкать next-next-next занимает не более 5 минут). Насколько я понимаю, с данной проблемой помог бы docker, но мне пока не доводилось с ним работать. Если кто-то из читателей сможет помочь — буду признателен.

      Плюс использования OAuth в том, что весь геморрой с авторизацией (двухфакторка, смена пароля, восстановление и т.д.) ложатся на Facebook. Если отказаться от этой зависимости — придется все это реализовывать самостоятельно и требовать настроенный email-сервер для отправки писем. По сравнению с этим настроить приложение все-таки гораздо проще.

      • Vitaly83vvp
        /#19741824 / -2

        Я, возможно, невнимательно прочитал момент, что это веб-проект. Для серверной реализации, такой вариант в порядке вещей.
        Но, для меня, иметь локальную базу у себя, а не где-то на сервере, гораздо важнее. Потому что, если сервис прекратит работу, то все данные будут утеряны. Так, я терял доступ к Bitbucket, по причине блокировок IP Amazon серверов (сейчас уже всё нормально, но осадок остался), прекращение работы QIP — у меня там была почта. Я минимизировал последствия этого тем, что имел копии данных локально (и, конечно, не в одном экземпляре). Ну, и проблемы у самого хостера никто не отменял.
        Да, у online решений с одной стороны есть несомненные плюсы, но есть и минусы в плане того, что данных у пользователя, фактически, нет — только доступ к ним.

        • impwx
          /#19741954 / +5

          Еще раз: у вас есть полный физический доступ к базе и файлам, Bonsai никак не ограничивает возможности для их защиты.

          Например, вы создаете собственную виртуалку в облаке и ставите на нее весь необходимый софт, включая базу и веб-сервер. Вы не зависите ни от каких провайдеров услуг, кроме собственно Amazon/Digital Ocean/Azure, а шанс их внезапного выхода из бизнеса без сохранения ваших данных я считаю принебрежимо малым. Даже если ваш IP попадет в черный список, вы можете хотя бы зайти на сервер с помощью прокси и сохранить все ваши данные.

          Если это вам кажется недостаточно безопасным режимом — Bonsai можно развернуть на собственном «железном» сервере, который стоит у вас в квартире. Тогда вы вообще не будете зависеть ни от чего, кроме электричества и интернета.

          В обоих случаях делать бэкапы крайне важно, но эта задача остается лично на вас. Я для себя сделал просто периодическую задачу, которая каждый день делает dump базы и кладет его на два запасных диска.

          • Vitaly83vvp
            /#19742836 / -2

            Для меня, online подразумевает доступ для множества пользователей.

            Мне не составляет проблем поставить весь этот софт, но для простых пользователей, которые умеют, разве что, работать с браузером, этот вариант может не понравиться. Как минимум, PostgreSQL может не стартовать «из коробки». Сколько не пробовал, всегда приходилось лезть в конфиги и вносить правки (вероятно, мне просто везло на такое и у других всё сразу работало). Пробовал с разными Linux дистрибутивами. Под Windows не пробовал, возможно, там всё работает.
            Это, что касалось концепции продукта. Что касается самого продукта, то он представляет интерес. Мне, как раз, интересна возможность online доступа тоже. Он сейчас работает только под Windows? Можно ли скомпилить для работы в Linux средах? В последних VS, вроде, было что-то такое, хотя, могу ошибаться.

            • impwx
              /#19743018 / +1

              Доступ для множества пользователей работает. Есть даже три группы пользователей:

              • Администратор: полный доступ.
              • Читатель: может просматривать все страницы, но не редактировать их. Есть мысль о том, чтобы все-таки разрешить читателю редактировать страницу про самого себя.
              • Гость: по умолчанию не видит ничего. Может подать заявку на регистрацию, которую должен проверить админ. Тогда гость сам становится читателем или админом.

              Насчет установки: как вы понимаете, сервер нужно настроить только один раз для семьи. Все остальные читатели и редакторы могут заходить через браузер как на любой другой сайт, и им не придется думать о базе данных, эластике и прочей ерунде. Я постараюсь облегчить первоначальную настройку с помощью докер-образа, но превратить этот процесс в однокнопочный все равно не получится: как минимум владелец должен будет настроить процесс бэкапа так, как ему удобно.

              Насчет операционных систем: все компоненты Bonsai (рантайм, фреймворк, зависимости) являются кроссплатформенными. Я использую Windows и пока не проверял, но запустить его на Linux должно быть вполне возможно — в лучшем случае просто сразу заработает, в худшем придется поправить пару строк.

          • beetleinweb
            /#19742854

            Делайте виртуалки сами, локально. Выкладывайте для тех потенциальных пользователей, кто не сразу решится на подвиг установки из девяти шагов. Используйте сами для тестов и как бекапы полностью работоспособной настроенной системы в какой-то стадии разработки — делайте снепшоты.

            Виртуалки локально — это совсем несложно. И очень полезно для веб-проекта (и для любого, требующего сложного процесса установки и настройки). Я сам долго откладывал в своём проекте, потом жалел о том, что столько откладывал. Один раз разберитесь с установкой OS (может быть даже разных). Попробуйте конвертирование разных форматов виртуалок (VMware, Virtual Box) — люди любят разные фломастеры.

            • impwx
              /#19742876

              Спасибо, это хорошая идея — попробую сделать на досуге.

              • beetleinweb
                /#19743010

                Да, вас тут ещё про Docker спрашивают. Ещё один совет до кучи — сначала сделайте переход на Linux, а потом уже пробуйте Docker. Docker под Windows — это проблемно, медленно и почти бесполезно, как мне показалось. А начать в любом случае лучше с простых виртуалок. Пусть будут как промежуточный шаг в направлении виртуализации-контейнеризации.

  4. trueClearThinker
    /#19742332 / +1

    Docker образ будет?

    • impwx
      /#19742890

      Да, есть в ближайших планах. Пока не доводилось им пользоваться, но разберусь. Больше интересует, что при встраивании чужих приложений (elastic, pgsql) происходит с лицензией?

      • trueClearThinker
        /#19745552

        Встраивать ничего не придется. Разверните соответсвующие контейнеры (elastic, pgsql) рядом с контейнером своего приложения. https://docs.docker.com/compose/ вам в помощь.

        • impwx
          /#19750600

          Образ для докера сделали, в Readme описан процесс установки. Если есть время — пожалуйста, попробуйте?

  5. KvanTTT
    /#19742992 / +1

    Из тех, что я смотрел, самыми приятными в использовании показались Gramps (бесплатный)

    Мне не понравился. По юзабилити и дизайну далек от коммерческих приложений (по крайней мере от MyHeritage). Может как-нибудь сделают что-нибудь получше на Авалонии :)


    Я просмотрел множество вариантов. Самый эстетически приятный был на сайте MyHeritage

    Мне тоже больше всего понравилась визуализация там.


    Данные хранятся в базе в виде направленного графа и, в теории, его должно быть несложно визуализировать. На практике же именно с отображением дерева возникло больше всего сложностей.

    А еще есть экзотический вариант визуализации с помощью интерфейса к Git. Писал о таком осенью Генеалогическое древо внутри Git.


    Задумайтесь на минуту: веб в нынешнем его виде существует едва ли двадцать лет, а семейная история подразумевает хранение веками.

    Если пофантазировать, то деревья будут все больше и больше объединяться, т.к. количество заинтересованных и качество сервисов будут расти. Возможно, крупнейшие социальные сервисы (vk, facebook) внедрят функциональность по генеалогическим деревьям и лучше продумают вопросы смертности (сейчас большой потребности нет).


    Первоочередные задачи — ускорить отображение дерева, разрешить загрузку документов в виде PDF и добавить тонкую настройку прав доступа.

    Думаю логичным решением является доступ по email. Хотя может действтительно нужно что-то еще более тонкое, чтобы часть дерева или часть инфы можно было шарить всем, а часть — определенному кругу лиц.

    • beetleinweb
      /#19743120 / +1

      Социальные сервисы типа вышеупомянутых — имхо, зло. Весь интернет должен быть социальной сетью, без деления на отдельные несовместимые между собой песочницы, старающиеся свой контент отгородить заборами. И отнимающие силы разработчиков, которые должны были быть потрачены на весь веб целиком, а не оазисы социальности в пустыне.

      Но сама идея — объединять генеалогические деревья-«бонсайчики» при рождении ребёнка — по-моему, замечательная. Автор должен оценить. Можно через экспорт-импорт слияние реализовать. Ну и просто перекрёстным связям уделить больше внимания. Есть бездетные браки, есть крёстные-крестники. Наконец, просто друзья-подруги — на семейных фото иные встречаются чаще многих кровных членов семьи. Если есть возможность завести внешний IP (или пользоваться сервисом для динамического IP), то можно делать ссылки даже с одной домашней виртуалки на другую.

      Фича хороша и для популяризации проекта. Она может редко используемая, но такая понятная, хорошо иллюстрируемая. Умильно-сентиментальная. Цепляюще-запоминаемая.

      • impwx
        /#19743248

        Идея с объединением деревьев действительно прикольная, но реализовать ее как следует будет невероятно сложно. По сути, это превратит централизованную систему в децентрализованную, со всеми вытекающими последствиями — необходимостью синхронизации дальнейших изменений, правами доступа, разрешением конфликтов, пересылкой больших файлов… Хотя через десять лет все равно придется полностью переписывать — там подумаем :)

        • beetleinweb
          /#19743272

          Я вообще-то подразумевал в первую очередь постоянное слияние. Папа и мама съехались и стали жить на одном сервере. :)

        • beetleinweb
          /#19743338

          — В формате генеалогических древ главное — чтобы совпали кодировки.
          — UTF-8?
          — Не… DNA-4.

  6. zen
    /#19743164 / +2

    Мне кажется, что с полнотекстовым поиском вы поторопились. Надо поставить русскую морфологию, например, отсюда github.com/postgrespro/hunspell_dicts и все должно работать. Дело в том, что по-умолчанию используется стеммер snowball, который довольно тупой.

    Вот, что получается:

    select ts_lexize('russian_stem','Иванова'), ts_lexize('russian_stem','Иванов'), ts_lexize('russian_stem','Иван');
    ts_lexize | ts_lexize | ts_lexize
    -----------+-----------+-----------
    {иванов} | {иван} | {ива}
    (1 row)

    Понятно, что ничего хорошего не получится. Если поставить hunspell_ru_ru, то ситуация лучше
    select ts_lexize('russian_hunspell','Иванова'), ts_lexize('russian_hunspell','Иванов'), ts_lexize('russian_hunspell','Иван');
    ts_lexize | ts_lexize | ts_lexize
    -----------+-----------+-----------
    (null) | {иван} | {иван}
    (1 row)

    russian_aot_hunspell выдает побогаче
    select ts_lexize('russian_aot_hunspell','Иванова'), ts_lexize('russian_aot_hunspell','Иванов'), ts_lexize('russian_aot_hunspell','Иван');
    ts_lexize | ts_lexize | ts_lexize
    ----------------------------------+-----------+-----------
    {иванов,иваново,ивановец,иванов} | {иванов} | {иван}
    (1 row)

    Можно в конце-концов сделать словарь синонимов и вбить туда исключения, я рекомендую использовать dict_xsyn для гибкости.

  7. Anton-V-K
    /#19743412 / -1

    Круто, но…

    .NET Core 2.1+

    Я думаю, реализация на тёплом ламповом PHP + MySQL была бы более востребована массами, поскольку такой хостинг проще найти по доступной цене.
    .NET в облаке — это, конечно, современно, но обывателю ввязываться в это ради генеалогического дерева нецелесообразно.
    Ну, и с технической точки зрения организация всего генеалогического зоопарка в виде P2P выглядела бы революционно…
    В любом случае ещё один бесплатный генеалогический веб-движок — это отлично :)

    • impwx
      /#19743874 / +1

      У меня не было цели сделать популярный или революционный продукт продукт. Я делал в первую очередь удобный инструмент для себя, поэтому и технологии выбирал на свой вкус. Так уж случилось, что я считаю PHP одним из самых плохо спроектированных языков на свете, и похоже что это начали понимать даже сами разработчики, но холивар оставим на другой раз.

      Что же до простого обывателя — ему достаточно будет запустить docker-образ и не придется думать о том, на чем написан движок. Хостинг для виртуалок в 2019 году очень широко распространен и стоит вполне доступно.

  8. UksusoFF
    /#19744170

    Давно хочу прикрутить подобный график как в ELK для webtrees. Можно по-подробнее в чем там проблема с временем рендера? Как поможет перенос логики на бэк?

    • impwx
      /#19744214

      Есть свойство thoroughness. Чем меньше значение — тем быстрее рендерится, чем больше — тем качественнее получается граф. По умолчанию там стоит значение 7, но оно дает довольно «шумный» граф с множеством лишних пересечений. Для хорошего результата приходится использовать значение 400, но тогда layout считается заметно дольше — порядка 5 секунд.

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

      • UksusoFF
        /#19744412

        А можете скинуть на pastebin или еще куда-нибудь кускок данных которые подаются сюда?

  9. questor
    /#19744424

    пардон, закрыл вкладку, а когда вернулся на предыдущую страницу и обрадовался, что полунаписанный комментарий остался — то не обратил внимания, что комментарий не в ту ветку попал.

  10. questor
    /#19744462

    Скачал проект, чтобы посмотреть исходники на досуге. Обожаю смотреть код реальных проектов, кто и как делал те или иные решения.

    А вот насчёт будущего не проекта даже, а по поводу данного конкретного файла данных скажу так. Будучи реалистом я думаю, что через 50 лет кто-то опубликует ментаграмму на хапле-хапле:

    «Надо же, мой дед носил хипстерские шмотки еще до того, как это стало мейнстримом». Первым делом я сохранил в мемокристалл данные (представляете, там не было ни одного голографического снимка! а некоторые побились и выглядели чёрно-белыми) и решил: «Хочешь сделать что-то хорошо — сделай это сам!»


    Все совпадения случайны :)

  11. motomac
    /#19744928

    Тоже сталкивался с проблемой, как у автора, искал решения и даже начинал пилить свой велосипед. Действительно, из standalone движков более или менее работоспособен только webtrees, от одного внешнего вида которого воротит. Еще есть один более приличный, но платный проект TNG. Автор занимается им в одиночку вот уже 18 лет. Конечно, выглядит тоже не очень современно, да и внутри тоже заметны тренды нулевых. Оба движка на православном PHP + MySQL.

    На мой взгляд, сервис подобного рода должен быть «гибридным», т.е. уметь работать и с локальной базой, и с удаленной для многопользовательского доступа. Например на PouchDB.

  12. Cheater
    /#19747116

    По поводу Graphviz: покажите плз пример данных, который он отрисовал некорректно? (желательно в *.dot) В графвиз куча различных настроек, приёмов и движков для отрисовки, часто основной сложностью становится выбор нужных параметров. Graphviz это индустриальный стандарт в визуализации графов, я крайне рекомендую не выкидывать его из списка кандидатов. Кроме того вы пользуетесь Viz.js, а не самим Graphviz. Судя по описанию проекта, это сборка на Emscripten обычного графвиза, так что поведение у Viz.js и Graphviz теоретически д.б. одинаковое, но я бы перестраховался и попробовал отрисовать все проблемные графы в традиционном Graphviz.

    • impwx
      /#19747452

      К сожалению, тестовые скрипты для генерации DOT-синтаксиса канули в лету, но вот небольшой пример. Нужно сгенерировать вот такой граф:



      Моя лучшая попытка на DOT
      digraph G {
          splines=ortho
          
      	n1 [label="Иванов Иван Иванович", shape=rect, width=3, fixedsize=true]
      	n2 [label="Иванова Анна Николаевна", shape=rect, width=3, fixedsize=true]
      	n3 [label="Иванов Михаил Иванович", shape=rect, width=3, fixedsize=true]
      	n4 [label="Иванов Константин Иванович", shape=rect, width=3, fixedsize=true]
      	n5 [label="Иванов Алексей Иванович", shape=rect, width=3, fixedsize=true]
      
      	x1 [label="", fixedwidth=true, width=0.01, height=0.01]
      	
      	{n1 n2} -> x1 [arrowhead=none]
      	x1 -> {n3 n4 n5} [arrowhead=none]
      }
      

  13. Cheater
    /#19747868

    Выравнивание групп нодов традиционно делается через объявление подграфов, +можно добавить ещё фиктивных нодов-точек и вручную управлять их раскладкой. Хотя да, это избыток ручных настроек и я не знаю как с ним бороться.

    Вот корректный граф:



    gv
    digraph G {
    splines=ortho
    node[shape=rect]
    edge[arrowhead=none]

    {
    rank=same
    n1 [label="Иванов Иван Иванович"]
    n1n2 [label="", shape = point]
    n2 [label="Иванова Анна Николаевна"]

    n1->n1n2->n2
    }

    n3n4n5 [label="", shape = point]

    {
    rank=same
    n3 [label="Иванов Михаил Иванович"]
    n4 [label="Иванов Константин Иванович"]
    n5 [label="Иванов Алексей Иванович"]
    }

    n3n4n5->{n3 n4 n5}
    n1n2->n3n4n5
    }

    • redmanmale
      /#19748322

      Пытался делать через точки, но на более-менее больших графах (30-40 человек) это превращается в мучение.
      И очень плохо автоматизируется.