Add one-shot scripts (plan maison, généalogie, OAuth, etc.)
This commit is contained in:
@@ -0,0 +1,760 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a structured PDF genealogy report from Gramps JSONL export."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from fpdf import FPDF
|
||||
|
||||
# ── Config ──────────────────────────────────────────────────────────────────
|
||||
INPUT = "/root/.hermes/webui/attachments/b3196dc4d94f/gramps-web-export-20260722234742.json"
|
||||
|
||||
# ── Load data ──────────────────────────────────────────────────────────────
|
||||
objects = []
|
||||
with open(INPUT) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line:
|
||||
if "|" in line and line[0].isdigit():
|
||||
line = line.split("|", 1)[1]
|
||||
objects.append(json.loads(line))
|
||||
|
||||
by_handle = {o["handle"]: o for o in objects}
|
||||
|
||||
# ── Event type mapping ───────────────────────────────────────────────────────
|
||||
EVENT_TYPES = {
|
||||
0: "Mariage", 1: "Mariage", 2: "Divorce",
|
||||
3: "Décès", 4: "Inhumation", 5: "Obsèques",
|
||||
6: "Naissance", 7: "Baptême", 8: "Adoption",
|
||||
9: "Baptême (LDS)", 10: "Endowment (LDS)",
|
||||
11: "Scellement (LDS)", 12: "Naissance", 13: "Décès",
|
||||
19: "Inhumation",
|
||||
}
|
||||
|
||||
def event_type_str(t):
|
||||
return EVENT_TYPES.get(t.get("value"), f"Type {t.get('value')}")
|
||||
|
||||
def format_date(d):
|
||||
if not d:
|
||||
return ""
|
||||
dv = d.get("dateval", [0, 0, 0])
|
||||
y, m, day = dv[2], dv[1], dv[0]
|
||||
if y == 0:
|
||||
return ""
|
||||
if m == 0:
|
||||
return str(y)
|
||||
if day == 0:
|
||||
months = ["", "Jan", "Fév", "Mar", "Avr", "Mai", "Juin",
|
||||
"Juil", "Aoû", "Sep", "Oct", "Nov", "Déc"]
|
||||
return f"{months[m]} {y}"
|
||||
return f"{day:02d}/{m:02d}/{y}"
|
||||
|
||||
def person_name(p):
|
||||
if not p:
|
||||
return "Inconnu"
|
||||
n = p.get("primary_name", {})
|
||||
first = n.get("first_name", "").strip()
|
||||
sur_list = n.get("surname_list", [])
|
||||
sur = sur_list[0].get("surname", "").strip() if sur_list else ""
|
||||
suffix = n.get("suffix", "").strip()
|
||||
parts = [first, sur, suffix] if suffix else [first, sur]
|
||||
return " ".join(p for p in parts if p).strip()
|
||||
|
||||
def get_person_events(p):
|
||||
birth, death = "", ""
|
||||
for er in p.get("event_ref_list", []):
|
||||
ev = by_handle.get(er.get("ref"))
|
||||
if ev:
|
||||
t = ev.get("type", {}).get("value")
|
||||
d = format_date(ev.get("date"))
|
||||
if t == 12:
|
||||
birth = d
|
||||
elif t == 13:
|
||||
death = d
|
||||
return birth, death
|
||||
|
||||
def get_events_for_person(p):
|
||||
events = []
|
||||
for er in p.get("event_ref_list", []):
|
||||
ev = by_handle.get(er.get("ref"))
|
||||
if ev:
|
||||
events.append({
|
||||
"type": event_type_str(ev.get("type", {})),
|
||||
"date": format_date(ev.get("date")),
|
||||
"desc": ev.get("description", ""),
|
||||
"place": ev.get("place", ""),
|
||||
})
|
||||
return events
|
||||
|
||||
def get_parents(person):
|
||||
father, mother = None, None
|
||||
for pf in person.get("parent_family_list", []):
|
||||
fam = by_handle.get(pf)
|
||||
if fam:
|
||||
if fam.get("father_handle"):
|
||||
father = by_handle.get(fam["father_handle"])
|
||||
if fam.get("mother_handle"):
|
||||
mother = by_handle.get(fam["mother_handle"])
|
||||
return father, mother
|
||||
|
||||
def get_families(person):
|
||||
return [f for f in families
|
||||
if f.get("father_handle") == person["handle"]
|
||||
or f.get("mother_handle") == person["handle"]]
|
||||
|
||||
def get_children(family):
|
||||
children = []
|
||||
for cr in family.get("child_ref_list", []):
|
||||
child = by_handle.get(cr.get("ref"))
|
||||
if child:
|
||||
children.append(child)
|
||||
return children
|
||||
|
||||
def get_place_name(handle):
|
||||
pl = by_handle.get(handle)
|
||||
if pl:
|
||||
return pl.get("name", {}).get("value", "")
|
||||
return ""
|
||||
|
||||
def get_notes_for_person(p):
|
||||
notes = []
|
||||
for nh in p.get("note_list", []):
|
||||
n = by_handle.get(nh)
|
||||
if n:
|
||||
notes.append(n.get("text", {}).get("string", ""))
|
||||
return notes
|
||||
|
||||
# ── Index data ──────────────────────────────────────────────────────────────
|
||||
persons = [o for o in objects if o.get("_class") == "Person"]
|
||||
families = [o for o in objects if o.get("_class") == "Family"]
|
||||
places = [o for o in objects if o.get("_class") == "Place"]
|
||||
media = [o for o in objects if o.get("_class") == "Media"]
|
||||
notes = [o for o in objects if o.get("_class") == "Note"]
|
||||
|
||||
# ── PDF Generation ───────────────────────────────────────────────────────────
|
||||
|
||||
class GenealogyPDF(FPDF):
|
||||
def header(self):
|
||||
if self.page_no() > 1:
|
||||
self.set_font("Helvetica", "I", 7)
|
||||
self.set_text_color(130, 130, 130)
|
||||
self.cell(0, 5, "Rapport de Genealogie - Familles Keller, Staerle & Alliees", align="C")
|
||||
self.ln(7)
|
||||
|
||||
def footer(self):
|
||||
self.set_y(-15)
|
||||
self.set_font("Helvetica", "I", 7)
|
||||
self.set_text_color(130, 130, 130)
|
||||
self.cell(0, 8, f"Page {self.page_no()}/{{nb}}", align="C")
|
||||
|
||||
def section_title(self, title):
|
||||
self.set_font("Helvetica", "B", 14)
|
||||
self.set_text_color(15, 23, 42)
|
||||
self.cell(0, 8, title)
|
||||
self.ln(4)
|
||||
self.set_draw_color(15, 23, 42)
|
||||
self.set_line_width(0.5)
|
||||
self.line(self.get_x(), self.get_y(), self.get_x() + 190, self.get_y())
|
||||
self.ln(6)
|
||||
|
||||
def sub_title(self, title):
|
||||
self.set_font("Helvetica", "B", 11)
|
||||
self.set_text_color(30, 60, 100)
|
||||
self.cell(0, 7, title)
|
||||
self.ln(5)
|
||||
|
||||
def body_text(self, text):
|
||||
self.set_font("Helvetica", "", 9)
|
||||
self.set_text_color(50, 50, 50)
|
||||
w = self.w - self.get_x() - self.r_margin
|
||||
self.multi_cell(w, 4.5, text)
|
||||
self.ln(1)
|
||||
|
||||
def bullet(self, text):
|
||||
self.set_font("Helvetica", "", 9)
|
||||
self.set_text_color(50, 50, 50)
|
||||
x0 = self.get_x()
|
||||
self.cell(5, 4.5, "-")
|
||||
w = self.w - self.get_x() - self.r_margin
|
||||
self.multi_cell(w, 4.5, text)
|
||||
self.ln(0.5)
|
||||
|
||||
def kv(self, key, value):
|
||||
self.set_font("Helvetica", "B", 9)
|
||||
self.set_text_color(50, 50, 50)
|
||||
self.cell(40, 5, key)
|
||||
self.set_font("Helvetica", "", 9)
|
||||
w = self.w - self.get_x() - self.r_margin
|
||||
self.multi_cell(w, 5, value)
|
||||
self.ln(0.5)
|
||||
|
||||
def person_card(self, p, level=0):
|
||||
name = person_name(p)
|
||||
birth, death = get_person_events(p)
|
||||
gender_map = {0: "F", 1: "M", 2: "?"}
|
||||
gender = gender_map.get(p.get("gender"), "?")
|
||||
|
||||
indent = level * 4
|
||||
self.set_x(self.l_margin + indent)
|
||||
|
||||
self.set_font("Helvetica", "B", 10)
|
||||
self.set_text_color(15, 23, 42)
|
||||
self.cell(0, 5, f"{name} [{gender}] ({p.get('gramps_id', '')})")
|
||||
self.ln(5)
|
||||
|
||||
self.set_x(self.l_margin + indent + 4)
|
||||
self.set_font("Helvetica", "", 9)
|
||||
self.set_text_color(80, 80, 80)
|
||||
dates = []
|
||||
if birth:
|
||||
dates.append(f"Ne(e) le {birth}")
|
||||
if death:
|
||||
dates.append(f"Decede(e) le {death}")
|
||||
if dates:
|
||||
self.cell(0, 4.5, " | ".join(dates))
|
||||
self.ln(4.5)
|
||||
|
||||
events = get_events_for_person(p)
|
||||
for ev in events:
|
||||
if ev["type"] not in ("Naissance", "Décès"):
|
||||
self.set_x(self.l_margin + indent + 4)
|
||||
self.set_font("Helvetica", "", 8)
|
||||
self.set_text_color(100, 100, 100)
|
||||
txt = f"{ev['type']}: {ev['date']}" if ev["date"] else ev["type"]
|
||||
if ev["place"]:
|
||||
place_name = get_place_name(ev["place"])
|
||||
if place_name:
|
||||
txt += f" - {place_name}"
|
||||
if ev["desc"]:
|
||||
txt += f" ({ev['desc']})"
|
||||
self.cell(0, 4, txt)
|
||||
self.ln(4)
|
||||
|
||||
# Notes (professions, etc.)
|
||||
p_notes = get_notes_for_person(p)
|
||||
for n in p_notes:
|
||||
self.set_x(self.l_margin + indent + 4)
|
||||
self.set_font("Helvetica", "I", 8)
|
||||
self.set_text_color(120, 120, 120)
|
||||
self.cell(0, 4, f"[{n.strip()}]")
|
||||
self.ln(4)
|
||||
|
||||
self.ln(2)
|
||||
|
||||
def family_section(self, family, label=""):
|
||||
father = by_handle.get(family.get("father_handle"))
|
||||
mother = by_handle.get(family.get("mother_handle"))
|
||||
children = get_children(family)
|
||||
|
||||
f_name = person_name(father) if father else "?"
|
||||
m_name = person_name(mother) if mother else "?"
|
||||
|
||||
marriage_date = ""
|
||||
marriage_place = ""
|
||||
for er in family.get("event_ref_list", []):
|
||||
ev = by_handle.get(er.get("ref"))
|
||||
if ev:
|
||||
marriage_date = format_date(ev.get("date"))
|
||||
marriage_place = get_place_name(ev.get("place", ""))
|
||||
|
||||
rel_type = family.get("type", {}).get("value", 0)
|
||||
rel_str = "Marie" if rel_type == 0 else "Non marie" if rel_type == 3 else ""
|
||||
|
||||
title = f"{f_name} & {m_name}"
|
||||
if label:
|
||||
title = f"{label}: {title}"
|
||||
self.sub_title(title)
|
||||
|
||||
if marriage_date:
|
||||
txt = marriage_date
|
||||
if marriage_place:
|
||||
txt += f" - {marriage_place}"
|
||||
self.kv("Mariage:", txt)
|
||||
if rel_str:
|
||||
self.kv("Statut:", rel_str)
|
||||
|
||||
if children:
|
||||
self.ln(1)
|
||||
self.set_font("Helvetica", "I", 9)
|
||||
self.set_text_color(80, 80, 80)
|
||||
self.cell(0, 5, "Enfants:")
|
||||
self.ln(5)
|
||||
for c in children:
|
||||
c_name = person_name(c)
|
||||
c_birth, c_death = get_person_events(c)
|
||||
c_dates = []
|
||||
if c_birth:
|
||||
c_dates.append(f"n. {c_birth}")
|
||||
if c_death:
|
||||
c_dates.append(f"d. {c_death}")
|
||||
c_info = f" {c_name}"
|
||||
if c_dates:
|
||||
c_info += f" ({', '.join(c_dates)})"
|
||||
self.bullet(c_info)
|
||||
self.ln(3)
|
||||
|
||||
|
||||
# ── Build the report ─────────────────────────────────────────────────────────
|
||||
|
||||
pdf = GenealogyPDF()
|
||||
pdf.alias_nb_pages()
|
||||
pdf.set_auto_page_break(auto=True, margin=20)
|
||||
pdf.add_page()
|
||||
|
||||
# ── Cover ────────────────────────────────────────────────────────────────────
|
||||
pdf.ln(30)
|
||||
pdf.set_font("Helvetica", "B", 24)
|
||||
pdf.set_text_color(15, 23, 42)
|
||||
pdf.cell(0, 12, "Rapport de Genealogie", align="C")
|
||||
pdf.ln(10)
|
||||
pdf.set_font("Helvetica", "", 14)
|
||||
pdf.set_text_color(60, 60, 60)
|
||||
pdf.cell(0, 8, "Familles Keller, Staerle & Alliees", align="C")
|
||||
pdf.ln(8)
|
||||
pdf.set_font("Helvetica", "", 10)
|
||||
pdf.cell(0, 6, "Suisse (Schwytz) - Alsace (France) - Moselle", align="C")
|
||||
pdf.ln(6)
|
||||
pdf.cell(0, 6, f"Genere le {datetime.now().strftime('%d/%m/%Y a %H:%M')}", align="C")
|
||||
pdf.ln(20)
|
||||
|
||||
# Stats box
|
||||
pdf.set_fill_color(240, 244, 248)
|
||||
pdf.set_draw_color(200, 210, 220)
|
||||
pdf.rect(30, pdf.get_y(), 150, 35, "DF")
|
||||
pdf.set_xy(35, pdf.get_y() + 3)
|
||||
pdf.set_font("Helvetica", "B", 10)
|
||||
pdf.set_text_color(15, 23, 42)
|
||||
pdf.cell(0, 5, "Resume")
|
||||
pdf.ln(6)
|
||||
pdf.set_x(35)
|
||||
pdf.set_font("Helvetica", "", 9)
|
||||
pdf.set_text_color(50, 50, 50)
|
||||
pdf.cell(0, 4.5, f"Personnes: {len(persons)} | Familles: {len(families)} | Lieux: {len(places)}")
|
||||
pdf.ln(4.5)
|
||||
pdf.set_x(35)
|
||||
pdf.cell(0, 4.5, f"Evenements: {len([o for o in objects if o.get('_class') == 'Event'])} | Medias: {len(media)}")
|
||||
pdf.ln(4.5)
|
||||
pdf.set_x(35)
|
||||
pdf.cell(0, 4.5, f"Generations: 7 (de 1760 a nos jours)")
|
||||
pdf.ln(4.5)
|
||||
pdf.set_x(35)
|
||||
pdf.cell(0, 4.5, f"Origines: Altendorf (SZ), Alsace, Moselle, Vaud")
|
||||
pdf.ln(15)
|
||||
|
||||
# ── Table of Contents ───────────────────────────────────────────────────────
|
||||
pdf.add_page()
|
||||
pdf.section_title("Table des matieres")
|
||||
toc = [
|
||||
"1. Arbre genealogique - Famille Keller",
|
||||
"2. Arbre genealogique - Famille Staerle (branche alsacienne)",
|
||||
"3. Branches alliees (Lemann, Kutzner, Hamm, Gangloff)",
|
||||
"4. Fiches individuelles detaillees",
|
||||
"5. Lieux",
|
||||
"6. Medias et documents",
|
||||
"7. Notes et sources",
|
||||
"8. Statistiques",
|
||||
]
|
||||
for item in toc:
|
||||
pdf.bullet(item)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 1. KELLER FAMILY TREE
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
pdf.add_page()
|
||||
pdf.section_title("1. Arbre genealogique - Famille Keller")
|
||||
|
||||
# Gen 1: Jakob Keller
|
||||
pdf.sub_title("Generation 1 (ancetre)")
|
||||
jakob = by_handle.get("a37f8646-d374-40da-863b-86febf23b165")
|
||||
if jakob:
|
||||
pdf.person_card(jakob)
|
||||
|
||||
# Family F0006: Jakob -> Silvio
|
||||
fam_f6 = by_handle.get("103b3ad8ce8c43637db4d46f804c")
|
||||
if fam_f6:
|
||||
pdf.family_section(fam_f6, "Famille")
|
||||
|
||||
# Gen 2: Silvio + Margrit
|
||||
pdf.sub_title("Generation 2")
|
||||
silvio = by_handle.get("d1220b0f-9a04-4581-994a-6c4985f14c83")
|
||||
margrit = by_handle.get("bad07459-e2d9-4413-85bd-5b8b6e8463d5")
|
||||
if silvio:
|
||||
pdf.person_card(silvio)
|
||||
if margrit:
|
||||
pdf.person_card(margrit)
|
||||
|
||||
fam_f1 = by_handle.get("103b34dfb7d317e6eacf93b9b322")
|
||||
if fam_f1:
|
||||
pdf.family_section(fam_f1, "Famille")
|
||||
|
||||
# Gen 3: Lucien + Sylvie
|
||||
pdf.sub_title("Generation 3")
|
||||
lucien = by_handle.get("4c572a11-13cd-46ee-9aee-39b904e86dcd")
|
||||
sylvie = by_handle.get("14d0669e-3174-4a94-b5e5-23af79331f20")
|
||||
if lucien:
|
||||
pdf.person_card(lucien)
|
||||
if sylvie:
|
||||
pdf.person_card(sylvie)
|
||||
|
||||
fam_f0 = by_handle.get("103b34b3fc3a78115abdc520a0d7")
|
||||
if fam_f0:
|
||||
pdf.family_section(fam_f0, "Famille")
|
||||
|
||||
# Gen 4: Laurent + Lucie
|
||||
pdf.sub_title("Generation 4")
|
||||
laurent = by_handle.get("7c2b4663-ba21-49ce-af58-b268babe3e25")
|
||||
if laurent:
|
||||
pdf.person_card(laurent)
|
||||
lucie = by_handle.get("7cb9d60e-8a61-47d5-a4bf-69d1a23a7068")
|
||||
if lucie:
|
||||
pdf.person_card(lucie)
|
||||
|
||||
# Gen 3 siblings: Tobias, Lea, Livia
|
||||
pdf.sub_title("Generation 3 - Freres et soeurs de Lucien")
|
||||
tobias = by_handle.get("1a501a5f-2721-491c-9bfe-7d3d1be88bdc")
|
||||
lea = by_handle.get("cfbe8ee0-0c31-4f92-ada8-a9054514df0c")
|
||||
livia = by_handle.get("b7c9c8c6-471d-481d-99c4-eb1753a0fb31")
|
||||
for p in [tobias, lea, livia]:
|
||||
if p:
|
||||
pdf.person_card(p)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 2. STAERLE FAMILY TREE (Alsace branch)
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
pdf.add_page()
|
||||
pdf.section_title("2. Arbre genealogique - Famille Staerle (Alsace)")
|
||||
|
||||
# Gen 1 (1760): Johann Michael Staerle + Catharina Bernhard
|
||||
pdf.sub_title("Generation 1 (v. 1760)")
|
||||
jm1 = by_handle.get("1c80dd1f-77b4-4ab9-940c-e0776b6161ff")
|
||||
cmb = by_handle.get("cc5dad4f-1963-4037-b7db-040680011dd9")
|
||||
if jm1:
|
||||
pdf.person_card(jm1)
|
||||
if cmb:
|
||||
pdf.person_card(cmb)
|
||||
|
||||
fam_f12 = by_handle.get("103b753f4b6772d2f9cd968bc788")
|
||||
if fam_f12:
|
||||
pdf.family_section(fam_f12, "Famille")
|
||||
|
||||
# Gen 2: Johann Michael Staerle + Christiana Metz
|
||||
pdf.sub_title("Generation 2")
|
||||
jm2 = by_handle.get("32c7f3f4-aa1c-4501-aa67-0d93ebcfbf68")
|
||||
cm = by_handle.get("01f5daba-73b0-4ae9-87cb-80afc755aca7")
|
||||
if jm2:
|
||||
pdf.person_card(jm2)
|
||||
if cm:
|
||||
pdf.person_card(cm)
|
||||
|
||||
fam_f11 = by_handle.get("103b74f88457186cf98a9b6eca8a")
|
||||
if fam_f11:
|
||||
pdf.family_section(fam_f11, "Famille")
|
||||
|
||||
# Gen 3: Jean Adam Staerle + Madeleine Helf
|
||||
pdf.sub_title("Generation 3")
|
||||
ja = by_handle.get("30aeb3bd-dafd-4a4f-baf3-d75c1fd0d535")
|
||||
mh = by_handle.get("ab0e498f-3e4f-41de-8766-0941d58614b9")
|
||||
if ja:
|
||||
pdf.person_card(ja)
|
||||
if mh:
|
||||
pdf.person_card(mh)
|
||||
|
||||
fam_f10 = by_handle.get("103b744d30a81442468b19dce1e6")
|
||||
if fam_f10:
|
||||
pdf.family_section(fam_f10, "Famille")
|
||||
|
||||
# Gen 4: Michel Staerle + Sophie Vogel
|
||||
pdf.sub_title("Generation 4")
|
||||
michel = by_handle.get("8d3313ce-d89c-473e-a7aa-4385d5bea769")
|
||||
sophie = by_handle.get("2c548840-a9ef-4f2c-9195-7c9007b963a2")
|
||||
if michel:
|
||||
pdf.person_card(michel)
|
||||
if sophie:
|
||||
pdf.person_card(sophie)
|
||||
|
||||
fam_f9 = by_handle.get("103b730b5d1b5e3c15d9fa4d5111")
|
||||
if fam_f9:
|
||||
pdf.family_section(fam_f9, "Famille")
|
||||
|
||||
# Gen 5: Guillaume Staerle + Frida Bender
|
||||
pdf.sub_title("Generation 5")
|
||||
guillaume1 = by_handle.get("3c98357d-3645-4a74-ae8d-3fb91b005fe8")
|
||||
frida = by_handle.get("ce24fa7a-b67d-47ba-ad46-3fc940be0c25")
|
||||
if guillaume1:
|
||||
pdf.person_card(guillaume1)
|
||||
if frida:
|
||||
pdf.person_card(frida)
|
||||
|
||||
fam_f3 = by_handle.get("103b39893eb03c1e4ed40f6f8d9b")
|
||||
if fam_f3:
|
||||
pdf.family_section(fam_f3, "Famille")
|
||||
|
||||
# Gen 6: Guillaume Staerle + Catherine Jund
|
||||
pdf.sub_title("Generation 6")
|
||||
guillaume2 = by_handle.get("bce9436e-8a1c-4eb1-b4c6-bfdcc7c6a935")
|
||||
catherine = by_handle.get("88c262e3-9d6f-4f29-a368-03732e5d4b7b")
|
||||
if guillaume2:
|
||||
pdf.person_card(guillaume2)
|
||||
if catherine:
|
||||
pdf.person_card(catherine)
|
||||
|
||||
fam_f2 = by_handle.get("103b35aa6e2918ae0dc0317e052b")
|
||||
if fam_f2:
|
||||
pdf.family_section(fam_f2, "Famille")
|
||||
|
||||
# Gen 7: Sylvie, Annette, Corinne, Isabelle, Anne
|
||||
pdf.sub_title("Generation 7 - Enfants Staerle")
|
||||
staerle_children = [
|
||||
"14d0669e-3174-4a94-b5e5-23af79331f20", # Sylvie
|
||||
"e30f96b7-2860-4473-acd5-f5d9b5ae4f36", # Annette
|
||||
"ccf375b4-c0ca-4bba-87e6-365c6b348bb5", # Corinne
|
||||
"9428a2d4-6330-4a1c-835e-f92eacefb805", # Isabelle
|
||||
"f6d9662e-02a4-4327-99b8-9b48a30f8bc9", # Anne
|
||||
]
|
||||
for h in staerle_children:
|
||||
p = by_handle.get(h)
|
||||
if p:
|
||||
pdf.person_card(p)
|
||||
|
||||
# Christian Staerle + Yvette
|
||||
pdf.sub_title("Generation 7 - Christian Staerle & Yvette")
|
||||
christian = by_handle.get("33c893ff-60b3-4420-931a-bb3f28ce0562")
|
||||
yvette = by_handle.get("66c36421-4e32-42e9-bc57-c47f6e6adfe4")
|
||||
if christian:
|
||||
pdf.person_card(christian)
|
||||
if yvette:
|
||||
pdf.person_card(yvette)
|
||||
|
||||
fam_f5 = by_handle.get("103b3ac22c3c4c35c31f77a70910")
|
||||
if fam_f5:
|
||||
pdf.family_section(fam_f5, "Famille")
|
||||
|
||||
# Gen 8: Chris, Jean-michel Staerle
|
||||
pdf.sub_title("Generation 8 - Enfants Christian & Yvette")
|
||||
chris = by_handle.get("4e908cd1-83e6-4aa5-a004-4d510144a528")
|
||||
jm_staerle = by_handle.get("ed8e0c2d-49e2-43a7-9c3f-18bd72afbdc7")
|
||||
for p in [chris, jm_staerle]:
|
||||
if p:
|
||||
pdf.person_card(p)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 3. ALLIED BRANCHES
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
pdf.add_page()
|
||||
pdf.section_title("3. Branches alliees")
|
||||
|
||||
# Annette Hamm + Daniel Gangloff
|
||||
pdf.sub_title("Famille Hamm-Gangloff")
|
||||
annette = by_handle.get("e30f96b7-2860-4473-acd5-f5d9b5ae4f36")
|
||||
daniel = by_handle.get("f6a18cb6-d08c-44b1-b52f-53de36d24107")
|
||||
if annette:
|
||||
pdf.person_card(annette)
|
||||
if daniel:
|
||||
pdf.person_card(daniel)
|
||||
|
||||
fam_f4 = by_handle.get("103b3aa996d7492b0a90e358032a")
|
||||
if fam_f4:
|
||||
pdf.family_section(fam_f4, "Famille")
|
||||
|
||||
# Lea Keller + Sandro Lemann
|
||||
pdf.sub_title("Famille Lemann")
|
||||
lea = by_handle.get("cfbe8ee0-0c31-4f92-ada8-a9054514df0c")
|
||||
sandro = by_handle.get("ef9b14d5-ab40-48fd-a110-eb4aac2a6af2")
|
||||
if lea:
|
||||
pdf.person_card(lea)
|
||||
if sandro:
|
||||
pdf.person_card(sandro)
|
||||
|
||||
fam_f7 = by_handle.get("103b3b4a63723ca20a2d8c369711")
|
||||
if fam_f7:
|
||||
pdf.family_section(fam_f7, "Famille")
|
||||
|
||||
# Livia Keller + Florian Kutzner
|
||||
pdf.sub_title("Famille Kutzner")
|
||||
livia = by_handle.get("b7c9c8c6-471d-481d-99c4-eb1753a0fb31")
|
||||
florian = by_handle.get("b4984dbd-2f15-4a41-9e6f-900d0f67e507")
|
||||
if livia:
|
||||
pdf.person_card(livia)
|
||||
if florian:
|
||||
pdf.person_card(florian)
|
||||
|
||||
fam_f8 = by_handle.get("103b3b5df8a1443d8513129fe995")
|
||||
if fam_f8:
|
||||
pdf.family_section(fam_f8, "Famille")
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 4. DETAILED PERSON CARDS
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
pdf.add_page()
|
||||
pdf.section_title("4. Fiches individuelles detaillees")
|
||||
|
||||
all_persons_sorted = sorted(persons, key=lambda p: p.get("gramps_id", ""))
|
||||
for p in all_persons_sorted:
|
||||
name = person_name(p)
|
||||
pid = p.get("gramps_id", "")
|
||||
birth, death = get_person_events(p)
|
||||
gender_map = {0: "Femme", 1: "Homme", 2: "Autre"}
|
||||
gender = gender_map.get(p.get("gender"), "?")
|
||||
|
||||
if pdf.get_y() > 230:
|
||||
pdf.add_page()
|
||||
|
||||
pdf.sub_title(f"{name} ({pid})")
|
||||
pdf.kv("Nom:", name)
|
||||
pdf.kv("Genre:", gender)
|
||||
if birth:
|
||||
pdf.kv("Naissance:", birth)
|
||||
if death:
|
||||
pdf.kv("Deces:", death)
|
||||
|
||||
father, mother = get_parents(p)
|
||||
if father or mother:
|
||||
parents_str = []
|
||||
if father:
|
||||
parents_str.append(person_name(father))
|
||||
if mother:
|
||||
parents_str.append(person_name(mother))
|
||||
pdf.kv("Parents:", " & ".join(parents_str))
|
||||
|
||||
own_fams = get_families(p)
|
||||
if own_fams:
|
||||
for f in own_fams:
|
||||
spouse_handle = f.get("father_handle") if f.get("mother_handle") == p["handle"] else f.get("mother_handle")
|
||||
spouse = by_handle.get(spouse_handle) if spouse_handle else None
|
||||
children = get_children(f)
|
||||
if spouse:
|
||||
pdf.kv("Conjoint(e):", person_name(spouse))
|
||||
if children:
|
||||
pdf.kv("Enfants:", ", ".join(person_name(c) for c in children))
|
||||
|
||||
events = get_events_for_person(p)
|
||||
for ev in events:
|
||||
txt = ev["type"]
|
||||
if ev["date"]:
|
||||
txt += f" le {ev['date']}"
|
||||
if ev["place"]:
|
||||
place_name = get_place_name(ev["place"])
|
||||
if place_name:
|
||||
txt += f" - {place_name}"
|
||||
if ev["desc"]:
|
||||
txt += f" ({ev['desc']})"
|
||||
pdf.kv(ev["type"] + ":", txt if txt != ev["type"] else "-")
|
||||
|
||||
p_notes = get_notes_for_person(p)
|
||||
for n in p_notes:
|
||||
pdf.kv("Note:", n.strip())
|
||||
|
||||
pdf.ln(3)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 5. PLACES
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
pdf.add_page()
|
||||
pdf.section_title("5. Lieux")
|
||||
for pl in places:
|
||||
name = pl.get("name", {}).get("value", "")
|
||||
lat = pl.get("lat", "")
|
||||
lon = pl.get("long", "")
|
||||
ptype = pl.get("place_type", {}).get("string", "")
|
||||
pid = pl.get("gramps_id", "")
|
||||
|
||||
if pdf.get_y() > 250:
|
||||
pdf.add_page()
|
||||
|
||||
pdf.sub_title(f"{name} ({pid})")
|
||||
if ptype:
|
||||
pdf.kv("Type:", ptype)
|
||||
if lat and lon:
|
||||
pdf.kv("Coordonnees:", f"{lat}, {lon}")
|
||||
|
||||
for pr in pl.get("placeref_list", []):
|
||||
parent = by_handle.get(pr.get("ref"))
|
||||
if parent:
|
||||
pname = parent.get("name", {}).get("value", "")
|
||||
pdf.kv("Fait partie de:", pname)
|
||||
pdf.ln(2)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 6. MEDIA
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
pdf.add_page()
|
||||
pdf.section_title("6. Medias et documents")
|
||||
for m in media:
|
||||
desc = m.get("desc", "")
|
||||
mime = m.get("mime", "")
|
||||
path = m.get("path", "")
|
||||
mid = m.get("gramps_id", "")
|
||||
date_str = format_date(m.get("date", {}))
|
||||
|
||||
pdf.sub_title(f"{desc} ({mid})")
|
||||
pdf.kv("Type:", mime)
|
||||
pdf.kv("Fichier:", path)
|
||||
if date_str:
|
||||
pdf.kv("Date:", date_str)
|
||||
pdf.ln(2)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 7. NOTES
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
pdf.add_page()
|
||||
pdf.section_title("7. Notes et sources")
|
||||
for n in notes:
|
||||
text = n.get("text", {}).get("string", "")
|
||||
ntype = n.get("type", {}).get("string", "")
|
||||
nid = n.get("gramps_id", "")
|
||||
|
||||
if pdf.get_y() > 260:
|
||||
pdf.add_page()
|
||||
|
||||
pdf.sub_title(f"Note {nid}")
|
||||
if ntype:
|
||||
pdf.kv("Type:", ntype)
|
||||
pdf.body_text(text.strip())
|
||||
pdf.ln(2)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# 8. STATISTICS
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
pdf.add_page()
|
||||
pdf.section_title("8. Statistiques")
|
||||
|
||||
males = sum(1 for p in persons if p.get("gender") == 1)
|
||||
females = sum(1 for p in persons if p.get("gender") == 0)
|
||||
other = sum(1 for p in persons if p.get("gender") == 2)
|
||||
|
||||
pdf.sub_title("Repartition par genre")
|
||||
pdf.kv("Hommes:", str(males))
|
||||
pdf.kv("Femmes:", str(females))
|
||||
pdf.kv("Autre/Inconnu:", str(other))
|
||||
pdf.ln(3)
|
||||
|
||||
married = sum(1 for f in families if f.get("type", {}).get("value") == 0)
|
||||
unmarried = sum(1 for f in families if f.get("type", {}).get("value") == 3)
|
||||
pdf.sub_title("Familles")
|
||||
pdf.kv("Mariees:", str(married))
|
||||
pdf.kv("Non mariees:", str(unmarried))
|
||||
pdf.ln(3)
|
||||
|
||||
pdf.sub_title("Evenements")
|
||||
event_counts = defaultdict(int)
|
||||
for ev in [o for o in objects if o.get("_class") == "Event"]:
|
||||
et = event_type_str(ev.get("type", {}))
|
||||
event_counts[et] += 1
|
||||
for et, count in sorted(event_counts.items(), key=lambda x: -x[1]):
|
||||
pdf.kv(et + ":", str(count))
|
||||
pdf.ln(3)
|
||||
|
||||
pdf.sub_title("Lieux")
|
||||
pdf.kv("Total lieux:", str(len(places)))
|
||||
for pl in places:
|
||||
name = pl.get("name", {}).get("value", "")
|
||||
ptype = pl.get("place_type", {}).get("string", "")
|
||||
pdf.bullet(f"{name} ({ptype})" if ptype else name)
|
||||
|
||||
# ── Save ────────────────────────────────────────────────────────────────────
|
||||
OUTPUT = "/root/workspace/rapport-genealogie-keller-staerle.pdf"
|
||||
pdf.output(OUTPUT)
|
||||
print(f"PDF genere: {OUTPUT}")
|
||||
print(f"Pages: {pdf.page_no()}")
|
||||
print(f"Personnes: {len(persons)}, Familles: {len(families)}, Lieux: {len(places)}, Evenements: {len([o for o in objects if o.get('_class') == 'Event'])}")
|
||||
Reference in New Issue
Block a user