<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Mes notes</title>
      <link>https://notes.liorzou.bzh</link>
      <description>Notes personnelles sur le dev, le vélo, et d&#x27;autres choses.</description>
      <generator>Zola</generator>
      <language>fr</language>
      <atom:link href="https://notes.liorzou.bzh/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Wed, 18 Oct 2023 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Convertir une base Heredis vers Gramps</title>
          <pubDate>Wed, 18 Oct 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://notes.liorzou.bzh/dev/convert-heredis-to-gramps/</link>
          <guid>https://notes.liorzou.bzh/dev/convert-heredis-to-gramps/</guid>
          <description xml:base="https://notes.liorzou.bzh/dev/convert-heredis-to-gramps/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;Je voulais convertir ma base Heredis 2020 vers Gramps.
Voici mes notes sur ce chantier.&lt;&#x2F;p&gt;
&lt;p&gt;Gramps est un logiciel de généalogie libre et open source.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Avertissement : cette page est en cours de rédaction.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;avant-de-convertir&quot;&gt;Avant de convertir&lt;&#x2F;h2&gt;
&lt;p&gt;J&#x27;ai d&#x27;abord essayé d&#x27;exporter en &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fr.wikipedia.org&#x2F;wiki&#x2F;GEDCOM&quot;&gt;GEDCOM&lt;&#x2F;a&gt;, option disponible dans Heredis.
Résultat : le fichier n&#x27;était pas compatible avec Gramps (données perdues, balises inconnues…).&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;ai trouvé &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.geneanet.org&#x2F;forum&#x2F;viewtopic.php?t=649865&quot;&gt;une discussion&lt;&#x2F;a&gt; sur un forum de généalogie français où quelqu&#x27;un avait réussi, sans partager son code.
Il explique que la base Heredis est une base SQLite.&lt;&#x2F;p&gt;
&lt;p&gt;Alors : écrivons le code !&lt;&#x2F;p&gt;
&lt;h2 id=&quot;code&quot;&gt;Code&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;introduction-1&quot;&gt;Introduction&lt;&#x2F;h3&gt;
&lt;p&gt;J&#x27;utilise Python car, mis à part du scripting rapide, je n&#x27;ai jamais fait de projet complet dans ce langage.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;J&#x27;essayerai de poster le code sur Github, mais pour l&#x27;instant il contient trop de données perso...&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;lire-la-base-sqlite&quot;&gt;Lire la base SQLite&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;#!&#x2F;usr&#x2F;bin&#x2F;python3
import sys, getopt, sqlite3

con = sqlite3.connect(&amp;#39;heredis.db&amp;#39;)
cur = con.cursor()
for row in cur.execute(&amp;quot;SELECT * FROM table&amp;quot;):
    print(row)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;schema-de-la-base-heredis&quot;&gt;Schéma de la base Heredis&lt;&#x2F;h3&gt;
&lt;p&gt;J&#x27;ai utilisé &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dbdiagram.io&#x2F;&quot;&gt;dbdiagram.io&lt;&#x2F;a&gt; pour visualiser le schéma.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;notes.liorzou.bzh&#x2F;dev&#x2F;convert-heredis-to-gramps&#x2F;images&#x2F;schema-md.png&quot; alt=&quot;Schéma de la base Heredis&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;architecture-de-la-solution&quot;&gt;Architecture de la solution&lt;&#x2F;h2&gt;
&lt;p&gt;Après avoir analysé le schéma SQLite d&#x27;Heredis, j&#x27;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.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;structure-du-projet&quot;&gt;Structure du projet&lt;&#x2F;h3&gt;
&lt;pre&gt;&lt;code&gt;heredis-to-gramp&#x2F;
├── heredis&#x2F;        # Accès et utilitaires Heredis
├── gramps&#x2F;         # Modèles et client Gramps Web
├── utils&#x2F;          # Outils (géocodage, mapping, affichage)
├── commands.py
├── HeredisToGramps.py
└── main.py
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;connexion-a-la-base-heredis&quot;&gt;Connexion à la base Heredis&lt;&#x2F;h3&gt;
&lt;p&gt;La première étape reste l&#x27;extraction du fichier SQLite depuis l&#x27;archive &lt;code&gt;.hmw&lt;&#x2F;code&gt; :&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;cp mon-fichier.hmw&#x2F;mon-fichier.heredis .&#x2F;heredis.db
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;(faites des sauvegardes !)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;J&#x27;utilise une classe singleton &lt;code&gt;HeredisDb&lt;&#x2F;code&gt; pour réutiliser une seule connexion SQLite et éviter les ouvertures&#x2F;fermetures répétées.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;comprendre-le-schema&quot;&gt;Comprendre le schéma&lt;&#x2F;h3&gt;
&lt;p&gt;Le schéma tourne autour de la table centrale &lt;code&gt;Individus&lt;&#x2F;code&gt;. Les autres tables clés sont : &lt;code&gt;Evts&lt;&#x2F;code&gt;, &lt;code&gt;LienIndividuEvenement&lt;&#x2F;code&gt;, &lt;code&gt;Lieux&lt;&#x2F;code&gt;, &lt;code&gt;Sources&lt;&#x2F;code&gt;, &lt;code&gt;Medias&lt;&#x2F;code&gt;, &lt;code&gt;Unions&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Certaines spécificités d&#x27;Heredis demandent des adaptations : prénoms multiples, professions hiérarchiques, témoins d&#x27;événements, numérotation Sosa, etc.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mapping-vers-gramps&quot;&gt;Mapping vers Gramps&lt;&#x2F;h3&gt;
&lt;p&gt;Le mapping documente la correspondance entre types d&#x27;événements, genres, lieux et les objets Gramps. Un fichier de mapping (&lt;code&gt;heredis_gramps_mapping.txt&lt;&#x2F;code&gt;) centralise ces correspondances.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;integration-avec-gramps-web&quot;&gt;Intégration avec Gramps Web&lt;&#x2F;h3&gt;
&lt;p&gt;Pour l&#x27;import, j&#x27;utilise l&#x27;API REST de &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gramps-project.github.io&#x2F;gramps-web-api&#x2F;&quot;&gt;Gramps Web&lt;&#x2F;a&gt; plutôt que l&#x27;interface CLI. Un service Docker Compose facilite le déploiement local de Gramps Web.&lt;&#x2F;p&gt;
&lt;p&gt;Au début, j&#x27;avais envisagé d&#x27;utiliser la CLI de Gramps, mais l&#x27;API REST s&#x27;est avérée plus flexible et avec une courbe d&#x27;apprentissage plus rapide.&lt;&#x2F;p&gt;
&lt;p&gt;Il faudrait certainement réécrire une partie du code pour utiliser la CLI, mais pour l&#x27;instant je me concentre sur l&#x27;API REST (et c&#x27;est du one-shot!).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;import-par-lots-vs-import-unitaire&quot;&gt;Import par lots vs. import unitaire&lt;&#x2F;h3&gt;
&lt;p&gt;J&#x27;ai expérimenté un import par lots via l&#x27;endpoint &lt;code&gt;&#x2F;objects&lt;&#x2F;code&gt; pour améliorer les performances, mais l&#x27;approche s&#x27;est révélée instable dans certains cas. En production la méthode la plus fiable est l&#x27;import unitaire, même si plus lent.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;limitations-et-ameliorations-futures&quot;&gt;Limitations et améliorations futures&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Témoins d&#x27;événements non transférés&lt;&#x2F;li&gt;
&lt;li&gt;Détection et fusion des doublons&lt;&#x2F;li&gt;
&lt;li&gt;Export incrémental&lt;&#x2F;li&gt;
&lt;li&gt;Interface web de suivi&lt;&#x2F;li&gt;
&lt;li&gt;Rapport détaillé des éléments non convertis&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Cette approche réutilise la même connexion pour toutes les requêtes, évitant ainsi le coût d&#x27;ouvertures&#x2F;fermetures répétées.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;comprendre-le-schema-heredis&quot;&gt;Comprendre le schéma Heredis&lt;&#x2F;h2&gt;
&lt;p&gt;Le schéma d&#x27;Heredis est organisé autour de la table centrale &lt;strong&gt;&lt;code&gt;Individus&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;. Voici les tables principales :&lt;&#x2F;p&gt;
&lt;h3 id=&quot;principales-tables&quot;&gt;Principales tables&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Individus&lt;&#x2F;strong&gt; : personnes (noms, prénoms, genre, etc.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Evenements&lt;&#x2F;strong&gt; : naissances, décès, mariages, etc.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;LienIndividuEvenement&lt;&#x2F;strong&gt; : liaison personnes ↔ événements&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Lieux&lt;&#x2F;strong&gt; : emplacements géographiques&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Sources&lt;&#x2F;strong&gt; : sources documentaires&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Medias&lt;&#x2F;strong&gt; : fichiers médias (photos, documents, etc.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;IndividusUnions&lt;&#x2F;strong&gt; : mariages &#x2F; unions&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;specificites-d-heredis&quot;&gt;Spécificités d&#x27;Heredis&lt;&#x2F;h3&gt;
&lt;p&gt;Quelques particularités à prendre en compte :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prénoms multiples&lt;&#x2F;strong&gt; : table &lt;code&gt;Prenoms&lt;&#x2F;code&gt; avec notion de &quot;prénom usuel&quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Professions multiples&lt;&#x2F;strong&gt; : table &lt;code&gt;Professions&lt;&#x2F;code&gt; avec hiérarchie&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Témoins d&#x27;événements&lt;&#x2F;strong&gt; : gérés via &lt;code&gt;LienIndividuEvenement&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Qualité des sources&lt;&#x2F;strong&gt; : plusieurs niveaux (Source, Information, Preuve)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Numérotation Sosa&lt;&#x2F;strong&gt; : table dédiée à la numérotation d&#x27;Aboville&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;differences-structurelles&quot;&gt;Différences structurelles&lt;&#x2F;h3&gt;
&lt;p&gt;Certaines notions n&#x27;ont pas d&#x27;équivalent direct dans Gramps :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;professions multiples → transformées en attributs multiples&lt;&#x2F;li&gt;
&lt;li&gt;prénoms multiples → stockés dans &lt;code&gt;name.first_name&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;témoins d&#x27;événements → non pris en charge nativement (peuvent devenir des notes)&lt;&#x2F;li&gt;
&lt;li&gt;subdivisions géographiques → converties en &lt;code&gt;Place&lt;&#x2F;code&gt; avec coordonnées&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;creation-d-objets-via-l-api&quot;&gt;Création d&#x27;objets via l&#x27;API&lt;&#x2F;h3&gt;
&lt;p&gt;Le projet contient des modèles pour représenter les objets Gramps côté client. Exemple simplifié pour créer une personne :&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;class GrampsPeople(GrampsObject):
    def __init__(self, prenoms: str = &amp;quot;&amp;quot;, nom: str = &amp;quot;&amp;quot;):
        self.name = f&amp;quot;{prenoms} {nom}&amp;quot;.strip()
        self.primary_name = GrampsName()
        self.primary_name.first_name = prenoms
        self.primary_name.surname_list = [GrampsSurname(nom, primary=True)]
        self.primary_name.type = &amp;quot;Birth Name&amp;quot;
        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 {
            &amp;quot;primary_name&amp;quot;: self.primary_name.to_dict(),
            &amp;quot;gender&amp;quot;: self.gender,
        }
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;utilisation-du-script&quot;&gt;Utilisation du script&lt;&#x2F;h2&gt;
&lt;p&gt;Le script principal propose plusieurs modes d&#x27;utilisation :&lt;&#x2F;p&gt;
&lt;h3 id=&quot;statistiques-de-la-base-heredis&quot;&gt;Statistiques de la base Heredis&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Parce que c&#x27;est toujours intéressant de connaître la taille de sa base avant de lancer un import !&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;python3 main.py --stats

# Exemple de sortie
Nombre d&amp;#39;individus : 1245
Nombre d&amp;#39;événements : 3892
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;rechercher-un-individu&quot;&gt;Rechercher un individu&lt;&#x2F;h3&gt;
&lt;p&gt;Le programme permet de rechercher des individus par nom et d&#x27;afficher leurs dates et relations :&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;python3 main.py --search &amp;quot;Dupont&amp;quot; --show-dates --show-parents
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Exemple d&#x27;affichage :&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;👤 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
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On peut aussi interroger un individu par son identifiant :&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;python3 main.py --individual --id 42 --show-dates --show-parents
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Format d&#x27;affichage (exemple) :&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;42	DUPONT	Jean (1890-03-15 - 1965-07-22) [M]
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;operations-d-import&quot;&gt;Opérations d&#x27;import&lt;&#x2F;h3&gt;
&lt;p&gt;Le programme supporte l&#x27;import individuel et l&#x27;import en lot (expérimental) :&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;# 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
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;L&#x27;option &lt;code&gt;-y&lt;&#x2F;code&gt; confirme automatiquement les opérations sans demander de saisie.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;geocodage-des-lieux&quot;&gt;Géocodage des lieux&lt;&#x2F;h2&gt;
&lt;p&gt;Gramps peut stocker des coordonnées GPS pour les lieux. J&#x27;ai ajouté un géocodage automatique :&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;import requests
from functools import lru_cache

@lru_cache(maxsize=1000)
def geocode_place(place_name):
    &amp;quot;&amp;quot;&amp;quot;Géocode un lieu et met le résultat en cache.&amp;quot;&amp;quot;&amp;quot;
    response = requests.get(
        &amp;quot;https:&#x2F;&#x2F;nominatim.openstreetmap.org&#x2F;search&amp;quot;,
        params={
            &amp;quot;q&amp;quot;: place_name,
            &amp;quot;format&amp;quot;: &amp;quot;json&amp;quot;,
            &amp;quot;limit&amp;quot;: 1
        }
    )
    
    if response.json():
        result = response.json()[0]
        return {
            &amp;quot;lat&amp;quot;: result[&amp;quot;lat&amp;quot;],
            &amp;quot;lon&amp;quot;: result[&amp;quot;lon&amp;quot;]
        }
    return None
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Un cache mémoire permet d&#x27;éviter les requêtes répétées pour les mêmes lieux, améliorant ainsi les performances (et en limitant les appels à l&#x27;API de Nominatim).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gestion-des-medias&quot;&gt;Gestion des médias&lt;&#x2F;h2&gt;
&lt;p&gt;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.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def get_media_for_individual(self, individual_id):
    &amp;quot;&amp;quot;&amp;quot;
    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)
    &amp;quot;&amp;quot;&amp;quot;
    query = &amp;quot;&amp;quot;&amp;quot;
        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
    &amp;quot;&amp;quot;&amp;quot;
    # Exécuter la requête et retourner les résultats
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;import-par-lots-experimentations&quot;&gt;Import par lots — expérimentations&lt;&#x2F;h2&gt;
&lt;p&gt;J&#x27;ai initialement développé un import par lots via l&#x27;endpoint &lt;code&gt;&#x2F;objects&lt;&#x2F;code&gt; de l&#x27;API Gramps pour améliorer les performances (réduire les 4–5 appels API par personne à 1–2 appels pour plusieurs personnes).&lt;&#x2F;p&gt;
&lt;p&gt;Cependant, &lt;strong&gt;cette approche par lots a été abandonnée&lt;&#x2F;strong&gt; à cause d&#x27;instabilités : échecs de transaction et comportements incohérents avec certains jeux de données.&lt;&#x2F;p&gt;
&lt;p&gt;La méthode stable actuelle importe les individus un par un — plus fiable, mais plus lent.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;limitations-actuelles-et-ameliorations-futures&quot;&gt;Limitations actuelles et améliorations futures&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;limitations-actuelles&quot;&gt;Limitations actuelles&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Témoins d&#x27;événements&lt;&#x2F;strong&gt; : non transférés (pas d&#x27;équivalent natif dans Gramps)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Hiérarchie des professions&lt;&#x2F;strong&gt; : aplatie en liste simple&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Recadrage des photos&lt;&#x2F;strong&gt; : informations perdues&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Tâches Heredis&lt;&#x2F;strong&gt; : non exportées&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Vitesse d&#x27;import&lt;&#x2F;strong&gt; : l&#x27;import unitaire est plus lent mais plus stable&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;ameliorations-envisagees&quot;&gt;Améliorations envisagées&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Exporter les témoins comme notes textuelles&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Détection et fusion des doublons&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Export incrémental&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Interface web de suivi de l&#x27;import&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Validation des données avant import&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Rapport détaillé des éléments non convertis&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Implémentation stable d&#x27;un import par lots&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Ce projet m&#x27;a permis de :&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Comprendre en profondeur les deux modèles (Heredis et Gramps)&lt;&#x2F;li&gt;
&lt;li&gt;Progresser en Python (singletons, décorateurs, optimisation SQLite)&lt;&#x2F;li&gt;
&lt;li&gt;Rédiger une documentation utile&lt;&#x2F;li&gt;
&lt;li&gt;Construire un convertisseur fonctionnel malgré les différences structurelles&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;ressources-utiles&quot;&gt;Ressources utiles&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gramps-project.github.io&#x2F;gramps-web-api&#x2F;&quot;&gt;Documentation Gramps Web API&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gramps-project&#x2F;gramps-web&quot;&gt;Gramps.js (Gramps Web)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dbdiagram.io&#x2F;&quot;&gt;DBDiagram.io&lt;&#x2F;a&gt; — pour visualiser les schémas SQL&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sqlite.org&#x2F;docs.html&quot;&gt;Documentation SQLite&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.geneanet.org&#x2F;forum&#x2F;&quot;&gt;Forum Geneanet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
    </channel>
</rss>
