gene-archive 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. gene_archive-0.4.0/LICENSE +21 -0
  2. gene_archive-0.4.0/PKG-INFO +239 -0
  3. gene_archive-0.4.0/README.md +217 -0
  4. gene_archive-0.4.0/gene_archive/__init__.py +1 -0
  5. gene_archive-0.4.0/gene_archive/apply.py +92 -0
  6. gene_archive-0.4.0/gene_archive/build.py +368 -0
  7. gene_archive-0.4.0/gene_archive/cli.py +416 -0
  8. gene_archive-0.4.0/gene_archive/config.py +85 -0
  9. gene_archive-0.4.0/gene_archive/discovery.py +363 -0
  10. gene_archive-0.4.0/gene_archive/editor.py +777 -0
  11. gene_archive-0.4.0/gene_archive/editor_web/editor.css +107 -0
  12. gene_archive-0.4.0/gene_archive/editor_web/editor.js +862 -0
  13. gene_archive-0.4.0/gene_archive/editor_web/index.html +21 -0
  14. gene_archive-0.4.0/gene_archive/engine/help.html +163 -0
  15. gene_archive-0.4.0/gene_archive/engine/i18n/en.json +595 -0
  16. gene_archive-0.4.0/gene_archive/engine/i18n/ro.json +595 -0
  17. gene_archive-0.4.0/gene_archive/engine/web/app.css +1082 -0
  18. gene_archive-0.4.0/gene_archive/engine/web/app.js +3323 -0
  19. gene_archive-0.4.0/gene_archive/engine/web/index.html +297 -0
  20. gene_archive-0.4.0/gene_archive/engine/web/languages.js +23 -0
  21. gene_archive-0.4.0/gene_archive/engine/web/vendor/leaflet/LICENSE +26 -0
  22. gene_archive-0.4.0/gene_archive/engine/web/vendor/leaflet/leaflet.css +661 -0
  23. gene_archive-0.4.0/gene_archive/engine/web/vendor/leaflet/leaflet.js +6 -0
  24. gene_archive-0.4.0/gene_archive/ged_export.py +289 -0
  25. gene_archive-0.4.0/gene_archive/ged_import.py +912 -0
  26. gene_archive-0.4.0/gene_archive/hints.py +161 -0
  27. gene_archive-0.4.0/gene_archive/localize.py +428 -0
  28. gene_archive-0.4.0/gene_archive/messages.py +18 -0
  29. gene_archive-0.4.0/gene_archive/privacy.py +139 -0
  30. gene_archive-0.4.0/gene_archive/server.py +609 -0
  31. gene_archive-0.4.0/gene_archive/share_cards.py +114 -0
  32. gene_archive-0.4.0/gene_archive/templates/AGENTS.en.md +86 -0
  33. gene_archive-0.4.0/gene_archive/templates/AGENTS.ru.md +85 -0
  34. gene_archive-0.4.0/gene_archive/validate.py +299 -0
  35. gene_archive-0.4.0/gene_archive.egg-info/PKG-INFO +239 -0
  36. gene_archive-0.4.0/gene_archive.egg-info/SOURCES.txt +46 -0
  37. gene_archive-0.4.0/gene_archive.egg-info/dependency_links.txt +1 -0
  38. gene_archive-0.4.0/gene_archive.egg-info/entry_points.txt +2 -0
  39. gene_archive-0.4.0/gene_archive.egg-info/requires.txt +3 -0
  40. gene_archive-0.4.0/gene_archive.egg-info/top_level.txt +1 -0
  41. gene_archive-0.4.0/pyproject.toml +38 -0
  42. gene_archive-0.4.0/setup.cfg +4 -0
  43. gene_archive-0.4.0/tests/test_apply.py +82 -0
  44. gene_archive-0.4.0/tests/test_editor.py +349 -0
  45. gene_archive-0.4.0/tests/test_engine.py +478 -0
  46. gene_archive-0.4.0/tests/test_ged_import.py +471 -0
  47. gene_archive-0.4.0/tests/test_schema.py +124 -0
  48. gene_archive-0.4.0/tests/test_server.py +158 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Igor Melnikov and gene-archive contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,239 @@
1
+ Metadata-Version: 2.4
2
+ Name: gene-archive
3
+ Version: 0.4.0
4
+ Summary: Static site generator for a family history archive: tree, people, places, documents, story — in Russian, English and Romanian
5
+ Author: Igor Melnikov
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/igormel81/gene_archive
8
+ Project-URL: Documentation, https://github.com/igormel81/gene_archive/tree/main/docs
9
+ Keywords: genealogy,family tree,gedcom,static site,family history,родословная
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Sociology :: Genealogy
13
+ Classifier: Natural Language :: Russian
14
+ Classifier: Natural Language :: English
15
+ Classifier: Natural Language :: Romanian
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Provides-Extra: images
20
+ Requires-Dist: Pillow>=10; extra == "images"
21
+ Dynamic: license-file
22
+
23
+ <div align="center">
24
+
25
+ # gene-archive
26
+
27
+ ### Your family's history as a website — every fact backed by its document.
28
+
29
+ [![Release](https://img.shields.io/github/v/release/igormel81/gene_archive?color=8a4b2a)](https://github.com/igormel81/gene_archive/releases)
30
+ [![CI](https://github.com/igormel81/gene_archive/actions/workflows/ci.yml/badge.svg)](https://github.com/igormel81/gene_archive/actions/workflows/ci.yml)
31
+ [![License: MIT](https://img.shields.io/badge/license-MIT-2e7d32)](LICENSE)
32
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776ab)
33
+ ![Languages](https://img.shields.io/badge/UI-English%20·%20Русский%20·%20Română-6b5b95)
34
+
35
+ **[▶ Live demo](https://igormel81.github.io/gene_archive/)** ·
36
+ **[Quick start](#quick-start)** ·
37
+ **[Documentation](#documentation)** ·
38
+ **[Русская версия](README.ru.md)**
39
+
40
+ </div>
41
+
42
+ Scans in shoeboxes, dates in a grandmother's notebook, a tree in MyHeritage, stories that
43
+ only one aunt still remembers. **gene-archive** gathers all of it into one beautiful website
44
+ your whole family can open on a phone: an interactive tree, a card for every person with the
45
+ documents behind each date, an archive of scans with transcriptions, a map of family places
46
+ and the story of the family — in English, Russian and Romanian.
47
+
48
+ Relatives don't just read it. They leave comments, suggest corrections and, with an account,
49
+ edit the archive right in the browser. You keep everything: one JSON file, your own domain,
50
+ free hosting, full history in git, GEDCOM in and out.
51
+
52
+ ![The tree and a person's card](docs/img/tree-en.png)
53
+
54
+ > Grew out of a real family archive: ~550 people, ~500 documents, three countries, relatives
55
+ > commenting in three languages. The [demo](https://igormel81.github.io/gene_archive/) is a
56
+ > fictional family made to show every feature.
57
+
58
+ ## Why gene-archive
59
+
60
+ | | |
61
+ |---|---|
62
+ | 📜 **Proof for every fact** | Each date and place shows where it comes from — a document, a family story, a calculation from an age — and `S12` in any text opens the scan. Doubtful links are drawn dashed, not hidden. |
63
+ | 👨‍👩‍👧 **Made for the whole family** | Relatives read it like a book, find themselves in the tree, ask "how are we related?", comment and suggest corrections — no registration on a platform. |
64
+ | ✏️ **Edit in the browser** | Accounts for editors and administrators, checks before every save, nobody overwrites anybody, every change is a git commit you can undo. |
65
+ | 🔒 **Private by default** | Living people show only their names — no dates, places or documents, not even in the data file or the GEDCOM export. |
66
+ | 🏠 **Your data, your site** | One JSON file, plain HTML, free hosting on GitHub Pages or Netlify, or your own server. No subscription, no lock-in: GEDCOM import and export. |
67
+ | 🌍 **Three languages** | English, Russian and Romanian interface out of the box; your own texts can be translated too. |
68
+ | 🤖 **AI-ready** | Every new site gets `AGENTS.md` for Claude Code and Codex, plus guides and ready prompts for research in archives and open sources. |
69
+
70
+ ## Features
71
+
72
+ ### 🌳 Tree, people and "How we are related"
73
+
74
+ - **Hourglass tree** around any person, a whole-tree chart, export to PNG, branches by surname.
75
+ - **Person card**: dates and places with their documents, alternative versions of a date,
76
+ relatives, a connected life story.
77
+ - **How we are related**: the exact relation between any two people — *second cousin once
78
+ removed*, *great-uncle*, *half-brother*, in-laws — with the chain of people in between.
79
+ In Russian and Romanian too, with their own kinship terms.
80
+ - **Search everything** from the magnifier: names, villages, any word of a document transcription, places, the story,
81
+ leads — word endings, the pre-1918 spelling and Latin/Cyrillic letters do not get in the way (“Kolodin” finds Колодин).
82
+ - **People**: a calendar of birthdays and anniversaries, surname pages, the zodiac.
83
+
84
+ ![How we are related](docs/img/kinship-en.png)
85
+
86
+ ### 🗂 Archive of documents
87
+
88
+ - Every scan and photograph with a transcription, the back of a photo, PDF previews.
89
+ - Filters by type of document and by family line; restored photos next to originals.
90
+ - **Sources**: every archive and database you searched — what it is, what was found there,
91
+ the documents and the research-log entries for each; plus a log of corrections.
92
+
93
+ | | |
94
+ |---|---|
95
+ | ![Archive, Russian edition](docs/img/archive-ru.png) | ![Calendar](docs/img/calendar-en.png) |
96
+
97
+ ### 🗺 Places and the family story
98
+
99
+ - A map (Leaflet + OpenStreetMap) with family branches, moves between places,
100
+ "then and now" names and timelines.
101
+ - The **Story** page: your narrative with a timeline and a "by place" filter.
102
+ - **Questions to relatives**: what the family does not know yet — answered right on the site.
103
+
104
+ ### ✏️ Online editor
105
+
106
+ - People, families, documents (with scan uploads) and places in a browser — on a phone too.
107
+ - Dates the way people write them: "about 1899", "between 1900 and 1905", "9.10.1899".
108
+ - A change that would break the data is not saved; the editor says what is wrong.
109
+ - Two people editing the same record? The second one is warned instead of overwriting.
110
+ - Every change goes to the corrections log and becomes a git commit by its author;
111
+ an administrator can restore any version.
112
+ - **Suggest a correction**: readers send a fix from a person's card; an administrator
113
+ applies it in one click.
114
+
115
+ ![The online editor](docs/img/editor-en.png)
116
+
117
+ ### 🔎 Research assistant
118
+
119
+ - **Data checks** (`gene hints` and the editor's "Checks" tab): impossible facts first — born
120
+ after the mother's death, a parent aged 11 — then gaps: no dates, a date without a document,
121
+ people not connected to the tree, documents nobody refers to.
122
+ - A step-by-step **research guide** and ready **AI prompts** for archives and open sources.
123
+
124
+ | | |
125
+ |---|---|
126
+ | ![Data checks](docs/img/hints-en.png) | ![Readers' proposals](docs/img/proposals-en.png) |
127
+
128
+ ### 🚀 Publish anywhere
129
+
130
+ - `gene build` → plain HTML for GitHub Pages, Netlify, Cloudflare Pages or any web server.
131
+ - `gene serve` or the Docker image adds comments, a PIN for a private archive and the editor.
132
+ - **Share** any person, document or place: the link shows a photo, a name and a description in
133
+ Telegram, WhatsApp and Facebook.
134
+ - Search engines find every person and surname: static pages, sitemap, Schema.org;
135
+ living people are `noindex`.
136
+
137
+ ## How it compares
138
+
139
+ | | gene-archive | Platforms (MyHeritage, Ancestry…) | Self-hosted apps (webtrees, Gramps Web) |
140
+ |---|:-:|:-:|:-:|
141
+ | Your own site and domain | ✓ | — | ✓ |
142
+ | Free static hosting, no database | ✓ | — | — |
143
+ | Free, open source | ✓ (MIT) | subscription | ✓ |
144
+ | A document behind every fact, on the page | ✓ | partly | partly |
145
+ | Story, map and archive in one site | ✓ | partly | partly |
146
+ | Relatives comment and suggest corrections | ✓ | ✓ | partly |
147
+ | Editing in a browser with roles and history | ✓ | ✓ | ✓ |
148
+ | DNA matches, hints from billions of records | — | ✓ | — |
149
+
150
+ gene-archive does not try to be a database of the world's records. It is the place where
151
+ **your** family's evidence and stories live — and where your relatives actually look.
152
+
153
+ ## Quick start
154
+
155
+ You need Python 3.10+. [Pillow](https://pypi.org/project/Pillow/) makes thumbnails
156
+ (optional), `pdftoppm` from poppler-utils makes PDF previews (optional).
157
+
158
+ ```sh
159
+ pip install "gene-archive[images] @ git+https://github.com/igormel81/gene_archive"
160
+
161
+ gene init my-family --example # a copy of the demo family to look around
162
+ gene serve my-family # → http://127.0.0.1:8111
163
+ ```
164
+
165
+ Start your own archive:
166
+
167
+ ```sh
168
+ gene init my-family --lang en # an empty site: family_tree.json + site.json
169
+ gene import tree.ged my-family # …or start from a GEDCOM file
170
+ gene validate my-family # check the data
171
+ gene build my-family # the site is in my-family/_site
172
+ ```
173
+
174
+ Then fill in `family_tree.json` (people, families, documents, places — see
175
+ [the data format](docs/data-format.md)) and `site.json` ([settings](docs/configuration.md)),
176
+ put scans into `sources/`, and write the story in `content/story.html`
177
+ ([content pages](docs/content.md)).
178
+
179
+ **New to genealogy?** Read [How to start researching your family history](docs/research-guide.md):
180
+ interviews, home documents, online databases, archives, evidence and how to record it.
181
+
182
+ ## Quick start with an AI assistant
183
+
184
+ Works in any AI coding environment — Claude Code, Codex, Cursor, GitHub Copilot, Windsurf, Gemini CLI.
185
+ Open an empty folder in it and paste:
186
+
187
+ ```text
188
+ Set up a family archive site with gene-archive (https://github.com/igormel81/gene_archive) in this folder.
189
+ 1. Install it: pip install "gene-archive[images] @ git+https://github.com/igormel81/gene_archive"
190
+ 2. Create the site: gene init . --lang en (or gene import <my file>.ged . if I give you a GEDCOM file),
191
+ then git init and a first commit.
192
+ 3. Read AGENTS.md and follow it in all further work: never invent facts or relationships,
193
+ cite a document for every fact, keep doubtful things as leads.
194
+ 4. Ask me about the family (my parents, grandparents, what documents and photos we have at home)
195
+ and fill family_tree.json and site.json from my answers.
196
+ 5. Run gene validate and gene serve ., give me the link and show what you changed.
197
+ ```
198
+
199
+ Then just talk to it: "here is a scan of my grandfather's birth certificate", "my aunt says…".
200
+ More ready-made requests and the rules for reviewing the agent's work:
201
+ [docs/ai-assistants.md](docs/ai-assistants.md).
202
+
203
+ ## Documentation
204
+
205
+ | | English | Русский |
206
+ |---|---|---|
207
+ | How to start the research | [research-guide.md](docs/research-guide.md) | [research-guide.ru.md](docs/research-guide.ru.md) |
208
+ | AI requests for research in open sources and archives | [research-prompts.md](docs/research-prompts.md) | [research-prompts.ru.md](docs/research-prompts.ru.md) |
209
+ | Filling the site with Claude or Codex | [ai-assistants.md](docs/ai-assistants.md) | [ai-assistants.ru.md](docs/ai-assistants.ru.md) |
210
+ | Data format (`family_tree.json`) | [data-format.md](docs/data-format.md) | [data-format.ru.md](docs/data-format.ru.md) |
211
+ | Settings (`site.json`) | [configuration.md](docs/configuration.md) | [configuration.ru.md](docs/configuration.ru.md) |
212
+ | Story, biographies, news | [content.md](docs/content.md) | [content.ru.md](docs/content.ru.md) |
213
+ | Languages and translations | [translations.md](docs/translations.md) | [translations.ru.md](docs/translations.ru.md) |
214
+ | Publishing: GitHub Pages, server, Docker, comments | [deploy.md](docs/deploy.md) | [deploy.ru.md](docs/deploy.ru.md) |
215
+ | Online editor: accounts, history, readers' proposals | [editor.md](docs/editor.md) | [editor.ru.md](docs/editor.ru.md) |
216
+ | Contributing to the engine | [CONTRIBUTING.md](CONTRIBUTING.md) | [CONTRIBUTING.ru.md](CONTRIBUTING.ru.md) |
217
+
218
+ ## Commands
219
+
220
+ | Command | What it does |
221
+ |---|---|
222
+ | `gene init <folder> [--lang ru\|en\|ro] [--example]` | a new site (or a copy of the demo) |
223
+ | `gene import tree.ged <folder> [--root @I12@]` | a new site from GEDCOM |
224
+ | `gene validate [folder]` | errors and warnings in the data |
225
+ | `gene build [folder] [-o out] [--base-url URL]` | build the static site |
226
+ | `gene serve [folder] [--pin 1234] [--moderate]` | build and serve with comments |
227
+ | `gene serve <folder> --editor` | … and the online editor at `/edit/` |
228
+ | `gene users <folder> add NAME [--role admin] \| invite \| list \| role \| disable \| passwd` | editor accounts |
229
+ | `gene comments <folder> list\|approve ID\|hide ID\|done ID "note"` | moderate comments |
230
+ | `gene gedcom [folder] [-o file.ged] [--version 5.5.1\|7.0] [--for geni\|public]` | GEDCOM for Gramps and others; `--for geni` — for uploading to Geni, MyHeritage, Ancestry; `--for public` — for open forums, living people without names (`--full`: with living people, for your own backup) |
231
+ | `gene translations <folder> <lang>` | your texts that have no translation yet |
232
+ | `gene hints [folder] [--json]` | gaps and doubtful facts — a to-do list for the research |
233
+
234
+ Messages are in Russian when the system language is Russian (or `GENE_LANG=ru`).
235
+
236
+ ## License
237
+
238
+ [MIT](LICENSE). Leaflet is BSD-licensed, map tiles © OpenStreetMap contributors — see
239
+ [THIRD_PARTY.md](THIRD_PARTY.md).
@@ -0,0 +1,217 @@
1
+ <div align="center">
2
+
3
+ # gene-archive
4
+
5
+ ### Your family's history as a website — every fact backed by its document.
6
+
7
+ [![Release](https://img.shields.io/github/v/release/igormel81/gene_archive?color=8a4b2a)](https://github.com/igormel81/gene_archive/releases)
8
+ [![CI](https://github.com/igormel81/gene_archive/actions/workflows/ci.yml/badge.svg)](https://github.com/igormel81/gene_archive/actions/workflows/ci.yml)
9
+ [![License: MIT](https://img.shields.io/badge/license-MIT-2e7d32)](LICENSE)
10
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776ab)
11
+ ![Languages](https://img.shields.io/badge/UI-English%20·%20Русский%20·%20Română-6b5b95)
12
+
13
+ **[▶ Live demo](https://igormel81.github.io/gene_archive/)** ·
14
+ **[Quick start](#quick-start)** ·
15
+ **[Documentation](#documentation)** ·
16
+ **[Русская версия](README.ru.md)**
17
+
18
+ </div>
19
+
20
+ Scans in shoeboxes, dates in a grandmother's notebook, a tree in MyHeritage, stories that
21
+ only one aunt still remembers. **gene-archive** gathers all of it into one beautiful website
22
+ your whole family can open on a phone: an interactive tree, a card for every person with the
23
+ documents behind each date, an archive of scans with transcriptions, a map of family places
24
+ and the story of the family — in English, Russian and Romanian.
25
+
26
+ Relatives don't just read it. They leave comments, suggest corrections and, with an account,
27
+ edit the archive right in the browser. You keep everything: one JSON file, your own domain,
28
+ free hosting, full history in git, GEDCOM in and out.
29
+
30
+ ![The tree and a person's card](docs/img/tree-en.png)
31
+
32
+ > Grew out of a real family archive: ~550 people, ~500 documents, three countries, relatives
33
+ > commenting in three languages. The [demo](https://igormel81.github.io/gene_archive/) is a
34
+ > fictional family made to show every feature.
35
+
36
+ ## Why gene-archive
37
+
38
+ | | |
39
+ |---|---|
40
+ | 📜 **Proof for every fact** | Each date and place shows where it comes from — a document, a family story, a calculation from an age — and `S12` in any text opens the scan. Doubtful links are drawn dashed, not hidden. |
41
+ | 👨‍👩‍👧 **Made for the whole family** | Relatives read it like a book, find themselves in the tree, ask "how are we related?", comment and suggest corrections — no registration on a platform. |
42
+ | ✏️ **Edit in the browser** | Accounts for editors and administrators, checks before every save, nobody overwrites anybody, every change is a git commit you can undo. |
43
+ | 🔒 **Private by default** | Living people show only their names — no dates, places or documents, not even in the data file or the GEDCOM export. |
44
+ | 🏠 **Your data, your site** | One JSON file, plain HTML, free hosting on GitHub Pages or Netlify, or your own server. No subscription, no lock-in: GEDCOM import and export. |
45
+ | 🌍 **Three languages** | English, Russian and Romanian interface out of the box; your own texts can be translated too. |
46
+ | 🤖 **AI-ready** | Every new site gets `AGENTS.md` for Claude Code and Codex, plus guides and ready prompts for research in archives and open sources. |
47
+
48
+ ## Features
49
+
50
+ ### 🌳 Tree, people and "How we are related"
51
+
52
+ - **Hourglass tree** around any person, a whole-tree chart, export to PNG, branches by surname.
53
+ - **Person card**: dates and places with their documents, alternative versions of a date,
54
+ relatives, a connected life story.
55
+ - **How we are related**: the exact relation between any two people — *second cousin once
56
+ removed*, *great-uncle*, *half-brother*, in-laws — with the chain of people in between.
57
+ In Russian and Romanian too, with their own kinship terms.
58
+ - **Search everything** from the magnifier: names, villages, any word of a document transcription, places, the story,
59
+ leads — word endings, the pre-1918 spelling and Latin/Cyrillic letters do not get in the way (“Kolodin” finds Колодин).
60
+ - **People**: a calendar of birthdays and anniversaries, surname pages, the zodiac.
61
+
62
+ ![How we are related](docs/img/kinship-en.png)
63
+
64
+ ### 🗂 Archive of documents
65
+
66
+ - Every scan and photograph with a transcription, the back of a photo, PDF previews.
67
+ - Filters by type of document and by family line; restored photos next to originals.
68
+ - **Sources**: every archive and database you searched — what it is, what was found there,
69
+ the documents and the research-log entries for each; plus a log of corrections.
70
+
71
+ | | |
72
+ |---|---|
73
+ | ![Archive, Russian edition](docs/img/archive-ru.png) | ![Calendar](docs/img/calendar-en.png) |
74
+
75
+ ### 🗺 Places and the family story
76
+
77
+ - A map (Leaflet + OpenStreetMap) with family branches, moves between places,
78
+ "then and now" names and timelines.
79
+ - The **Story** page: your narrative with a timeline and a "by place" filter.
80
+ - **Questions to relatives**: what the family does not know yet — answered right on the site.
81
+
82
+ ### ✏️ Online editor
83
+
84
+ - People, families, documents (with scan uploads) and places in a browser — on a phone too.
85
+ - Dates the way people write them: "about 1899", "between 1900 and 1905", "9.10.1899".
86
+ - A change that would break the data is not saved; the editor says what is wrong.
87
+ - Two people editing the same record? The second one is warned instead of overwriting.
88
+ - Every change goes to the corrections log and becomes a git commit by its author;
89
+ an administrator can restore any version.
90
+ - **Suggest a correction**: readers send a fix from a person's card; an administrator
91
+ applies it in one click.
92
+
93
+ ![The online editor](docs/img/editor-en.png)
94
+
95
+ ### 🔎 Research assistant
96
+
97
+ - **Data checks** (`gene hints` and the editor's "Checks" tab): impossible facts first — born
98
+ after the mother's death, a parent aged 11 — then gaps: no dates, a date without a document,
99
+ people not connected to the tree, documents nobody refers to.
100
+ - A step-by-step **research guide** and ready **AI prompts** for archives and open sources.
101
+
102
+ | | |
103
+ |---|---|
104
+ | ![Data checks](docs/img/hints-en.png) | ![Readers' proposals](docs/img/proposals-en.png) |
105
+
106
+ ### 🚀 Publish anywhere
107
+
108
+ - `gene build` → plain HTML for GitHub Pages, Netlify, Cloudflare Pages or any web server.
109
+ - `gene serve` or the Docker image adds comments, a PIN for a private archive and the editor.
110
+ - **Share** any person, document or place: the link shows a photo, a name and a description in
111
+ Telegram, WhatsApp and Facebook.
112
+ - Search engines find every person and surname: static pages, sitemap, Schema.org;
113
+ living people are `noindex`.
114
+
115
+ ## How it compares
116
+
117
+ | | gene-archive | Platforms (MyHeritage, Ancestry…) | Self-hosted apps (webtrees, Gramps Web) |
118
+ |---|:-:|:-:|:-:|
119
+ | Your own site and domain | ✓ | — | ✓ |
120
+ | Free static hosting, no database | ✓ | — | — |
121
+ | Free, open source | ✓ (MIT) | subscription | ✓ |
122
+ | A document behind every fact, on the page | ✓ | partly | partly |
123
+ | Story, map and archive in one site | ✓ | partly | partly |
124
+ | Relatives comment and suggest corrections | ✓ | ✓ | partly |
125
+ | Editing in a browser with roles and history | ✓ | ✓ | ✓ |
126
+ | DNA matches, hints from billions of records | — | ✓ | — |
127
+
128
+ gene-archive does not try to be a database of the world's records. It is the place where
129
+ **your** family's evidence and stories live — and where your relatives actually look.
130
+
131
+ ## Quick start
132
+
133
+ You need Python 3.10+. [Pillow](https://pypi.org/project/Pillow/) makes thumbnails
134
+ (optional), `pdftoppm` from poppler-utils makes PDF previews (optional).
135
+
136
+ ```sh
137
+ pip install "gene-archive[images] @ git+https://github.com/igormel81/gene_archive"
138
+
139
+ gene init my-family --example # a copy of the demo family to look around
140
+ gene serve my-family # → http://127.0.0.1:8111
141
+ ```
142
+
143
+ Start your own archive:
144
+
145
+ ```sh
146
+ gene init my-family --lang en # an empty site: family_tree.json + site.json
147
+ gene import tree.ged my-family # …or start from a GEDCOM file
148
+ gene validate my-family # check the data
149
+ gene build my-family # the site is in my-family/_site
150
+ ```
151
+
152
+ Then fill in `family_tree.json` (people, families, documents, places — see
153
+ [the data format](docs/data-format.md)) and `site.json` ([settings](docs/configuration.md)),
154
+ put scans into `sources/`, and write the story in `content/story.html`
155
+ ([content pages](docs/content.md)).
156
+
157
+ **New to genealogy?** Read [How to start researching your family history](docs/research-guide.md):
158
+ interviews, home documents, online databases, archives, evidence and how to record it.
159
+
160
+ ## Quick start with an AI assistant
161
+
162
+ Works in any AI coding environment — Claude Code, Codex, Cursor, GitHub Copilot, Windsurf, Gemini CLI.
163
+ Open an empty folder in it and paste:
164
+
165
+ ```text
166
+ Set up a family archive site with gene-archive (https://github.com/igormel81/gene_archive) in this folder.
167
+ 1. Install it: pip install "gene-archive[images] @ git+https://github.com/igormel81/gene_archive"
168
+ 2. Create the site: gene init . --lang en (or gene import <my file>.ged . if I give you a GEDCOM file),
169
+ then git init and a first commit.
170
+ 3. Read AGENTS.md and follow it in all further work: never invent facts or relationships,
171
+ cite a document for every fact, keep doubtful things as leads.
172
+ 4. Ask me about the family (my parents, grandparents, what documents and photos we have at home)
173
+ and fill family_tree.json and site.json from my answers.
174
+ 5. Run gene validate and gene serve ., give me the link and show what you changed.
175
+ ```
176
+
177
+ Then just talk to it: "here is a scan of my grandfather's birth certificate", "my aunt says…".
178
+ More ready-made requests and the rules for reviewing the agent's work:
179
+ [docs/ai-assistants.md](docs/ai-assistants.md).
180
+
181
+ ## Documentation
182
+
183
+ | | English | Русский |
184
+ |---|---|---|
185
+ | How to start the research | [research-guide.md](docs/research-guide.md) | [research-guide.ru.md](docs/research-guide.ru.md) |
186
+ | AI requests for research in open sources and archives | [research-prompts.md](docs/research-prompts.md) | [research-prompts.ru.md](docs/research-prompts.ru.md) |
187
+ | Filling the site with Claude or Codex | [ai-assistants.md](docs/ai-assistants.md) | [ai-assistants.ru.md](docs/ai-assistants.ru.md) |
188
+ | Data format (`family_tree.json`) | [data-format.md](docs/data-format.md) | [data-format.ru.md](docs/data-format.ru.md) |
189
+ | Settings (`site.json`) | [configuration.md](docs/configuration.md) | [configuration.ru.md](docs/configuration.ru.md) |
190
+ | Story, biographies, news | [content.md](docs/content.md) | [content.ru.md](docs/content.ru.md) |
191
+ | Languages and translations | [translations.md](docs/translations.md) | [translations.ru.md](docs/translations.ru.md) |
192
+ | Publishing: GitHub Pages, server, Docker, comments | [deploy.md](docs/deploy.md) | [deploy.ru.md](docs/deploy.ru.md) |
193
+ | Online editor: accounts, history, readers' proposals | [editor.md](docs/editor.md) | [editor.ru.md](docs/editor.ru.md) |
194
+ | Contributing to the engine | [CONTRIBUTING.md](CONTRIBUTING.md) | [CONTRIBUTING.ru.md](CONTRIBUTING.ru.md) |
195
+
196
+ ## Commands
197
+
198
+ | Command | What it does |
199
+ |---|---|
200
+ | `gene init <folder> [--lang ru\|en\|ro] [--example]` | a new site (or a copy of the demo) |
201
+ | `gene import tree.ged <folder> [--root @I12@]` | a new site from GEDCOM |
202
+ | `gene validate [folder]` | errors and warnings in the data |
203
+ | `gene build [folder] [-o out] [--base-url URL]` | build the static site |
204
+ | `gene serve [folder] [--pin 1234] [--moderate]` | build and serve with comments |
205
+ | `gene serve <folder> --editor` | … and the online editor at `/edit/` |
206
+ | `gene users <folder> add NAME [--role admin] \| invite \| list \| role \| disable \| passwd` | editor accounts |
207
+ | `gene comments <folder> list\|approve ID\|hide ID\|done ID "note"` | moderate comments |
208
+ | `gene gedcom [folder] [-o file.ged] [--version 5.5.1\|7.0] [--for geni\|public]` | GEDCOM for Gramps and others; `--for geni` — for uploading to Geni, MyHeritage, Ancestry; `--for public` — for open forums, living people without names (`--full`: with living people, for your own backup) |
209
+ | `gene translations <folder> <lang>` | your texts that have no translation yet |
210
+ | `gene hints [folder] [--json]` | gaps and doubtful facts — a to-do list for the research |
211
+
212
+ Messages are in Russian when the system language is Russian (or `GENE_LANG=ru`).
213
+
214
+ ## License
215
+
216
+ [MIT](LICENSE). Leaflet is BSD-licensed, map tiles © OpenStreetMap contributors — see
217
+ [THIRD_PARTY.md](THIRD_PARTY.md).
@@ -0,0 +1 @@
1
+ __version__ = "0.4.0"
@@ -0,0 +1,92 @@
1
+ """`gene apply`: changes to family_tree.json from scripts and AI assistants, through the same path as the online editor —
2
+ the checks (a change that adds an error is refused), the corrections and research logs, a git commit per change by
3
+ its author and, with --build, a rebuild of the site.
4
+
5
+ echo '{"op": "save", "kind": "people", "record": {"given_names": "Пётр", "surname": "Колодин", "sex": "M"}}' \\
6
+ | gene apply my-family --author "Igor" --build
7
+
8
+ Requests (one JSON object, a list, or one object per line):
9
+ {"op": "save", "kind": K, "record": {...}, "reason": "…", "source_ids": ["S12"]}
10
+ a new record (no id) or an update of the record with this id: the fields given are merged into it
11
+ (nested objects too; a field set to null is removed)
12
+ {"op": "delete", "kind": K, "id": "I12", "reason": "…"}
13
+ {"op": "log", "action": "what was searched and where", "result": "what came out", "scope": "…"}
14
+ K is one of people, families, sources, places, research_leads (person, family, source, place, lead also work).
15
+ Answer: one JSON object per request, {"ok": true, ...} or {"ok": false, "error": "…", "errors": [...]}.
16
+ """
17
+ import copy
18
+ import json
19
+
20
+ from .editor import KINDS, SaveError, Store
21
+
22
+ ALIASES = {"person": "people", "family": "families", "source": "sources", "place": "places", "lead": "research_leads"}
23
+
24
+
25
+ def merge(old, new):
26
+ """Fields of `new` over `old`; nested objects merged; None removes a field."""
27
+ out = copy.deepcopy(old)
28
+ for k, v in new.items():
29
+ if v is None:
30
+ out.pop(k, None)
31
+ elif isinstance(v, dict) and isinstance(out.get(k), dict):
32
+ out[k] = merge(out[k], v)
33
+ else:
34
+ out[k] = v
35
+ return out
36
+
37
+
38
+ def parse(text):
39
+ text = text.strip()
40
+ if not text:
41
+ return []
42
+ try:
43
+ req = json.loads(text)
44
+ return req if isinstance(req, list) else [req]
45
+ except json.JSONDecodeError:
46
+ return [json.loads(line) for line in text.splitlines() if line.strip()]
47
+
48
+
49
+ def run_one(store, user, req, dry_run=False):
50
+ op = req.get("op", "save")
51
+ if op == "log":
52
+ return store.log(user, req.get("action"), req.get("result"), req.get("scope", ""), dry_run=dry_run)
53
+ kind = ALIASES.get(req.get("kind"), req.get("kind"))
54
+ if kind not in KINDS:
55
+ raise SaveError(f"kind must be one of {', '.join(KINDS)}", 400)
56
+ key_field, _ = KINDS[kind]
57
+ current = {r.get(key_field): r for r in store.load().get(kind, [])}
58
+ if op == "delete":
59
+ key = req.get("id") or req.get(key_field)
60
+ if key not in current:
61
+ raise SaveError(f"no {key} in {kind}", 404)
62
+ return store.delete(user, kind, current[key], req.get("reason", ""), dry_run=dry_run)
63
+ if op != "save":
64
+ raise SaveError(f"unknown op {op!r}", 400)
65
+ record = req.get("record")
66
+ if not isinstance(record, dict):
67
+ raise SaveError("record must be an object", 400)
68
+ key = record.get(key_field) or req.get("id")
69
+ if key and key in current: # update: merge into the stored record, which is also the base (no lost edits)
70
+ base = current[key]
71
+ rec = merge(base, record)
72
+ rec[key_field] = key
73
+ return store.save(user, kind, rec, base=base, reason=req.get("reason", ""),
74
+ source_ids=req.get("source_ids"), dry_run=dry_run)
75
+ if key and kind != "places":
76
+ raise SaveError(f"no {key} in {kind} (leave the id out to create a record)", 404)
77
+ return store.save(user, kind, record, reason=req.get("reason", ""), source_ids=req.get("source_ids"), dry_run=dry_run)
78
+
79
+
80
+ def apply(folder, requests, author="gene apply", login="cli", dry_run=False):
81
+ store = Store(folder)
82
+ user = {"name": login, "display": author, "role": "editor"}
83
+ out = []
84
+ for req in requests:
85
+ try:
86
+ r = run_one(store, user, req if isinstance(req, dict) else {}, dry_run)
87
+ out.append({"ok": True, **r})
88
+ except SaveError as e:
89
+ out.append({"ok": False, "error": str(e), "status": e.status, **e.extra})
90
+ except (ValueError, TypeError, KeyError) as e:
91
+ out.append({"ok": False, "error": f"bad request: {e}", "status": 400})
92
+ return out