Convertir une base Heredis vers Gramps
Introduction
Je voulais convertir ma base Heredis 2020 vers Gramps. Voici mes notes sur ce chantier.
Gramps est un logiciel de généalogie libre et open source.
Avertissement : cette page est en cours de rédaction.
Avant de convertir
J'ai d'abord essayé d'exporter en GEDCOM, option disponible dans Heredis. Résultat : le fichier n'était pas compatible avec Gramps (données perdues, balises inconnues…).
J'ai trouvé une discussion sur un forum de généalogie français où quelqu'un avait réussi, sans partager son code. Il explique que la base Heredis est une base SQLite.
Alors : écrivons le code !
Code
Introduction
J'utilise Python car, mis à part du scripting rapide, je n'ai jamais fait de projet complet dans ce langage.
J'essayerai de poster le code sur Github, mais pour l'instant il contient trop de données perso...
Lire la base SQLite
#!/usr/bin/python3
import sys, getopt, sqlite3
con = sqlite3.connect('heredis.db')
cur = con.cursor()
for row in cur.execute("SELECT * FROM table"):
print(row)
Schéma de la base Heredis
J'ai utilisé dbdiagram.io pour visualiser le schéma.

Architecture de la solution
Après avoir analysé le schéma SQLite d'Heredis, j'ai structuré le projet en modules Python pour séparer la lecture de la base, la transformation des données et la communication avec Gramps.
Structure du projet
heredis-to-gramp/
├── heredis/ # Accès et utilitaires Heredis
├── gramps/ # Modèles et client Gramps Web
├── utils/ # Outils (géocodage, mapping, affichage)
├── commands.py
├── HeredisToGramps.py
└── main.py
Connexion à la base Heredis
La première étape reste l'extraction du fichier SQLite depuis l'archive .hmw :
cp mon-fichier.hmw/mon-fichier.heredis ./heredis.db
(faites des sauvegardes !)
J'utilise une classe singleton HeredisDb pour réutiliser une seule connexion SQLite et éviter les ouvertures/fermetures répétées.
Comprendre le schéma
Le schéma tourne autour de la table centrale Individus. Les autres tables clés sont : Evts, LienIndividuEvenement, Lieux, Sources, Medias, Unions.
Certaines spécificités d'Heredis demandent des adaptations : prénoms multiples, professions hiérarchiques, témoins d'événements, numérotation Sosa, etc.
Mapping vers Gramps
Le mapping documente la correspondance entre types d'événements, genres, lieux et les objets Gramps. Un fichier de mapping (heredis_gramps_mapping.txt) centralise ces correspondances.
Intégration avec Gramps Web
Pour l'import, j'utilise l'API REST de Gramps Web plutôt que l'interface CLI. Un service Docker Compose facilite le déploiement local de Gramps Web.
Au début, j'avais envisagé d'utiliser la CLI de Gramps, mais l'API REST s'est avérée plus flexible et avec une courbe d'apprentissage plus rapide.
Il faudrait certainement réécrire une partie du code pour utiliser la CLI, mais pour l'instant je me concentre sur l'API REST (et c'est du one-shot!).
Import par lots vs. import unitaire
J'ai expérimenté un import par lots via l'endpoint /objects pour améliorer les performances, mais l'approche s'est révélée instable dans certains cas. En production la méthode la plus fiable est l'import unitaire, même si plus lent.
Limitations et améliorations futures
- Témoins d'événements non transférés
- Détection et fusion des doublons
- Export incrémental
- Interface web de suivi
- Rapport détaillé des éléments non convertis
Cette approche réutilise la même connexion pour toutes les requêtes, évitant ainsi le coût d'ouvertures/fermetures répétées.
Comprendre le schéma Heredis
Le schéma d'Heredis est organisé autour de la table centrale Individus. Voici les tables principales :
Principales tables
- Individus : personnes (noms, prénoms, genre, etc.)
- Evenements : naissances, décès, mariages, etc.
- LienIndividuEvenement : liaison personnes ↔ événements
- Lieux : emplacements géographiques
- Sources : sources documentaires
- Medias : fichiers médias (photos, documents, etc.)
- IndividusUnions : mariages / unions
Spécificités d'Heredis
Quelques particularités à prendre en compte :
- Prénoms multiples : table
Prenomsavec notion de "prénom usuel" - Professions multiples : table
Professionsavec hiérarchie - Témoins d'événements : gérés via
LienIndividuEvenement - Qualité des sources : plusieurs niveaux (Source, Information, Preuve)
- Numérotation Sosa : table dédiée à la numérotation d'Aboville
Différences structurelles
Certaines notions n'ont pas d'équivalent direct dans Gramps :
- professions multiples → transformées en attributs multiples
- prénoms multiples → stockés dans
name.first_name - témoins d'événements → non pris en charge nativement (peuvent devenir des notes)
- subdivisions géographiques → converties en
Placeavec coordonnées
Création d'objets via l'API
Le projet contient des modèles pour représenter les objets Gramps côté client. Exemple simplifié pour créer une personne :
class GrampsPeople(GrampsObject):
def __init__(self, prenoms: str = "", nom: str = ""):
self.name = f"{prenoms} {nom}".strip()
self.primary_name = GrampsName()
self.primary_name.first_name = prenoms
self.primary_name.surname_list = [GrampsSurname(nom, primary=True)]
self.primary_name.type = "Birth Name"
self.gender = 2 # Inconnu par défaut
def set_gender(self, heredis_gender: int):
# Heredis : 109 = Masculin, 102 = Féminin
self.gender = 1 if heredis_gender == 109 else 0 if heredis_gender == 102 else 2
def to_dict(self):
return {
"primary_name": self.primary_name.to_dict(),
"gender": self.gender,
}
Utilisation du script
Le script principal propose plusieurs modes d'utilisation :
Statistiques de la base Heredis
Parce que c'est toujours intéressant de connaître la taille de sa base avant de lancer un import !
python3 main.py --stats
# Exemple de sortie
Nombre d'individus : 1245
Nombre d'événements : 3892
Rechercher un individu
Le programme permet de rechercher des individus par nom et d'afficher leurs dates et relations :
python3 main.py --search "Dupont" --show-dates --show-parents
Exemple d'affichage :
👤 Jean DUPONT (ID : 42)
Sexe : Masculin
📅 Naissance : 1890-03-15 à Paris
📅 Décès : 1965-07-22 à Lyon
👨👩👧 Parents :
- Pierre DUPONT (ID : 21)
- Marie MARTIN (ID : 22)
👤 Sophie DUPONT (ID : 156)
Sexe : Féminin
📅 Naissance : 1920-05-10 à Marseille
On peut aussi interroger un individu par son identifiant :
python3 main.py --individual --id 42 --show-dates --show-parents
Format d'affichage (exemple) :
42 DUPONT Jean (1890-03-15 - 1965-07-22) [M]
Opérations d'import
Le programme supporte l'import individuel et l'import en lot (expérimental) :
# Importer un individu
python3 main.py --import-individual --id 42 -y
# Importer avec parents et enfants
python3 main.py --import-individual --id 42 --import-parents --import-children -y
# Importer tous les individus (avec limite)
python3 main.py --import-all --limit 100 -y
L'option -y confirme automatiquement les opérations sans demander de saisie.
Géocodage des lieux
Gramps peut stocker des coordonnées GPS pour les lieux. J'ai ajouté un géocodage automatique :
import requests
from functools import lru_cache
@lru_cache(maxsize=1000)
def geocode_place(place_name):
"""Géocode un lieu et met le résultat en cache."""
response = requests.get(
"https://nominatim.openstreetmap.org/search",
params={
"q": place_name,
"format": "json",
"limit": 1
}
)
if response.json():
result = response.json()[0]
return {
"lat": result["lat"],
"lon": result["lon"]
}
return None
Un cache mémoire permet d'éviter les requêtes répétées pour les mêmes lieux, améliorant ainsi les performances (et en limitant les appels à l'API de Nominatim).
Gestion des médias
Les médias dans Heredis sont référencés par des chemins absolus : il faut copier les fichiers et adapter les chemins pour Gramps.
def get_media_for_individual(self, individual_id):
"""
Récupère tous les médias liés à un individu.
Retourne une liste de dictionnaires contenant :
- media_id (int)
- file_path (str)
- file_name (str)
- media_principal (bool)
"""
query = """
SELECT
m.CodeID as media_id,
m.FilePath as file_path,
m.FileName as file_name,
lmi.MediaPrincipal as media_principal
FROM Medias m
JOIN LiensMediaIndividu lmi ON m.CodeID = lmi.XrefMedia
WHERE lmi.XrefIndividu = ?
ORDER BY lmi.Ordre
"""
# Exécuter la requête et retourner les résultats
Import par lots — expérimentations
J'ai initialement développé un import par lots via l'endpoint /objects de l'API Gramps pour améliorer les performances (réduire les 4–5 appels API par personne à 1–2 appels pour plusieurs personnes).
Cependant, cette approche par lots a été abandonnée à cause d'instabilités : échecs de transaction et comportements incohérents avec certains jeux de données.
La méthode stable actuelle importe les individus un par un — plus fiable, mais plus lent.
Limitations actuelles et améliorations futures
Limitations actuelles
- Témoins d'événements : non transférés (pas d'équivalent natif dans Gramps)
- Hiérarchie des professions : aplatie en liste simple
- Recadrage des photos : informations perdues
- Tâches Heredis : non exportées
- Vitesse d'import : l'import unitaire est plus lent mais plus stable
Améliorations envisagées
- Exporter les témoins comme notes textuelles
- Détection et fusion des doublons
- Export incrémental
- Interface web de suivi de l'import
- Validation des données avant import
- Rapport détaillé des éléments non convertis
- Implémentation stable d'un import par lots
Conclusion
Ce projet m'a permis de :
- Comprendre en profondeur les deux modèles (Heredis et Gramps)
- Progresser en Python (singletons, décorateurs, optimisation SQLite)
- Rédiger une documentation utile
- Construire un convertisseur fonctionnel malgré les différences structurelles
Le code est disponible sur GitHub (lien à ajouter).
Si vous avez des questions ou des suggestions d'amélioration, ouvrez une issue !
Ressources utiles
- Documentation Gramps Web API
- Gramps.js (Gramps Web)
- DBDiagram.io — pour visualiser les schémas SQL
- Documentation SQLite
- Forum Geneanet