my code should create a graphe , with the origines of nodes for the geanealogie of french buldings.
here is the input 

JSON1 : {
    "filiation": [
        {
            "date": "12/04/2011",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "101 à 104",
                    "lot_fille": "",
                    "volume_mere": "1",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2011P5495/UD1",
                    "designation_cadastrale_mere": "CD 34, CD 231, CD 239 à 241, CD 243",
                    "designation_cadastrale_fille": ""
                }
            ]
        },
        {
            "date": "24/07/2009",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "1 à 2",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6563/UD1",
                    "designation_cadastrale_mere": "CD 243 à 244",
                    "designation_cadastrale_fille": ""
                }
            ]
        },
        {
            "date": "23/07/2009",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "1 à 2",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6563/UD1",
                    "designation_cadastrale_mere": "CD 243 à 244",
                    "designation_cadastrale_fille": ""
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 33",
                    "designation_cadastrale_fille": "CD 243 à 244"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 163",
                    "designation_cadastrale_fille": "CD 238 à 241"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 230",
                    "designation_cadastrale_fille": "CD 231 à 237"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P3197/UD1",
                    "designation_cadastrale_mere": "CD 34",
                    "designation_cadastrale_fille": ""
                }
            ]
        },
        {
            "date": "23/07/2009",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 33",
                    "designation_cadastrale_fille": "CD 243 à 244"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 163",
                    "designation_cadastrale_fille": "CD 238 à 241"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 230",
                    "designation_cadastrale_fille": "CD 231 à 237, CD 242"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P2045/UD1",
                    "designation_cadastrale_mere": "CD 31 à 32, CD 35 à 38, CD 164 à 165",
                    "designation_cadastrale_fille": "CD 230"
                }
            ]
        },
        {
            "date": "16/03/2009",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 33",
                    "designation_cadastrale_fille": "CD 243 à 244"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 163",
                    "designation_cadastrale_fille": "CD 238 à 241"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P6551/UD1",
                    "designation_cadastrale_mere": "CD 230",
                    "designation_cadastrale_fille": "CD 231 à 237, CD 242"
                },
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 7EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2009P2045/UD1",
                    "designation_cadastrale_mere": "CD 31 à 32, CD 35 à 38, CD 164 à 165",
                    "designation_cadastrale_fille": "CD 230"
                }
            ]
        },
        [
            {
                "lots": [],
                "commune": "LYON 7EME",
                "volumes": [],
                "cadastres": [
                    "CD233",
                    "CD234",
                    "CD235",
                    "CD236",
                    "CD242"
                ]
            }
        ]
    ]
}


JSON 2 : {
    "filiation": [
        {
            "date": "29/07/2009",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "635 à 659",
                    "lot_fille": "696",
                    "volume_mere": "",
                    "commune_mere": "VILLEURBANNE",
                    "volume_fille": "",
                    "commune_fille": "VILLEURBANNE",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P03 2009P7318/UD1",
                    "designation_cadastrale_mere": "BB 304",
                    "designation_cadastrale_fille": "BB 304"
                },
                {
                    "lot_mere": "610 à 634",
                    "lot_fille": "695",
                    "volume_mere": "",
                    "commune_mere": "VILLEURBANNE",
                    "volume_fille": "",
                    "commune_fille": "VILLEURBANNE",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P03 2009P7318/UD1",
                    "designation_cadastrale_mere": "BB 304",
                    "designation_cadastrale_fille": "BB 304"
                },
                {
                    "lot_mere": "589 à 590, 593 à 602",
                    "lot_fille": "694",
                    "volume_mere": "",
                    "commune_mere": "VILLEURBANNE",
                    "volume_fille": "",
                    "commune_fille": "VILLEURBANNE",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P03 2009P7318/UD1",
                    "designation_cadastrale_mere": "BB 304",
                    "designation_cadastrale_fille": "BB 304"
                }
            ]
        },
        {
            "date": "12/04/2011",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "101 à 104",
                    "lot_fille": "",
                    "volume_mere": "1",
                    "commune_mere": "LYON 7EME",
                    "volume_fille": "",
                    "commune_fille": "",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2011P5495/UD1",
                    "designation_cadastrale_mere": "CD 34, CD 231, CD 239 à 241, CD 243",
                    "designation_cadastrale_fille": ""
                }
            ]
        },
        {
            "date": "24/11/2010",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 3EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 3EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2010P10862/UD1",
                    "designation_cadastrale_mere": "AY 159",
                    "designation_cadastrale_fille": "AY 170 à 171"
                }
            ]
        },
        {
            "date": "29/07/2009",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "635 à 659",
                    "lot_fille": "696",
                    "volume_mere": "",
                    "commune_mere": "VILLEURBANNE",
                    "volume_fille": "",
                    "commune_fille": "VILLEURBANNE",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P03 2009P7318/UD1",
                    "designation_cadastrale_mere": "BB 304",
                    "designation_cadastrale_fille": "BB 304"
                },
                {
                    "lot_mere": "610 à 634",
                    "lot_fille": "695",
                    "volume_mere": "",
                    "commune_mere": "VILLEURBANNE",
                    "volume_fille": "",
                    "commune_fille": "VILLEURBANNE",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P03 2009P7318/UD1",
                    "designation_cadastrale_mere": "BB 304",
                    "designation_cadastrale_fille": "BB 304"
                },
                {
                    "lot_mere": "589 à 590, 593 à 602",
                    "lot_fille": "694",
                    "volume_mere": "",
                    "commune_mere": "VILLEURBANNE",
                    "volume_fille": "",
                    "commune_fille": "VILLEURBANNE",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P03 2009P7318/UD1",
                    "designation_cadastrale_mere": "BB 304",
                    "designation_cadastrale_fille": "BB 304"
                }
            ]
        }
        ,
        {
            "date": "27/06/2005",
            "tableau_immeuble_mere_fille": [
                {
                    "lot_mere": "",
                    "lot_fille": "",
                    "volume_mere": "",
                    "commune_mere": "LYON 3EME",
                    "volume_fille": "",
                    "commune_fille": "LYON 3EME",
                    "numero_disposition": "1",
                    "identifiant_formalite": "6904P02 2005P5853/UD1",
                    "designation_cadastrale_mere": "AY 51",
                    "designation_cadastrale_fille": "AY 126 à 127"
                }
            ]
        },
        []
    ]
}

Here is how i call my function to create the gros of nodes and origin : 

import os
import sys

# Add the project root directory to the Python path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))

import json

import src.schemas.filiation as filiation
from src.core.data import generate_graph_choix_pdf, generate_word_filliation
from src.core.graph import *
from src.core.utils import get_logger

# print(os.getcwd())
from src.schemas.filiation import *



if __name__ == "__main__":
    with open("tableaux_mere_fille1.json", "r") as f:
        ouput_etat_hypothecaire = json.load(f)
    liste_filiations = [ouput_etat_hypothecaire["filiation"]]

    with open("tableaux_mere_fille2.json", "r") as f:
        ouput_etat_hypothecaire = json.load(f)
        liste_filiations.append(ouput_etat_hypothecaire["filiation"])
    logger = get_logger()

    json_result = generate_graphe_from_formalite(liste_filiations, logger=logger)
    with open("graphe_filiation.json", "w") as f:
        json.dump(json_result, f, indent=4)

    word_b64 = generate_word_filliation(json_result["origines"])
    pdf_b64 = generate_graph_choix_pdf(json_result)

    with open("word_b64.txt", "w") as f:
        f.write(word_b64)
    with open("pdf_b64.txt", "w") as f:
        f.write(pdf_b64)

    # generate_and_save_graph(liste_filiations)

    # visualize_graph(json_result["origines"])


Here is my code base :

import re
import unicodedata
from datetime import datetime
from typing import List


def first_numeric_index(s):
    for index, char in enumerate(s):
        if char.isdigit():
            return index
    return -1


def normalize_string(input_string: str) -> str:
    """
    input : input_string : str : string to normalize
    output : str : normalized string

    Normalize a string by removing accents, special characters, and converting to uppercase
    """
    # Normalize string to decompose special characters
    normalized = unicodedata.normalize("NFD", input_string)
    # Remove diacritics (accents) by filtering out the non-letter characters
    no_accents = "".join(
        char for char in normalized if unicodedata.category(char) != "Mn"
    )
    # Replace '-' and "'" with spaces
    no_accents = no_accents.replace("-", " ").replace("'", " ")
    # Remove all characters except letters, digits, and spaces
    clean_string = re.sub(r"[^A-Za-z0-9\s]", "", no_accents)
    # Convert to uppercase
    return clean_string.upper()


class Bien:
    type_bien: str
    description: dict

    def __init__(
        self, type_bien: str, description: dict
    ):  # description = {"Cadastre": "E181 à 184", "Volume": "149", "Lot": "11", "Commune": "PUTEAUX"}
        self.type_bien = type_bien
        self.description = {
            "Commune": description["Commune"],
            "Cadastre": description["Cadastre"],  # peut etre une liste
            "Volume": description["Volume"],  # contient le volume avec l anne courante
            "Lot": description["Lot"],  # contient le lot avec l anne courante
        }


class EvenementFiliation:
    date_evemement: str
    commune: str
    type_element_sujet: str
    type_element_changement: str
    Liste_elements_sujet: list
    Liste_elements_changement: list
    sujet: dict
    changement: dict
    cadastre: str
    volume: str
    lot: str
    temps: int

    def __init__(
        self,
        date_evemement: str,
        type_element_sujet: str,
        type_element_changement: str,
        Liste_elements_sujet: list,
        Liste_elements_changement: list,
        commune: str,
        cadastre: str,
        volume: str,
        lot: str,
        temps: int,
    ):
        self.date_evemement = date_evemement
        self.type_element_sujet = type_element_sujet  # Cadastre, Volume, Lot
        self.type_element_changement = type_element_changement
        self.Liste_elements_sujet = []
        self.Liste_elements_changement = []
        if type_element_sujet == "Lot":
            if len(Liste_elements_sujet) == 0:
                self.Liste_elements_sujet = (
                    tratement_volume(volume)
                    if len(volume) > 0
                    else traitement_cadastre(cadastre)
                )
            else:
                self.Liste_elements_sujet = Liste_elements_sujet
            if len(Liste_elements_changement) == 0:
                self.Liste_elements_changement = (
                    tratement_volume(volume)
                    if len(volume) > 0
                    else traitement_cadastre(cadastre)
                )
            else:
                self.Liste_elements_changement = Liste_elements_changement
        if type_element_sujet == "Volume":
            if len(Liste_elements_sujet) == 0:
                self.Liste_elements_sujet = traitement_cadastre(cadastre)
            else:
                self.Liste_elements_sujet = Liste_elements_sujet

            if len(Liste_elements_changement) == 0:
                self.Liste_elements_changement = traitement_cadastre(cadastre)
            else:
                self.Liste_elements_changement = Liste_elements_changement

        if type_element_sujet == "Cadastre":
            self.Liste_elements_sujet = Liste_elements_sujet
        if type_element_changement == "Cadastre":
            self.Liste_elements_changement = Liste_elements_changement
        self.commune = normalize_string(commune)
        self.cadastre = cadastre
        self.volume = volume
        self.lot = lot
        self.temps = temps

    def is_in_changement(self, element: str, commune: str):
        # if "VOL" in element:
        #     print(element,self.Liste_elements_changement, commune, self.commune)

        return element in self.Liste_elements_changement and commune == self.commune

    def is_in_sujet(self, element: str, commune: str):
        # print(element,self.Liste_elements_sujet, commune, self.commune)
        return element in self.Liste_elements_sujet and commune == self.commune


class Historique:  # represent a FIFO list of EvenementFiliation
    evenements: List[EvenementFiliation]

    def __init__(self):
        self.evenements = []

    def add_evenement(self, evenement: EvenementFiliation):
        addable = True
        for event in self.evenements:
            if (
                event.cadastre == evenement.cadastre
                and event.volume == evenement.volume
                and event.lot == evenement.lot
                and event.commune == evenement.commune
                and event.Liste_elements_changement == evenement.Liste_elements_changement
                and event.Liste_elements_sujet == evenement.Liste_elements_sujet
            ):
                addable = False
                break
        if addable:
            print(  "Evenement deja present : ",evenement.cadastre,",",evenement.volume,",",evenement.lot,",",evenement.commune,"--",evenement.Liste_elements_sujet,"--",evenement.Liste_elements_changement)
            self.evenements.append(evenement)
        #print(len(self.evenements))

    def get_evenement_courant(self):
        return self.evenements.pop(0)

    def get_length(self):
        return len(self.evenements)


class Filiation:
    historique: Historique
    bien: Bien
    descendance: dict

    def __init__(self, historique: Historique, bien: Bien):
        self.historique = historique
        self.bien = bien
        # Le premier descendent est le bien lui meme
        self.descendance = {
            "Commune": bien.description["Commune"],
            "Cadastre": [
                {"designation": cadastre} for cadastre in bien.description["Cadastre"]
            ],  # liste de dictionaires retracent l historique du cadastre {"deisignation": "CAD_E219", "date": "2013"}
            "Volume": [
                {"designation": volume} for volume in bien.description["Volume"]
            ],  # liste de dictionaires retracent l historique du volume {"deisignation": "VOL_219", "date": "2013"}
            "Lot": [
                {"designation": lot} for lot in bien.description["Lot"]
            ],  # liste de dictionaires retracent l historique du lot {"deisignation": "LOT_219", "date": "2013"}
        }

    def get_descendance(self):
        return self.descendance

    def add_ancetre(self, evenement: EvenementFiliation):
        cades_tmp = []
        ind_cadastre = 0
        # for cadastre_courant in self.descendance["Cadastre"]:
        while ind_cadastre < len(self.descendance["Cadastre"]):
            cadastre_courant = self.descendance["Cadastre"][ind_cadastre]
            # print(cadastre_courant)
            if evenement.is_in_changement(
                cadastre_courant["designation"], self.descendance["Commune"]
            ):
                # On ajoute a remplie la liste des ancetre du cadastre courant
                for ancetre in evenement.Liste_elements_sujet:
                    # cades_tmp.append({"designation": ancetre, "evenement": evenement, "descendance": cadastre_courant["designation"]})
                    self.descendance["Cadastre"].append(
                        {
                            "designation": ancetre,
                            "evenement": evenement,
                            "descendance": cadastre_courant["designation"],
                        }
                    )  # dangerous code
            ind_cadastre += 1
        # if len(cades_tmp) > 0:
        #     self.descendance["Cadastre"].extend(cades_tmp)

        vol_tmp = []
        ind_volume = 0
        # for volume_courant in self.descendance["Volume"]:
        while ind_volume < len(self.descendance["Volume"]):
            # print("le volume en cour est : ", self.descendance["Volume"][ind_volume])
            volume_courant = self.descendance["Volume"][ind_volume]
            if evenement.is_in_changement(
                volume_courant["designation"], self.descendance["Commune"]
            ):
                # On ajoute a remplie la liste des ancetre du volume courant
                for ancetre in evenement.Liste_elements_sujet:
                    # vol_tmp.append({"designation": ancetre, "evenement": evenement, "descendance": volume_courant["designation"]})
                    self.descendance["Volume"].append(
                        {
                            "designation": ancetre,
                            "evenement": evenement,
                            "descendance": volume_courant["designation"],
                        }
                    )  # dangerous code
            ind_volume += 1
        # if len(vol_tmp) > 0:
        #     self.descendance["Volume"].extend(vol_tmp)

        lot_tmp = []
        ind_lot = 0
        # for lot_courant in self.descendance["Lot"]:
        while ind_lot < len(self.descendance["Lot"]):
            lot_courant = self.descendance["Lot"][ind_lot]
            if evenement.is_in_changement(
                lot_courant["designation"], self.descendance["Commune"]
            ):
                # On ajoute a remplie la liste des ancetre du lot courant
                for ancetre in evenement.Liste_elements_sujet:
                    # lot_tmp.append({"designation": ancetre, "evenement": evenement, "descendance": lot_courant["designation"]})
                    self.descendance["Lot"].append(
                        {
                            "designation": ancetre,
                            "evenement": evenement,
                            "descendance": lot_courant["designation"],
                        }
                    )  # dangerous code
            ind_lot += 1
        # if len(lot_tmp) > 0:
        #     self.descendance["Lot"].extend(lot_tmp)

    # On recupere les ancetres de chaque element du bien, on stocke les ancetres dans le champ descendance
    def get_ancetres(self):
        event_ancetres = self.historique.get_evenement_courant()
        while self.historique.get_length() > 0:
            self.add_ancetre(event_ancetres)
            event_ancetres = self.historique.get_evenement_courant()
        self.add_ancetre(event_ancetres)
        # Remove duplicate event reference

        cadastre_event_tmp = []
        cadastre_tmp = []
        event_cades_tmp = [
            event for event in self.descendance["Cadastre"] if len(event.keys()) > 1
        ]  # seul les event on des keys superieur a 1
        for cadastre in event_cades_tmp:
            if cadastre["evenement"] not in cadastre_event_tmp:
                cadastre_event_tmp.append(cadastre["evenement"])
                cadastre_tmp.append(cadastre)
        self.descendance["Cadastre"] = cadastre_tmp

        volume_event_tmp = []
        volume_tmp = []
        event_vol_tmp = [
            event for event in self.descendance["Volume"] if len(event.keys()) > 1
        ]  # seul les event on des keys superieur a 1
        for volume in event_vol_tmp:
            if volume["evenement"] not in volume_event_tmp:
                volume_event_tmp.append(volume["evenement"])
                volume_tmp.append(volume)
        self.descendance["Volume"] = volume_tmp

        lot_event_tmp = []
        lot_tmp = []
        event_lot_tmp = [
            event for event in self.descendance["Lot"] if len(event.keys()) > 1
        ]  # seul les event on des keys superieur a 1
        for lot in event_lot_tmp:
            if lot["evenement"] not in lot_event_tmp:
                lot_event_tmp.append(lot["evenement"])
                lot_tmp.append(lot)
        self.descendance["Lot"] = lot_tmp
        return self.descendance


def traitement_cadastre(cadastre: str) -> list[str]:
    # traitement du cadastre
    lien_cadastre_virgule = cadastre.replace(" ", "").split(",")
    cadastre_finale = []
    if len(lien_cadastre_virgule) > 1:
        for cadastre_tmp in lien_cadastre_virgule:
            liste_cadastre = cadastre_tmp.replace(" ", "").split("à")
            if cadastre_tmp.replace(" ", "")[1].isnumeric():
                ind_start_num = 0
            else:
                ind_start_num = 1
            if len(liste_cadastre) > 1:
                prefix = liste_cadastre[0][: ind_start_num + 1]
                start = int(liste_cadastre[0][ind_start_num + 1 :])
                end = int(liste_cadastre[-1])

                for i in range(start, end + 1):
                    cadastre_finale.append("CAD_" + prefix + str(i))
            else:
                cadastre_finale += (
                    ["CAD_" + liste_cadastre[0]] if liste_cadastre[0] != "" else []
                )
    else:
        liste_cadastre = cadastre.replace(" ", "").split("à")
        if liste_cadastre[0] != "" and cadastre.replace(" ", "")[1].isnumeric():
            ind_start_num = 0
        else:
            ind_start_num = 1
        if len(liste_cadastre) > 1:
            prefix = liste_cadastre[0][: ind_start_num + 1]

            ind_start = first_numeric_index(liste_cadastre[0])
            start = int(liste_cadastre[0][ind_start:])
            ind_end = first_numeric_index(liste_cadastre[-1])
            end = int(liste_cadastre[-1][ind_end:])

            for i in range(start, end + 1):
                cadastre_finale.append("CAD_" + prefix + str(i))
        else:
            cadastre_finale += (
                ["CAD_" + liste_cadastre[0]] if liste_cadastre[0] != "" else []
            )
    return cadastre_finale


def tratement_volume(volume: str):
    # traitement du volume
    lien_volume_virgule = volume.replace(" ", "").split(",")
    volume_finale = []
    if len(lien_volume_virgule) > 1:
        for volume_tmp in lien_volume_virgule:
            liste_volume = volume_tmp.replace(" ", "").split("à")
            if len(liste_volume) > 1:
                start = int(liste_volume[0])
                end = int(liste_volume[-1])
                for i in range(start, end + 1):
                    volume_finale.append("VOL_" + str(i))
            else:
                volume_finale += ["VOL_" + volume_tmp] if volume_tmp != "" else []
    else:
        liste_volume = volume.replace(" ", "").split("à")
        if len(liste_volume) > 1:
            start = int(liste_volume[0])
            end = int(liste_volume[-1])
            for i in range(start, end + 1):
                volume_finale.append("VOL_" + str(i))
        else:
            volume_finale += ["VOL_" + volume] if volume != "" else []
    return volume_finale


def traitement_lot(lot: str):
    # traitement du lot
    lien_lot_virgule = lot.replace(" ", "").split(",")
    lot_finale = []
    if len(lien_lot_virgule) > 1:
        for lot_tmp in lien_lot_virgule:
            liste_lot = lot_tmp.replace(" ", "").split("à")
            if len(liste_lot) > 1:
                start = int(liste_lot[0])
                end = int(liste_lot[-1])
                for i in range(start, end + 1):
                    lot_finale.append("LOT_" + str(i))
            else:
                lot_finale += ["LOT_" + lot_tmp] if lot_tmp != "" else []
    else:
        liste_lot = lot.replace(" ", "").split("à")
        if len(liste_lot) > 1:
            start = int(liste_lot[0])
            end = int(liste_lot[-1])
            for i in range(start, end + 1):
                lot_finale.append("LOT_" + str(i))
        else:
            lot_finale += ["LOT_" + lot] if lot != "" else []
    return lot_finale


def creation_bien(cadastre: str, volume: str, lot: str, commune: str):
    return Bien(
        "Terrain",
        {
            "Cadastre": traitement_cadastre(cadastre),
            "Volume": tratement_volume(volume),
            "Lot": traitement_lot(lot),
            "Commune": commune,
        },
    )


##element_sujet et element_changement sont des creer en appelant les fonctions de traitement de cadastre, volume et lot
def creation_evenement(
    date: str,
    type_element_sujet: str,
    type_element_changement: str,
    elements_sujet: list,
    elements_changement: list,
    commune: str,
    cadastre: str,
    volume: str,
    lot: str,
    temps: int,
):
    return EvenementFiliation(
        date,
        type_element_sujet,
        type_element_changement,
        elements_sujet,
        elements_changement,
        commune,
        cadastre,
        volume,
        lot,
        temps,
    )


def creation_historique(list_evenements: list[dict]):
    historique = Historique()
    annees = len(list_evenements)
    for evenement in list_evenements:
        for ligne_mere_fille in evenement["tableau_immeuble_mere_fille"]:
            type_element_sujet = check_changement(ligne_mere_fille)
            if type_element_sujet is not None:
                historique.add_evenement(
                    creation_evenement(
                        evenement["date"],
                        type_element_sujet["type_element"],
                        type_element_sujet["type_element"],
                        type_element_sujet["elements_sujet"],
                        type_element_sujet["elements_changement"],
                        ligne_mere_fille["commune_mere"],
                        ligne_mere_fille["designation_cadastrale_mere"],
                        ligne_mere_fille["volume_mere"],
                        ligne_mere_fille["lot_mere"],
                        annees,
                    )
                )
                annees -= 1
    return historique


def creation_filiation(list_evenements: list[dict], bien: dict):
    return Filiation(
        creation_historique(list_evenements),
        creation_bien(bien["Cadastre"], bien["Volume"], bien["Lot"], bien["Commune"]),
    )


def check_changement(ligne_mere_fille: dict):
    if ligne_mere_fille["lot_mere"] != ligne_mere_fille["lot_fille"]:
        return {
            "type_element": "Lot",
            "elements_sujet": traitement_lot(ligne_mere_fille["lot_mere"]),
            "elements_changement": traitement_lot(ligne_mere_fille["lot_fille"]),
        }
    elif ligne_mere_fille["volume_mere"] != ligne_mere_fille["volume_fille"]:
        return {
            "type_element": "Volume",
            "elements_sujet": tratement_volume(ligne_mere_fille["volume_mere"]),
            "elements_changement": tratement_volume(ligne_mere_fille["volume_fille"]),
        }
    elif (
        ligne_mere_fille["designation_cadastrale_mere"]
        != ligne_mere_fille["designation_cadastrale_fille"]
    ):
        return {
            "type_element": "Cadastre",
            "elements_sujet": traitement_cadastre(
                ligne_mere_fille["designation_cadastrale_mere"]
            ),
            "elements_changement": traitement_cadastre(
                ligne_mere_fille["designation_cadastrale_fille"]
            ),
        }
    else:
        return None


def get_event_changement_not_sujet_futur(
    historique: Historique,
) -> list[EvenementFiliation]:
    """
    Retourne la liste des evenements dont les changements ne sont pas des sujets d'evenements futurs

    Args:
        historique (Historique): L'historique des evenements

    Returns:
        list[EvenementFiliation]: La liste des evenements dont les changements ne sont pas des sujets d'evenements futurs
    """
    historique_tmp = historique.evenements.copy()
    historique_tmp.reverse()
    evenements = []
    for i, event in enumerate(historique_tmp):
        is_changement_not_sujet = True
        for future_event in historique_tmp[i + 1 :]:
            ## CAS ou on passe de cadastre a cadastre, ou de volume a volume, ou de lot a lot
            cadastre_courant = traitement_cadastre(event.cadastre)
            sujet_courant = list(
                set(event.Liste_elements_changement + cadastre_courant)
            )
            if (
                any(
                    element
                    in future_event.Liste_elements_sujet
                    + traitement_cadastre(future_event.cadastre)
                    for element in sujet_courant
                )
                and event.commune == future_event.commune
            ):
                is_changement_not_sujet = False
                break

            ##CAs ou on passe de lot a cadastre ou lot a volume, ou de volume a cadastre
            changement_courant = list(
                set(event.Liste_elements_changement + cadastre_courant)
            )

            if (
                any(
                    element
                    in future_event.Liste_elements_changement
                    + traitement_cadastre(future_event.cadastre)
                    for element in changement_courant
                )
                and event.commune == future_event.commune
            ):
                is_changement_not_sujet = False
                break
        if is_changement_not_sujet:
            evenements.append(event)

    return evenements


def generate_graph_changement_no_is_sujet_result(
    leaf: list[EvenementFiliation],
) -> dict:
    """
        Generate a graph of the changement no is sujet result

        Args:
            leaf (list[EvenementFiliation]): The list of the leaf of the tres of events

        Returns:
            dict: The graph of the changement no is sujet result
            The dict is composed of :
            {
        "nodes": [ //represente une liste d elements pouvant etre cadastre, volumes, lots
            {
            "id": int, //identifiant unique du noeud
            "type": str, // valeur possible "lot" ou "volume", ou "cadastre" ou "commune"
            "parents_ids": list[int], //identifiant des des structures plus petites  commune <- cadastre, casdastre <- volume, cadastre <- lot, volume <- lot
            "children_ids": list[int], //identifiant des des structures plus petites  commune -> cadastre, casdastre ->volume, cadastre->lot, volume -> lot
            "value": str, //valeur du noeud, E120 pour un cadastre par exemple
            "choosable": bool //indique si ce noeud est clicable ou non
            },
            ...
        ],

    }

    Exemple :
    {
        "nodes": [
            {"id": 1, "type": "lot", "parents_ids": [4], "children_ids": [], "value": "1"},
            {"id": 2, "type": "lot", "parents_ids": [4,5], "children_ids": [], "value": "4"},
            {"id": 10, "type": "lot", "parents_ids": [3], "children_ids": [], "value": "1"},
            {"id": 3, "type": "volume", "parents_ids": [5,6,7], "children_ids": [10], "value": "100"},
            {"id": 7, "type": "cadastre", "parents_ids": [9], "children_ids": [3], "value": "A202"},
            {"id": 6, "type": "cadastre", "parents_ids": [9], "children_ids": [3], "value": "A201"},
            {"id": 5, "type": "cadastre", "parents_ids": [8,9], "children_ids": [2,3], "value": "E182"},
            {"id": 4, "type": "cadastre", "parents_ids": [8], "children_ids": [1,2], "value": "E181"},
            {"id": 8, "type": "commune", "parents_ids": [], "children_ids": [4,5,6,7], "value": "Nanterre"},
            {"id": 9, "type": "commune", "parents_ids": [], "children_ids": [5,6,7], "value": "Paris"}
        ]
    }
    """
    nodes = []
    id_counter = 1
    id_map = {}  # To keep track of created nodes

    for event in leaf:
        commune = event.commune
        cadastres = (
            event.Liste_elements_changement
            if event.type_element_changement == "Cadastre"
            else traitement_cadastre(event.cadastre)
        )
        volumes = (
            event.Liste_elements_changement
            if event.type_element_changement == "Volume"
            else tratement_volume(event.volume)
        )
        lots = (
            event.Liste_elements_changement
            if event.type_element_changement == "Lot"
            else traitement_lot(event.lot)
        )

        if lots != []:
            if "C" in lots[0].replace("LOT_", ""):
                lots = []
        # Create or get commune node
        if commune not in id_map:
            commune_node = {
                "id": id_counter,
                "type": "commune",
                "parents_ids": [],
                "children_ids": [],
                "value": commune,
                "choosable": False,
                "chosen": False,
                "ref_event": event,
                "origine_id": None,
            }
            nodes.append(commune_node)
            id_map[commune] = id_counter
            id_counter += 1
        commune_id = id_map[commune]

        # Create cadastre nodes
        for cadastre in cadastres:
            if cadastre not in id_map:
                cadastre_node = {
                    "id": id_counter,
                    "type": "cadastre",
                    "parents_ids": [],
                    "children_ids": [],
                    "value": cadastre.replace("CAD_", ""),
                    "choosable": True,
                    "chosen": False,
                    "ref_event": event,
                    "origine_id": None,
                }
                nodes.append(cadastre_node)
                id_map[cadastre] = id_counter
                id_counter += 1
            cadastre_id = id_map[cadastre]

            # Add commune as parent if not already present
            if commune_id not in nodes[cadastre_id - 1]["parents_ids"]:
                nodes[cadastre_id - 1]["parents_ids"].append(commune_id)
            if cadastre_id not in nodes[commune_id - 1]["children_ids"]:
                nodes[commune_id - 1]["children_ids"].append(cadastre_id)

            if (
                not volumes
            ):  # If volumes list is empty, connect lots directly to cadastre
                for lot in lots:
                    if lot not in id_map:
                        lot_node = {
                            "id": id_counter,
                            "type": "lot",
                            "parents_ids": [],
                            "children_ids": [],
                            "value": lot.replace("LOT_", ""),
                            "choosable": True,
                            "chosen": False,
                            "ref_event": event,
                            "origine_id": None,
                        }
                        nodes.append(lot_node)
                        id_map[lot] = id_counter
                        id_counter += 1
                    lot_id = id_map[lot]

                    # Add cadastre as parent if not already present
                    if cadastre_id not in nodes[lot_id - 1]["parents_ids"]:
                        nodes[lot_id - 1]["parents_ids"].append(cadastre_id)
                    if lot_id not in nodes[cadastre_id - 1]["children_ids"]:
                        nodes[cadastre_id - 1]["children_ids"].append(lot_id)
            else:
                # Create volume nodes
                for volume in volumes:
                    if volume not in id_map:
                        volume_node = {
                            "id": id_counter,
                            "type": "volume",
                            "parents_ids": [],
                            "children_ids": [],
                            "value": volume.replace("VOL_", ""),
                            "choosable": True,
                            "chosen": False,
                            "ref_event": event,
                            "origine_id": None,
                        }
                        nodes.append(volume_node)
                        id_map[volume] = id_counter
                        id_counter += 1
                    volume_id = id_map[volume]

                    # Add cadastre as parent if not already present
                    if cadastre_id not in nodes[volume_id - 1]["parents_ids"]:
                        nodes[volume_id - 1]["parents_ids"].append(cadastre_id)
                    if volume_id not in nodes[cadastre_id - 1]["children_ids"]:
                        nodes[cadastre_id - 1]["children_ids"].append(volume_id)

                    # Create lot nodes
                    for lot in lots:
                        if lot not in id_map:
                            lot_node = {
                                "id": id_counter,
                                "type": "lot",
                                "parents_ids": [],
                                "children_ids": [],
                                "value": lot.replace("LOT_", ""),
                                "choosable": True,
                                "chosen": False,
                                "ref_event": event,
                                "origine_id": None,
                            }
                            nodes.append(lot_node)
                            id_map[lot] = id_counter
                            id_counter += 1
                        lot_id = id_map[lot]

                        # Add volume as parent if not already present
                        if volume_id not in nodes[lot_id - 1]["parents_ids"]:
                            nodes[lot_id - 1]["parents_ids"].append(volume_id)
                        if lot_id not in nodes[volume_id - 1]["children_ids"]:
                            nodes[volume_id - 1]["children_ids"].append(lot_id)

    # Remove self-references from parents_ids and children_ids
    for node in nodes:
        node["parents_ids"] = [
            parent_id for parent_id in node["parents_ids"] if parent_id != node["id"]
        ]
        node["children_ids"] = [
            child_id for child_id in node["children_ids"] if child_id != node["id"]
        ]

    # Only nods with empty children_ids are choosable
    for node in nodes:
        node["choosable"] = len(node["children_ids"]) == 0

    return {"nodes": nodes}


def add_node(graph: dict, event: EvenementFiliation):
    nodes = graph["nodes"]
    id_counter = max(node["id"] for node in nodes) + 1 if len(nodes) > 0 else 1
    id_map = {node["value"]: node["id"] for node in nodes}

    commune = event.commune
    cadastres = event.cadastre
    volumes = event.volume
    lots = event.lot

    # Create or get commune node
    if commune not in id_map:
        commune_node = {
            "id": id_counter,
            "type": "commune",
            "parents_ids": [],
            "children_ids": [],
            "value": commune,
            "choosable": False,
            "chosen": False,
            "ref_event": event,
            "origine_id": None,
        }
        nodes.append(commune_node)
        id_map[commune] = id_counter
        id_counter += 1
    commune_id = id_map[commune]

    # Update or create cadastre nodes
    cadastre_ids = []
    for cadastre in cadastres:
        cadastre_value = cadastre.replace("CAD_", "")
        if cadastre_value in id_map:
            cadastre_id = id_map[cadastre_value]
            cadastre_node = next(node for node in nodes if node["id"] == cadastre_id)
            if commune_id not in cadastre_node["parents_ids"]:
                cadastre_node["parents_ids"].append(commune_id)
        else:
            cadastre_node = {
                "id": id_counter,
                "type": "cadastre",
                "parents_ids": [commune_id],
                "children_ids": [],
                "value": cadastre_value,
                "choosable": True,
                "chosen": False,
                "ref_event": event,
                "origine_id": None,
            }
            nodes.append(cadastre_node)
            id_map[cadastre_value] = id_counter
            cadastre_id = id_counter
            id_counter += 1

        cadastre_ids.append(cadastre_id)

        # Update commune's children_ids if not already present
        if cadastre_id not in nodes[commune_id - 1]["children_ids"]:
            nodes[commune_id - 1]["children_ids"].append(cadastre_id)

    if not volumes:
        for lot in lots:
            lot_value = lot.replace("LOT_", "")
            create_new_lot = True
            if lot_value in id_map:
                existing_lot_id = id_map[lot_value]
                existing_lot_node = next(
                    node for node in nodes if node["id"] == existing_lot_id
                )
                existing_cadastre_parents = [
                    p
                    for p in existing_lot_node["parents_ids"]
                    if nodes[p - 1]["type"] == "cadastre"
                ]
                if all(p in cadastre_ids for p in existing_cadastre_parents):
                    create_new_lot = False
                    lot_node = existing_lot_node
                    for cadastre_id in cadastre_ids:
                        if cadastre_id not in lot_node["parents_ids"]:
                            lot_node["parents_ids"].append(cadastre_id)
                        if (
                            existing_lot_id
                            not in nodes[cadastre_id - 1]["children_ids"]
                        ):
                            nodes[cadastre_id - 1]["children_ids"].append(
                                existing_lot_id
                            )

            if create_new_lot:
                lot_node = {
                    "id": id_counter,
                    "type": "lot",
                    "parents_ids": cadastre_ids,
                    "children_ids": [],
                    "value": lot_value,
                    "choosable": True,
                    "chosen": False,
                    "ref_event": event,
                    "origine_id": None,
                }
                nodes.append(lot_node)
                id_map[lot_value] = id_counter
                for cadastre_id in cadastre_ids:
                    nodes[cadastre_id - 1]["children_ids"].append(id_counter)
                id_counter += 1
    else:
        volume_ids = []
        for volume in volumes:
            volume_value = volume.replace("VOL_", "")
            create_new_volume = True
            if volume_value in id_map:
                existing_volume_id = id_map[volume_value]
                existing_volume_node = next(
                    node for node in nodes if node["id"] == existing_volume_id
                )
                existing_cadastre_parents = [
                    p
                    for p in existing_volume_node["parents_ids"]
                    if nodes[p - 1]["type"] == "cadastre"
                ]
                if all(p in cadastre_ids for p in existing_cadastre_parents):
                    create_new_volume = False
                    volume_node = existing_volume_node
                    for cadastre_id in cadastre_ids:
                        if cadastre_id not in volume_node["parents_ids"]:
                            volume_node["parents_ids"].append(cadastre_id)
                        if (
                            existing_volume_id
                            not in nodes[cadastre_id - 1]["children_ids"]
                        ):
                            nodes[cadastre_id - 1]["children_ids"].append(
                                existing_volume_id
                            )
                    volume_ids.append(existing_volume_id)

            if create_new_volume:
                volume_node = {
                    "id": id_counter,
                    "type": "volume",
                    "parents_ids": cadastre_ids,
                    "children_ids": [],
                    "value": volume_value,
                    "choosable": True,
                    "chosen": False,
                    "ref_event": event,
                    "origine_id": None,
                }
                nodes.append(volume_node)
                id_map[volume_value] = id_counter
                for cadastre_id in cadastre_ids:
                    nodes[cadastre_id - 1]["children_ids"].append(id_counter)
                volume_ids.append(id_counter)
                id_counter += 1

        for lot in lots:
            lot_value = lot.replace("LOT_", "")
            create_new_lot = True
            if lot_value in id_map:
                existing_lot_id = id_map[lot_value]
                existing_lot_node = next(
                    node for node in nodes if node["id"] == existing_lot_id
                )
                existing_volume_parents = [
                    p
                    for p in existing_lot_node["parents_ids"]
                    if nodes[p - 1]["type"] == "volume"
                ]
                if all(p in volume_ids for p in existing_volume_parents):
                    create_new_lot = False
                    lot_node = existing_lot_node
                    for volume_id in volume_ids:
                        if volume_id not in lot_node["parents_ids"]:
                            lot_node["parents_ids"].append(volume_id)
                        if existing_lot_id not in nodes[volume_id - 1]["children_ids"]:
                            nodes[volume_id - 1]["children_ids"].append(existing_lot_id)

            if create_new_lot:
                lot_node = {
                    "id": id_counter,
                    "type": "lot",
                    "parents_ids": volume_ids,
                    "children_ids": [],
                    "value": lot_value,
                    "choosable": True,
                    "chosen": False,
                    "ref_event": event,
                    "origine_id": None,
                }
                nodes.append(lot_node)
                id_map[lot_value] = id_counter
                for volume_id in volume_ids:
                    nodes[volume_id - 1]["children_ids"].append(id_counter)
                id_counter += 1

    # Update choosable property
    for node in nodes:
        node["choosable"] = len(node["children_ids"]) == 0

    return graph


def filter_event_not_in_leaf(historique: Historique, leaf: list[EvenementFiliation]):
    not_in_leaf = []
    for event in historique.evenements:
        if event not in leaf:
            not_in_leaf.append(event)
    return not_in_leaf


def get_event_parent_of_leaf(
    leafs: list[EvenementFiliation], not_leaf: list[EvenementFiliation]
):
    parent_leaf = []
    # print detailed leafs

    for parent_event in not_leaf:
        # element with time > to parent_event.time
        younger_no_leafs = [
            leaf for leaf in not_leaf if leaf.temps > parent_event.temps
        ]
        for leaf in leafs + younger_no_leafs:
            if (
                len(parent_event.Liste_elements_changement)
                and "CAD" in parent_event.Liste_elements_changement[0]
            ):  # parent subit cadastre
                child_cadastre = traitement_cadastre(leaf.cadastre)
                # print(leaf.Liste_elements_sujet, leaf.cadastre, leaf.volume, leaf.lot, leaf.Liste_elements_changement)
                if (
                    len(parent_event.Liste_elements_sujet)
                    and "CAD" in parent_event.Liste_elements_sujet[0]
                ):  # parent subit cadastre vers cadastre
                    for cadastre in child_cadastre:
                        if (
                            cadastre in parent_event.Liste_elements_changement
                            and parent_event.commune == leaf.commune
                        ):
                            parent_leaf.append(
                                {
                                    "parent": parent_event,
                                    "parent_value": parent_event.cadastre,
                                    "child": leaf,
                                    "type": "cadastre",
                                    "child_value": cadastre,
                                    "label_edge": "division de cadastre"
                                    if len(parent_event.Liste_elements_changement)
                                    > len(parent_event.Liste_elements_sujet)
                                    else (
                                        "fusion de cadastre"
                                        if len(parent_event.Liste_elements_changement)
                                        != len(parent_event.Liste_elements_sujet)
                                        else "modification de cadastre"
                                    ),
                                }
                            )
                else:
                    for cadastre in child_cadastre:
                        if (
                            cadastre in parent_event.Liste_elements_changement
                            and parent_event.commune == leaf.commune
                        ):
                            parent_leaf.append(
                                {
                                    "parent": parent_event,
                                    "parent_value": parent_event.cadastre,
                                    "child": leaf,
                                    "type": "cadastre",
                                    "child_value": cadastre,
                                    "label_edge": "creation de cadastre "
                                    if len(parent_event.Liste_elements_sujet) == 0
                                    else "etats descriptif de division",  # CHANGER CELON TOUS LES CAS POSSIBLE suppretion de lot ou volume
                                    # CHANGER CELON TOUS LES CAS POSSIBLE suppretion de lot ou volume
                                }
                            )
            if (
                len(parent_event.Liste_elements_changement)
                and "VOL" in parent_event.Liste_elements_changement[0]
            ):
                child_volume = tratement_volume(leaf.volume)
                parent_cadastre = traitement_cadastre(parent_event.cadastre)
                child_cadastre = traitement_cadastre(leaf.cadastre)
                if (
                    len(parent_event.Liste_elements_sujet)
                    and "VOL" in parent_event.Liste_elements_sujet[0]
                ):  # parent subit volume vers volume
                    for volume in child_volume:
                        if (
                            volume in parent_event.Liste_elements_changement
                            and parent_event.commune == leaf.commune
                            and any(
                                [
                                    cadastre in parent_cadastre
                                    for cadastre in child_cadastre
                                ]
                            )
                        ):
                            parent_leaf.append(
                                {
                                    "parent": parent_event,
                                    "parent_value": parent_event.volume,
                                    "child": leaf,
                                    "type": "volume",
                                    "child_value": volume,
                                    "label_edge": "division de volume"
                                    if len(parent_event.Liste_elements_changement)
                                    > len(parent_event.Liste_elements_sujet)
                                    else (
                                        "fusion de volume"
                                        if len(parent_event.Liste_elements_changement)
                                        != len(parent_event.Liste_elements_sujet)
                                        else "modification de volume"
                                    ),
                                }
                            )
                else:
                    for volume in child_volume:
                        if (
                            volume in parent_event.Liste_elements_changement
                            and parent_event.commune == leaf.commune
                            and any(
                                [
                                    cadastre in parent_cadastre
                                    for cadastre in child_cadastre
                                ]
                            )
                        ):
                            parent_leaf.append(
                                {
                                    "parent": parent_event,
                                    "parent_value": parent_event.volume,
                                    "child": leaf,
                                    "type": "volume",
                                    "child_value": volume,
                                    "label_edge": "creation de volume"
                                    if len(parent_event.Liste_elements_sujet) == 0
                                    else "etats descriptif de division",  # CHANGER CELON TOUS LES CAS POSSIBLE suppretion de lot ou volume
                                }
                            )
            if (
                len(parent_event.Liste_elements_changement)
                and "LOT" in parent_event.Liste_elements_changement[0]
            ):
                child_lot = traitement_lot(leaf.lot)
                child_cadastre = traitement_cadastre(leaf.cadastre)
                child_volume = tratement_volume(leaf.volume)
                parent_cadastre = traitement_cadastre(parent_event.cadastre)
                parent_volume = tratement_volume(parent_event.volume)
                if (
                    len(leaf.Liste_elements_changement)
                    and "LOT" in leaf.Liste_elements_changement[0]
                ):  # parent subit lot vers lot TODO: add case suppression de lot
                    for lot in child_lot:
                        if (
                            lot in parent_event.Liste_elements_changement
                            and parent_event.commune == leaf.commune
                            and any(
                                [
                                    cadastre in parent_cadastre
                                    for cadastre in child_cadastre
                                ]
                            )
                        ):
                            if any(
                                [volume in parent_volume for volume in child_volume]
                            ):  # volume non vide pour le parent et l'enfant
                                parent_leaf.append(
                                    {
                                        "parent": parent_event,
                                        "parent_value": parent_event.lot,
                                        "child": leaf,
                                        "type": "lot",
                                        "child_value": lot,
                                        "label_edge": "division de lot"
                                        if len(parent_event.Liste_elements_changement)
                                        > len(parent_event.Liste_elements_sujet)
                                        else (
                                            "fusion de lot"
                                            if len(
                                                parent_event.Liste_elements_changement
                                            )
                                            != len(parent_event.Liste_elements_sujet)
                                            else "modification de lot"
                                        ),
                                    }
                                )
                            elif len(child_volume) == 0:  # Aucune volume pour l'enfant
                                parent_leaf.append(
                                    {
                                        "parent": parent_event,
                                        "parent_value": parent_event.lot,
                                        "child": leaf,
                                        "type": "lot",
                                        "child_value": lot,
                                        "label_edge": "division de lot"
                                        if len(parent_event.Liste_elements_changement)
                                        > len(parent_event.Liste_elements_sujet)
                                        else (
                                            "fusion de lot"
                                            if len(
                                                parent_event.Liste_elements_changement
                                            )
                                            != len(parent_event.Liste_elements_sujet)
                                            else "modification de lot"
                                        ),
                                    }
                                )
    

    return parent_leaf


class NodeIDManager:
    def __init__(self):
        self.node_id_map = {}
        self.next_node_id = 1

    def get_node_id(self, value):
        if value not in self.node_id_map:
            self.node_id_map[value] = self.next_node_id
            self.next_node_id += 1
        return self.node_id_map[value]


def loop_get_event_parent_of_leaf(
    leafs: list[EvenementFiliation], not_leaf: list[EvenementFiliation]
):
    """
    Loop on get_event_parent_of_leaf, by removing elements of not_leaf that are in parent_leaf, until not_leaf stops changing.
    Removes duplicates from all_parent_leaf at every iteration, considering parent_value and child_value.
    Adds a unique origin_id for each parent, event_nodes structure for each element, predecessor_ids, and successors_id.
    Ensures no duplicates in predecessor_ids and successors_id.
    """
    all_parent_leaf = []
    seen = set()  # To keep track of unique parent-child-value combinations
    parent_id_map = {}  # To keep track of unique IDs for each parent
    next_parent_id = 1  # Counter for generating unique parent IDs
    child_to_parent_map = {}  # To keep track of child-parent relationships
    parent_to_child_map = {}  # To keep track of parent-child relationships

    choice_nodes = generate_graph_changement_no_is_sujet_result(leafs)

    while True:
        parent_leaf = get_event_parent_of_leaf(leafs, not_leaf)
        print(len(parent_leaf))
        if not parent_leaf:
            break

        # Add new unique parent-leaf relationships
        for item in parent_leaf:
            key = (
                item["parent"],
                item["child"],
                item["parent_value"],
                item["child_value"],
            )
            if key not in seen:
                seen.add(key)

                # Assign or get the unique ID for the parent
                if item["parent"] not in parent_id_map:
                    parent_id_map[item["parent"]] = next_parent_id
                    next_parent_id += 1

                # Add the origin_id to the item
                item["origin_id"] = parent_id_map[item["parent"]]

                # Create event_nodes structure
                node_manager = NodeIDManager()  # New manager for each item
                item["child_node"] = item["child"]

                # Initialize predecessor_ids and successors_id as sets
                item["predecessor_ids"] = set()
                item["successors_id"] = set()

                # Update child_to_parent_map
                if item["child"] not in child_to_parent_map:
                    child_to_parent_map[item["child"]] = set()
                child_to_parent_map[item["child"]].add(item["origin_id"])

                # Update parent_to_child_map
                if item["parent"] not in parent_to_child_map:
                    parent_to_child_map[item["parent"]] = set()
                parent_to_child_map[item["parent"]].add(item["origin_id"])

                all_parent_leaf.append(item)

        # Remove events from not_leaf that are now parents
        not_leaf = [
            event
            for event in not_leaf
            if event not in [item["parent"] for item in parent_leaf]
        ]

        # Add new parents to leafs for the next iteration
        leafs.extend([item["parent"] for item in parent_leaf])

    #  ALL PARENT HAVE A UNIQUE CHILD, WICH IS THE NEAREST TO THE PARENT
    unique_parent_leaf = {}
    for entry in all_parent_leaf:
        parent_temps = entry["parent"].temps
        if (
            parent_temps not in unique_parent_leaf
            or entry["child"].temps < unique_parent_leaf[parent_temps]["child"].temps
        ):
            unique_parent_leaf[parent_temps] = entry

    # Sort the unique entries by decreasing child.temps,
    all_parent_leaf = sorted(
        unique_parent_leaf.values(), key=lambda x: x["child"].temps, reverse=True
    )

    # Update predecessor_ids and successors_id for all items
    for item in all_parent_leaf:
        if item["parent"] in child_to_parent_map:
            item["predecessor_ids"] = child_to_parent_map[item["parent"]]
        if item["child"] in parent_to_child_map:
            item["successors_id"] = parent_to_child_map[item["child"]]

    # Convert sets to lists for JSON serialization
    for item in all_parent_leaf:
        item["predecessor_ids"] = list(item["predecessor_ids"])
        item["successors_id"] = list(item["successors_id"])

    origines = []
    for item in all_parent_leaf:
        if {
            "id": item["origin_id"],
            "child_node": item["child_node"],
            "predecessor_ids": item["predecessor_ids"],
            "successors_id": item["successors_id"],
            "type_event": item["type"],
            "date_event": item["parent"].date_evemement,
            # "event_nodes": item["event_nodes"],
            "commune": item["parent"].commune,
            "sujet": item["parent"].Liste_elements_sujet,
            "changement": item["parent"].Liste_elements_changement,
            "initial_cadasre": item["parent"].cadastre,
            "initial_volume": item["parent"].volume,
            "initial_lot": item["parent"].lot,
        } not in origines:
            origines.append(
                {
                    "id": item["origin_id"],
                    "sujet": item["parent"].Liste_elements_sujet,
                    "changement": item["parent"].Liste_elements_changement,
                    "initial_cadasre": item["parent"].cadastre,
                    "initial_volume": item["parent"].volume,
                    "initial_lot": item["parent"].lot,
                    "child_node": item["child_node"],
                    "predecessor_ids": item["predecessor_ids"],
                    "successors_id": item["successors_id"],
                    "type_event": item["type"],
                    "date_event": item["parent"].date_evemement,
                    # "event_nodes": item["event_nodes"],
                    "commune": item["parent"].commune,
                }
            )

    # remove useless child_node that successors_id is not []

    # add origine_id to the node with ref_event exist in origines
    for node in choice_nodes["nodes"]:
        for ancestor in origines:
            if ancestor["child_node"] == node["ref_event"]:
                node["origine_id"] = ancestor["id"]
        if node["origine_id"] == None:
            node["origine_id"] = None

    #######################################UPDATE THE NODES WITH NO SUCCESSORS############################################
    # lits of all element with no successors id
    list_no_successors = []
    for node in origines:
        if node["successors_id"] == []:
            list_no_successors.append(node)
    tranlate_id_factor = len(list_no_successors)
    map_predecessor = {}
    # saving the predecessor id of the node with no successors
    for node in list_no_successors:
        map_predecessor[node["id"]] = node["id"] + tranlate_id_factor

    direct_parent = []
    for node in list_no_successors:
        direct_parent.append(
            {
                "id": node["id"],
                "sujet": node["child_node"].Liste_elements_sujet,
                "changement": node["child_node"].Liste_elements_changement,
                "initial_cadasre": node["child_node"].cadastre,
                "initial_volume": node["child_node"].volume,
                "initial_lot": node["child_node"].lot,
                "child_node": node["child_node"],
                "predecessor_ids": [map_predecessor[node["id"]]],
                "successors_id": [],
                "type_event": node["type_event"],
                "date_event": node["child_node"].date_evemement,
                "commune": node["child_node"].commune,
            }
        )

    # update all ids in origin, incrementing all by tranlate_id_factor
    for node in origines:
        node["id"] += tranlate_id_factor
        node["predecessor_ids"] = [
            id + tranlate_id_factor for id in node["predecessor_ids"]
        ]
        node["successors_id"] = [
            id + tranlate_id_factor for id in node["successors_id"]
        ]

    # add successors_id to the node with no successors in origin
    for node in origines:
        if node["successors_id"] == []:
            node["successors_id"] = [node["id"] - tranlate_id_factor]

    # add direct_parent to the origines
    max_id = 0
    for node in origines:
        if node["id"] > max_id:
            max_id = node["id"]
    max_id += 1
    origines = direct_parent + origines
    ################################################## CASE OF EVENTS THAT HAVE NO PRECEDENT EVENTS (ONLY ONE CHANGE EVER), IT WON'T BE IN THE ORIGINES LIST ############################################
    leaf_with_no_origines = []
    add = True
    for event in leafs:
        for norigin in origines:
            if (
                event.Liste_elements_changement == norigin["changement"]
                and event.Liste_elements_sujet == norigin["sujet"]
            ):
                add = False
                break
        if add:
            leaf_with_no_origines.append(
                {
                    "id": max_id,
                    "sujet": event.Liste_elements_sujet,
                    "changement": event.Liste_elements_changement,
                    "initial_cadasre": event.cadastre,
                    "initial_volume": event.volume,
                    "initial_lot": event.lot,
                    "child_node": event,
                    "predecessor_ids": [],
                    "successors_id": [],
                    "type_event": node["type"]
                    if node.get("type")
                    else node["type_event"],
                    "date_event": event.date_evemement,
                    "commune": event.commune,
                }
            )
            max_id += 1
        add = True


    origines = origines + leaf_with_no_origines

    # update origines of nodes in choice_nodes that ref are in leaf_with_no_origines then  remove "ref_event" from the node
    for node in choice_nodes["nodes"]:
        for origin in leaf_with_no_origines:
            if node["ref_event"] == origin["child_node"]:
                node["origine_id"] = origin["id"]
                if len(origin["changement"]) > 0:
                    if "CAD" in origin["changement"][0]:
                        origin["child_node"].cadastre = origin["changement"]
                    if "VOL" in origin["changement"][0]:
                        origin["child_node"].volume = origin["changement"]
                    if "LOT" in origin["changement"][0]:
                        origin["child_node"].lot = origin["changement"]

        del node["ref_event"]

    # remove "child_node" from the origines
    for item in origines:
        item["child_node"] = {
            "cadastre": traitement_cadastre(item["child_node"].cadastre)
            if isinstance(item["child_node"].cadastre, str)
            else ",".join(
                item["child_node"].cadastre
            ),  # THIS ELSE IF FOR THE NODES WE JUST HANDLED FOR THE LEAF_WITH_NO_ORIGINES
            "volume": tratement_volume(item["child_node"].volume)
            if isinstance(item["child_node"].volume, str)
            else ",".join(item["child_node"].volume),
            "lot": traitement_lot(item["child_node"].lot)
            if isinstance(item["child_node"].lot, str)
            else ",".join(item["child_node"].lot),
        }

    # print node
    # for node in choice_nodes["nodes"]:
    #     print(node)

    return {
        # "debug" : all_parent_leaf,
        "nodes": choice_nodes["nodes"],
        "origines": origines,
    }


def add_node(graph: dict, event: EvenementFiliation):
    nodes = graph["nodes"]
    id_counter = max(node["id"] for node in nodes) + 1 if len(nodes) > 0 else 1
    id_map = {node["value"]: node["id"] for node in nodes}

    commune = event.commune
    cadastres = event.cadastre
    volumes = event.volume
    lots = event.lot

    # Create or get commune node
    if commune not in id_map:
        commune_node = {
            "id": id_counter,
            "type": "commune",
            "parents_ids": [],
            "children_ids": [],
            "value": commune,
            "choosable": False,
            "chosen": False,
            "ref_event": event,
            "origine_id": None,
        }
        nodes.append(commune_node)
        id_map[commune] = id_counter
        id_counter += 1
    commune_id = id_map[commune]

    # Update or create cadastre nodes
    cadastre_ids = []
    for cadastre in cadastres:
        cadastre_value = cadastre.replace("CAD_", "")
        if cadastre_value in id_map:
            cadastre_id = id_map[cadastre_value]
            cadastre_node = next(node for node in nodes if node["id"] == cadastre_id)
            if commune_id not in cadastre_node["parents_ids"]:
                cadastre_node["parents_ids"].append(commune_id)
        else:
            cadastre_node = {
                "id": id_counter,
                "type": "cadastre",
                "parents_ids": [commune_id],
                "children_ids": [],
                "value": cadastre_value,
                "choosable": True,
                "chosen": False,
                "ref_event": event,
                "origine_id": None,
            }
            nodes.append(cadastre_node)
            id_map[cadastre_value] = id_counter
            cadastre_id = id_counter
            id_counter += 1

        cadastre_ids.append(cadastre_id)

        # Update commune's children_ids if not already present
        if cadastre_id not in nodes[commune_id - 1]["children_ids"]:
            nodes[commune_id - 1]["children_ids"].append(cadastre_id)

    if not volumes:
        for lot in lots:
            lot_value = lot.replace("LOT_", "")
            create_new_lot = True
            if lot_value in id_map:
                existing_lot_id = id_map[lot_value]
                existing_lot_node = next(
                    node for node in nodes if node["id"] == existing_lot_id
                )
                existing_cadastre_parents = [
                    p
                    for p in existing_lot_node["parents_ids"]
                    if nodes[p - 1]["type"] == "cadastre"
                ]
                if all(p in cadastre_ids for p in existing_cadastre_parents):
                    create_new_lot = False
                    lot_node = existing_lot_node
                    for cadastre_id in cadastre_ids:
                        if cadastre_id not in lot_node["parents_ids"]:
                            lot_node["parents_ids"].append(cadastre_id)
                        if (
                            existing_lot_id
                            not in nodes[cadastre_id - 1]["children_ids"]
                        ):
                            nodes[cadastre_id - 1]["children_ids"].append(
                                existing_lot_id
                            )

            if create_new_lot:
                lot_node = {
                    "id": id_counter,
                    "type": "lot",
                    "parents_ids": cadastre_ids,
                    "children_ids": [],
                    "value": lot_value,
                    "choosable": True,
                    "chosen": False,
                    "ref_event": event,
                    "origine_id": None,
                }
                nodes.append(lot_node)
                id_map[lot_value] = id_counter
                for cadastre_id in cadastre_ids:
                    if id_counter not in nodes[cadastre_id - 1]["children_ids"]:
                        nodes[cadastre_id - 1]["children_ids"].append(id_counter)
                id_counter += 1
    else:
        volume_ids = []
        for volume in volumes:
            volume_value = volume.replace("VOL_", "")
            create_new_volume = True
            if volume_value in id_map:
                existing_volume_id = id_map[volume_value]
                existing_volume_node = next(
                    node for node in nodes if node["id"] == existing_volume_id
                )
                existing_cadastre_parents = [
                    p
                    for p in existing_volume_node["parents_ids"]
                    if nodes[p - 1]["type"] == "cadastre"
                ]
                if all(p in cadastre_ids for p in existing_cadastre_parents):
                    create_new_volume = False
                    volume_node = existing_volume_node
                    for cadastre_id in cadastre_ids:
                        if cadastre_id not in volume_node["parents_ids"]:
                            volume_node["parents_ids"].append(cadastre_id)
                        if (
                            existing_volume_id
                            not in nodes[cadastre_id - 1]["children_ids"]
                        ):
                            nodes[cadastre_id - 1]["children_ids"].append(
                                existing_volume_id
                            )
                    volume_ids.append(existing_volume_id)

            if create_new_volume:
                volume_node = {
                    "id": id_counter,
                    "type": "volume",
                    "parents_ids": cadastre_ids,
                    "children_ids": [],
                    "value": volume_value,
                    "choosable": True,
                    "chosen": False,
                    "ref_event": event,
                    "origine_id": None,
                }
                nodes.append(volume_node)
                id_map[volume_value] = id_counter
                for cadastre_id in cadastre_ids:
                    if id_counter not in nodes[cadastre_id - 1]["children_ids"]:
                        nodes[cadastre_id - 1]["children_ids"].append(id_counter)
                volume_ids.append(id_counter)
                id_counter += 1

        for lot in lots:
            lot_value = lot.replace("LOT_", "")
            create_new_lot = True
            if lot_value in id_map:
                existing_lot_id = id_map[lot_value]
                existing_lot_node = next(
                    node for node in nodes if node["id"] == existing_lot_id
                )
                existing_volume_parents = [
                    p
                    for p in existing_lot_node["parents_ids"]
                    if nodes[p - 1]["type"] == "volume"
                ]
                if all(p in volume_ids for p in existing_volume_parents):
                    create_new_lot = False
                    lot_node = existing_lot_node
                    for volume_id in volume_ids:
                        if volume_id not in lot_node["parents_ids"]:
                            lot_node["parents_ids"].append(volume_id)
                        if existing_lot_id not in nodes[volume_id - 1]["children_ids"]:
                            nodes[volume_id - 1]["children_ids"].append(existing_lot_id)

            if create_new_lot:
                lot_node = {
                    "id": id_counter,
                    "type": "lot",
                    "parents_ids": volume_ids,
                    "children_ids": [],
                    "value": lot_value,
                    "choosable": True,
                    "chosen": False,
                    "ref_event": event,
                    "origine_id": None,
                }
                nodes.append(lot_node)
                id_map[lot_value] = id_counter
                for volume_id in volume_ids:
                    if id_counter not in nodes[volume_id - 1]["children_ids"]:
                        nodes[volume_id - 1]["children_ids"].append(id_counter)
                id_counter += 1

    # Remove any invalid parent-child relationships
    for node in nodes:
        node["parents_ids"] = [
            pid for pid in node["parents_ids"] if nodes[pid - 1]["type"] != node["type"]
        ]
        node["children_ids"] = [
            cid
            for cid in node["children_ids"]
            if nodes[cid - 1]["type"] != node["type"]
        ]

    # Update choosable property
    for node in nodes:
        node["choosable"] = len(node["children_ids"]) == 0

    return graph


def generate_graphe_from_formalite(list_evenement: list[list[dict]], logger) -> dict:
    """
    Generate a graph of the changement no is sujet result

    Args:
        list_evenement (list[dict]): The list of the evenement

    exemple:
    [

        {
            "date": "25/05/2024",
            "tableau_immeuble_mere_fille": [
                {
                    "commune_mere": "PARIS 17EME",
                    "designation_cadastrale_mere": "CD50",
                    "volume_mere": "190",
                    "lot_mere": "1",
                    "commune_fille": "",
                    "designation_cadastrale_fille": "CD50",
                    "volume_fille": "190",
                    "lot_fille": "2"
                }
            ]
        },...
    ]

    Returns:
        dict: The graph of the changement no is sujet result

    """

    if len(list_evenement) == 0:
        return {"nodes": [], "origines": []}

    try:
        # keeep the tableau_immeuble
        tableau_immeuble_tmp = [tableau[-1] for tableau in list_evenement]
        tableau_immeuble = []
        for tableau in tableau_immeuble_tmp:
            if tableau not in tableau_immeuble:
                tableau_immeuble.extend(tableau)

        # keep the tableau_immeuble_mere_fille

        list_evenement = [tableau[:-1] for tableau in list_evenement]

        tmp_list_evement = []

        for filiation in list_evenement:
            if filiation != []:
                tmp_list_evement.append(filiation)

        list_evenement = tmp_list_evement

        if list_evenement == []:
            nodes = {"nodes": [], "origines": []}
            for immeuble in tableau_immeuble:
                event = EvenementFiliation(
                    date_evemement="",
                    Liste_elements_sujet=[],
                    Liste_elements_changement=[],
                    cadastre=immeuble["cadastres"],
                    volume=immeuble["volumes"],
                    lot=immeuble["lots"],
                    commune=immeuble["commune"],
                    temps=1,
                    type_element_sujet="",
                    type_element_changement="",
                )
                nodes = add_node(nodes, event)
            for node in nodes["nodes"]:
                if "ref_event" in node.keys():
                    del node["ref_event"]
            return nodes

        ### if here, the immeuble_mere_fille is not empty, then we can generate the graph and add immeuble
        # order each list of evenement by date desc

        for evenement in list_evenement:
            evenement = sorted(
                evenement,
                key=lambda x: datetime.strptime(x["date"], "%d/%m/%Y"),
                reverse=True,
            )

        # order the list of evenement by first date

        list_evenement = sorted(
            list_evenement,
            key=lambda x: datetime.strptime(x[0]["date"], "%d/%m/%Y"),
            reverse=True,
        )

        # create one list of all evenement
        list_evenement_all = []
        for evenement in list_evenement:
            list_evenement_all.extend(evenement)

        # create the historique
        historique = creation_historique(list_evenement_all)

        # #get the leaf of the tree
        # leaf = get_event_changement_not_sujet_futur(historique)

        not_leaf = filter_event_not_in_leaf(
            historique, get_event_changement_not_sujet_futur(historique)
        )

        # print([event.Liste_elements_changement for event in not_leaf])

        leafs = get_event_changement_not_sujet_futur(historique)
        # print([event.Liste_elements_changement for event in leafs])
        # for leaf in leafs:
        #     print(leaf.Liste_elements_changement, (leaf.cadastre, leaf.volume, leaf.lot), "--->", leaf.Liste_elements_sujet)

        # generate the graph
        parent_leaf = loop_get_event_parent_of_leaf(leafs, not_leaf)

        if len(tableau_immeuble) > 0:
            # add the immeuble to the graph
            nodes = parent_leaf["nodes"]
            for immeuble in tableau_immeuble:
                event = EvenementFiliation(
                    date_evemement="",
                    Liste_elements_sujet=[],
                    Liste_elements_changement=[],
                    cadastre=immeuble["cadastres"],
                    volume=immeuble["volumes"],
                    lot=immeuble["lots"],
                    commune=immeuble["commune"],
                    temps=1,
                    type_element_sujet="",
                    type_element_changement="",
                )
                nodes = add_node(parent_leaf, event)

            # remove ref_event from the node if it exist
            for node in nodes["nodes"]:
                if "ref_event" in node.keys():
                    del node["ref_event"]

            parent_leaf["nodes"] = nodes["nodes"]

        # generate the graph
        return parent_leaf
    except Exception as e:
        import traceback

        print(traceback.format_exc())
        logger.error(f"Error in generate_graphe_from_formalite: {e}")
        return {"nodes": [], "origines": []}
Yet i got this ouput  : 

{
    "nodes": [
        {
            "id": 1,
            "type": "commune",
            "parents_ids": [],
            "children_ids": [
                2,
                3,
                4,
                5
            ],
            "value": "LYON 3EME",
            "choosable": false,
            "chosen": false,
            "origine_id": 11
        },
        {
            "id": 2,
            "type": "cadastre",
            "parents_ids": [
                1
            ],
            "children_ids": [],
            "value": "AY126",
            "choosable": true,
            "chosen": false,
            "origine_id": 11
        },
        {
            "id": 3,
            "type": "cadastre",
            "parents_ids": [
                1
            ],
            "children_ids": [],
            "value": "AY127",
            "choosable": true,
            "chosen": false,
            "origine_id": 11
        },
        {
            "id": 4,
            "type": "cadastre",
            "parents_ids": [
                1
            ],
            "children_ids": [],
            "value": "AY170",
            "choosable": true,
            "chosen": false,
            "origine_id": 12
        },
        {
            "id": 5,
            "type": "cadastre",
            "parents_ids": [
                1
            ],
            "children_ids": [],
            "value": "AY171",
            "choosable": true,
            "chosen": false,
            "origine_id": 12
        },
        {
            "id": 6,
            "type": "commune",
            "parents_ids": [],
            "children_ids": [
                7
            ],
            "value": "VILLEURBANNE",
            "choosable": false,
            "chosen": false,
            "origine_id": 13
        },
        {
            "id": 7,
            "type": "cadastre",
            "parents_ids": [
                6
            ],
            "children_ids": [
                8
            ],
            "value": "BB304",
            "choosable": false,
            "chosen": false,
            "origine_id": 13
        },
        {
            "id": 8,
            "type": "lot",
            "parents_ids": [
                7
            ],
            "children_ids": [],
            "value": "696",
            "choosable": true,
            "chosen": false,
            "origine_id": 13
        },
        {
            "id": 9,
            "type": "commune",
            "parents_ids": [],
            "children_ids": [
                10,
                12,
                13,
                14,
                15,
                16,
                17,
                18,
                19,
                20,
                21
            ],
            "value": "LYON 7EME",
            "choosable": false,
            "chosen": false,
            "origine_id": 5
        },
        {
            "id": 10,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [
                11
            ],
            "value": "CD34",
            "choosable": false,
            "chosen": false,
            "origine_id": 5
        },
        {
            "id": 11,
            "type": "volume",
            "parents_ids": [
                10,
                12,
                13,
                14,
                15,
                16
            ],
            "children_ids": [],
            "value": "1",
            "choosable": true,
            "chosen": false,
            "origine_id": 5
        },
        {
            "id": 12,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [
                11
            ],
            "value": "CD231",
            "choosable": false,
            "chosen": false,
            "origine_id": 5
        },
        {
            "id": 13,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [
                11
            ],
            "value": "CD239",
            "choosable": false,
            "chosen": false,
            "origine_id": 5
        },
        {
            "id": 14,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [
                11
            ],
            "value": "CD240",
            "choosable": false,
            "chosen": false,
            "origine_id": 5
        },
        {
            "id": 15,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [
                11
            ],
            "value": "CD241",
            "choosable": false,
            "chosen": false,
            "origine_id": 5
        },
        {
            "id": 16,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [
                11
            ],
            "value": "CD243",
            "choosable": false,
            "chosen": false,
            "origine_id": 5
        },
        {
            "id": 17,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [],
            "value": "CD233",
            "choosable": true,
            "chosen": false,
            "origine_id": null
        },
        {
            "id": 18,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [],
            "value": "CD234",
            "choosable": true,
            "chosen": false,
            "origine_id": null
        },
        {
            "id": 19,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [],
            "value": "CD235",
            "choosable": true,
            "chosen": false,
            "origine_id": null
        },
        {
            "id": 20,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [],
            "value": "CD236",
            "choosable": true,
            "chosen": false,
            "origine_id": null
        },
        {
            "id": 21,
            "type": "cadastre",
            "parents_ids": [
                9
            ],
            "children_ids": [],
            "value": "CD242",
            "choosable": true,
            "chosen": false,
            "origine_id": null
        }
    ],
    "origines": [
        {
            "id": 1,
            "sujet": [
                "LOT_101",
                "LOT_102",
                "LOT_103",
                "LOT_104"
            ],
            "changement": [
                "VOL_1"
            ],
            "initial_cadasre": "CD 34, CD 231, CD 239 \u00e0 241, CD 243",
            "initial_volume": "1",
            "initial_lot": "101 \u00e0 104",
            "child_node": {
                "cadastre": [
                    "CAD_CD34",
                    "CAD_CD231",
                    "CAD_CD239",
                    "CAD_CD240",
                    "CAD_CD241",
                    "CAD_CD243"
                ],
                "volume": [
                    "VOL_1"
                ],
                "lot": [
                    "LOT_101",
                    "LOT_102",
                    "LOT_103",
                    "LOT_104"
                ]
            },
            "predecessor_ids": [
                5
            ],
            "successors_id": [],
            "type_event": "cadastre",
            "date_event": "12/04/2011",
            "commune": "LYON 7EME"
        },
        {
            "id": 3,
            "sujet": [
                "LOT_101",
                "LOT_102",
                "LOT_103",
                "LOT_104"
            ],
            "changement": [
                "VOL_1"
            ],
            "initial_cadasre": "CD 34, CD 231, CD 239 \u00e0 241, CD 243",
            "initial_volume": "1",
            "initial_lot": "101 \u00e0 104",
            "child_node": {
                "cadastre": [
                    "CAD_CD34",
                    "CAD_CD231",
                    "CAD_CD239",
                    "CAD_CD240",
                    "CAD_CD241",
                    "CAD_CD243"
                ],
                "volume": [
                    "VOL_1"
                ],
                "lot": [
                    "LOT_101",
                    "LOT_102",
                    "LOT_103",
                    "LOT_104"
                ]
            },
            "predecessor_ids": [
                7
            ],
            "successors_id": [],
            "type_event": "cadastre",
            "date_event": "12/04/2011",
            "commune": "LYON 7EME"
        },
        {
            "id": 4,
            "sujet": [
                "LOT_101",
                "LOT_102",
                "LOT_103",
                "LOT_104"
            ],
            "changement": [
                "VOL_1"
            ],
            "initial_cadasre": "CD 34, CD 231, CD 239 \u00e0 241, CD 243",
            "initial_volume": "1",
            "initial_lot": "101 \u00e0 104",
            "child_node": {
                "cadastre": [
                    "CAD_CD34",
                    "CAD_CD231",
                    "CAD_CD239",
                    "CAD_CD240",
                    "CAD_CD241",
                    "CAD_CD243"
                ],
                "volume": [
                    "VOL_1"
                ],
                "lot": [
                    "LOT_101",
                    "LOT_102",
                    "LOT_103",
                    "LOT_104"
                ]
            },
            "predecessor_ids": [
                8
            ],
            "successors_id": [],
            "type_event": "cadastre",
            "date_event": "12/04/2011",
            "commune": "LYON 7EME"
        },
        {
            "id": 5,
            "sujet": [
                "LOT_101",
                "LOT_102",
                "LOT_103",
                "LOT_104"
            ],
            "changement": [
                "VOL_1"
            ],
            "initial_cadasre": "CD 34, CD 231, CD 239 \u00e0 241, CD 243",
            "initial_volume": "1",
            "initial_lot": "101 \u00e0 104",
            "child_node": {
                "cadastre": [
                    "CAD_CD34",
                    "CAD_CD231",
                    "CAD_CD239",
                    "CAD_CD240",
                    "CAD_CD241",
                    "CAD_CD243"
                ],
                "volume": [
                    "VOL_1"
                ],
                "lot": [
                    "LOT_101",
                    "LOT_102",
                    "LOT_103",
                    "LOT_104"
                ]
            },
            "predecessor_ids": [
                9
            ],
            "successors_id": [],
            "type_event": "cadastre",
            "date_event": "12/04/2011",
            "commune": "LYON 7EME"
        },
        {
            "id": 5,
            "sujet": [
                "LOT_1",
                "LOT_2"
            ],
            "changement": [
                "CAD_CD243",
                "CAD_CD244"
            ],
            "initial_cadasre": "CD 243 \u00e0 244",
            "initial_volume": "",
            "initial_lot": "1 \u00e0 2",
            "child_node": {
                "cadastre": [
                    "CAD_CD34",
                    "CAD_CD231",
                    "CAD_CD239",
                    "CAD_CD240",
                    "CAD_CD241",
                    "CAD_CD243"
                ],
                "volume": [
                    "VOL_1"
                ],
                "lot": [
                    "LOT_101",
                    "LOT_102",
                    "LOT_103",
                    "LOT_104"
                ]
            },
            "predecessor_ids": [
                6
            ],
            "successors_id": [
                1
            ],
            "type_event": "cadastre",
            "date_event": "24/07/2009",
            "commune": "LYON 7EME"
        },
        {
            "id": 7,
            "sujet": [
                "CAD_CD163"
            ],
            "changement": [
                "CAD_CD238",
                "CAD_CD239",
                "CAD_CD240",
                "CAD_CD241"
            ],
            "initial_cadasre": "CD 163",
            "initial_volume": "",
            "initial_lot": "",
            "child_node": {
                "cadastre": [
                    "CAD_CD34",
                    "CAD_CD231",
                    "CAD_CD239",
                    "CAD_CD240",
                    "CAD_CD241",
                    "CAD_CD243"
                ],
                "volume": [
                    "VOL_1"
                ],
                "lot": [
                    "LOT_101",
                    "LOT_102",
                    "LOT_103",
                    "LOT_104"
                ]
            },
            "predecessor_ids": [],
            "successors_id": [
                3
            ],
            "type_event": "cadastre",
            "date_event": "23/07/2009",
            "commune": "LYON 7EME"
        },
        {
            "id": 8,
            "sujet": [
                "CAD_CD230"
            ],
            "changement": [
                "CAD_CD231",
                "CAD_CD232",
                "CAD_CD233",
                "CAD_CD234",
                "CAD_CD235",
                "CAD_CD236",
                "CAD_CD237"
            ],
            "initial_cadasre": "CD 230",
            "initial_volume": "",
            "initial_lot": "",
            "child_node": {
                "cadastre": [
                    "CAD_CD34",
                    "CAD_CD231",
                    "CAD_CD239",
                    "CAD_CD240",
                    "CAD_CD241",
                    "CAD_CD243"
                ],
                "volume": [
                    "VOL_1"
                ],
                "lot": [
                    "LOT_101",
                    "LOT_102",
                    "LOT_103",
                    "LOT_104"
                ]
            },
            "predecessor_ids": [
                10
            ],
            "successors_id": [
                4
            ],
            "type_event": "cadastre",
            "date_event": "23/07/2009",
            "commune": "LYON 7EME"
        },
        {
            "id": 9,
            "sujet": [
                "CAD_CD230"
            ],
            "changement": [
                "CAD_CD231",
                "CAD_CD232",
                "CAD_CD233",
                "CAD_CD234",
                "CAD_CD235",
                "CAD_CD236",
                "CAD_CD237",
                "CAD_CD242"
            ],
            "initial_cadasre": "CD 230",
            "initial_volume": "",
            "initial_lot": "",
            "child_node": {
                "cadastre": [
                    "CAD_CD34",
                    "CAD_CD231",
                    "CAD_CD239",
                    "CAD_CD240",
                    "CAD_CD241",
                    "CAD_CD243"
                ],
                "volume": [
                    "VOL_1"
                ],
                "lot": [
                    "LOT_101",
                    "LOT_102",
                    "LOT_103",
                    "LOT_104"
                ]
            },
            "predecessor_ids": [
                10
            ],
            "successors_id": [
                5
            ],
            "type_event": "cadastre",
            "date_event": "23/07/2009",
            "commune": "LYON 7EME"
        },
        {
            "id": 6,
            "sujet": [
                "CAD_CD33"
            ],
            "changement": [
                "CAD_CD243",
                "CAD_CD244"
            ],
            "initial_cadasre": "CD 33",
            "initial_volume": "",
            "initial_lot": "",
            "child_node": {
                "cadastre": [
                    "CAD_CD243",
                    "CAD_CD244"
                ],
                "volume": [],
                "lot": [
                    "LOT_1",
                    "LOT_2"
                ]
            },
            "predecessor_ids": [],
            "successors_id": [
                5
            ],
            "type_event": "cadastre",
            "date_event": "23/07/2009",
            "commune": "LYON 7EME"
        },
        {
            "id": 10,
            "sujet": [
                "CAD_CD31",
                "CAD_CD32",
                "CAD_CD35",
                "CAD_CD36",
                "CAD_CD37",
                "CAD_CD38",
                "CAD_CD164",
                "CAD_CD165"
            ],
            "changement": [
                "CAD_CD230"
            ],
            "initial_cadasre": "CD 31 \u00e0 32, CD 35 \u00e0 38, CD 164 \u00e0 165",
            "initial_volume": "",
            "initial_lot": "",
            "child_node": {
                "cadastre": [
                    "CAD_CD230"
                ],
                "volume": [],
                "lot": []
            },
            "predecessor_ids": [],
            "successors_id": [
                9
            ],
            "type_event": "cadastre",
            "date_event": "23/07/2009",
            "commune": "LYON 7EME"
        },
        {
            "id": 11,
            "sujet": [
                "CAD_AY51"
            ],
            "changement": [
                "CAD_AY126",
                "CAD_AY127"
            ],
            "initial_cadasre": "AY 51",
            "initial_volume": "",
            "initial_lot": "",
            "child_node": {
                "cadastre": "CAD_AY126,CAD_AY127",
                "volume": [],
                "lot": []
            },
            "predecessor_ids": [],
            "successors_id": [],
            "type_event": "cadastre",
            "date_event": "27/06/2005",
            "commune": "LYON 3EME"
        },
        {
            "id": 12,
            "sujet": [
                "CAD_AY159"
            ],
            "changement": [
                "CAD_AY170",
                "CAD_AY171"
            ],
            "initial_cadasre": "AY 159",
            "initial_volume": "",
            "initial_lot": "",
            "child_node": {
                "cadastre": "CAD_AY170,CAD_AY171",
                "volume": [],
                "lot": []
            },
            "predecessor_ids": [],
            "successors_id": [],
            "type_event": "cadastre",
            "date_event": "24/11/2010",
            "commune": "LYON 3EME"
        },
        {
            "id": 13,
            "sujet": [
                "LOT_635",
                "LOT_636",
                "LOT_637",
                "LOT_638",
                "LOT_639",
                "LOT_640",
                "LOT_641",
                "LOT_642",
                "LOT_643",
                "LOT_644",
                "LOT_645",
                "LOT_646",
                "LOT_647",
                "LOT_648",
                "LOT_649",
                "LOT_650",
                "LOT_651",
                "LOT_652",
                "LOT_653",
                "LOT_654",
                "LOT_655",
                "LOT_656",
                "LOT_657",
                "LOT_658",
                "LOT_659"
            ],
            "changement": [
                "LOT_696"
            ],
            "initial_cadasre": "BB 304",
            "initial_volume": "",
            "initial_lot": "635 \u00e0 659",
            "child_node": {
                "cadastre": [
                    "CAD_BB304"
                ],
                "volume": [],
                "lot": "LOT_696"
            },
            "predecessor_ids": [],
            "successors_id": [],
            "type_event": "cadastre",
            "date_event": "29/07/2009",
            "commune": "VILLEURBANNE"
        }
    ]
}

There is not supposed to be duplicates same events in origin list, take your time reading all of this, and find out how can i prevent this duplucates to happen, which modification should i do ?