]> git.immae.eu Git - perso/Denise/oms.git/blobdiff - gestion_donnees.py
Ajout d'un message si aucune courbe n'est tracée.
[perso/Denise/oms.git] / gestion_donnees.py
index ea1f2593f625953852cf2e8655f83412cef60102..6911320ecbc1309072bff8883ae282e4a9bb5da7 100644 (file)
@@ -2,8 +2,9 @@
 # -*- coding: utf-8 -*-
 
 from configuration import CONFIG,DEFAUT
-from gestion_erreurs import *
-from gestion_couleurs import *
+from gestion_erreurs import debug, warning, erreur, initialise_erreurs
+from gestion_couleurs import rgb_vers_tuple, tuple_vers_rgb
+from gestion_unites import choix_unite
 import datetime
 import json
 import unidecode
@@ -20,6 +21,7 @@ def convertit_jours_vers_python(chaine,liste_err):
     Si un des caractères n'est ni un nombre, ni une lettre "autorisée" ni une espace,
     on affiche un warning et on ignore ce caractère
     """
+#    debug("conversion de "+chaine+" vers un nb de jours",liste_err)
     chainenombre = ""
     agejours = 0.
     for lettre in chaine:
@@ -44,15 +46,17 @@ def convertit_jours_vers_python(chaine,liste_err):
                 chainenombre = ""
             elif lettre != ' ':
                 # autre caractère : bizarre ?
-                warning("convertit_jour_vers_python : caractère invalide : "+lettre,liste_err)
+                warning("problème à la conversion de "+chaine+". Caractère invalide : "+lettre,liste_err)
     # à la fin s'il reste qqch on le garde dans les jours
     if chainenombre != "":
         agejours += int(chainenombre)
     if agejours<0:
         warning("L'âge est négatif !",liste_err)
         agejours = 0
+#    debug("On a convertit ! Résultat : "+str(agejours),liste_err)
     return agejours
 
+# python -> json
 def convertit_age_vers_texte(nombre):
     """ convertit un nombre de jours en un truc plus lisible en mois, années, jours
     et renvoie une chaîne sous la forme 3a2m1j par exemple"""
@@ -69,8 +73,19 @@ def convertit_age_vers_texte(nombre):
     if jours>0 or  nombre ==0: # si c'est la naissance, faut beien écrire 0j quand même
         chaine += str(jours)+"j"
     return chaine
+
 ##########################
     
+# fonction qui calcule "auto" le maxi du graphique en fonction du max
+def calcule_max_graphique(l_jours):
+    """ calcule l'age maxi sur le graphique"""
+    if l_jours == []:
+        return CONFIG["jours_defaut_donneesvides"]
+    else:
+        jour_maxi = max(l_jours)# pas la peine d'aller très au delà du jour max
+        jour_maxi = int(jour_maxi* 1.2)+3 # on rajoute un peu
+        return jour_maxi
+    
 
 def simplifie_nom(chaine):
     """ simplifie le nom chaine afin d'en faire une extension
@@ -83,52 +98,64 @@ def simplifie_nom(chaine):
     chaine2 =  unidecode.unidecode(chaine2)
     return chaine2[:15]
 
-def convertit_poids_vers_python(chaine,liste_err):
-    """ convertit une chaine vers un float qui est le poids.
-    On gère notamment la virgule, et on enlève les espaces
-    Un poids invalide -> on renvoie 0 avec un warning"""
+def convertit_donnee_vers_python(chaine,typedonnee,liste_err):
+    """ convertit une chaine vers un float qui est le type donnee voulu.
+    La virgule peut être . ou , et on vire d'éventuels espaces.
+    Taille invalide : on renvoie 0 avec un warning.
+    Si la chaine est en fait déjà au format float, on laisse tel quel"""
+    if type(chaine) == float:
+        return chaine
     chaine2 = chaine.replace(",",".")
     chaine2 = chaine2.replace(" ","")
+    
 
     try:
-        poids = float(chaine2)
+        donnee = float(chaine2)
     except:
-        warning("Poids impossible à lire : "+chaine,liste_err)
-        poids = 0
-    if not( 0<=poids<CONFIG["poids_maxi"]):
-        warning("Poids incohérent : "+str(poids),liste_err)
-        poids = 0
-    return poids
+        warning(typedonnee+" impossible à lire : "+chaine,liste_err)
+        donnee = 0
+        
+    # Pour le poids, un cas particulier
+    if typedonnee == "poids" and donnee > CONFIG["poids_maxi_conversion"]:
+        donnee = donnee/1000 # conversion en grammes
+    if not( 0<=donnee<CONFIG[typedonnee+"_maxi"]):
+        warning(typedonnee+" incohérent(e) : "+str(donnee),liste_err)
+        donnee = 0
+    return donnee    
 
-#def convertit_poids_vers_texte(poids):
-#    """ convertit un poids vers du texte. Rien à dire là pour l'instant """
-#    return str(poids)
 
 #########################
 
+# web -> python
 def convertit_date_vers_python(chaine,liste_err):
     """ prend une chaine comme renvoyée par un champ de formulaire date
     aaaa-mm-jj et en fait une date python
-    renvoie "" si ne marche pas"""
-    liste = chaine.split("-")
+    renvoie "" si ne marche pas.
+    Si jamais la date est au format avec des / ça devrait passer aussi."""
+    if "/" in chaine:
+        liste = chaine.split("/")
+    else:
+        liste = chaine.split("-")
     if len(liste) != 3:
         warning("La date : "+chaine+" est invalide !",liste_err)
         return ""
     else:
+        debug("Conversion de la date "+chaine+". Découpage : "+str(liste),liste_err)
         try:
             date = datetime.date(int(liste[0]),int(liste[1]),int(liste[2]))
         except:
             date = ""
-            warning("Impossible de lire la date "+chaine,liste_err)
+            warning("Impossible de lire la date "+chaine+". Format accepté : aaaa-mm-jj",liste_err)
         return date
-    
+  
+# python -> json
 def convertit_date_vers_texte(date):
     """ convertit une date python en format texte aaaa-mm-jj"""
     if date == "":
         return ""
     else:
-        return (str(date.year)+"-"+str(date.month)+"-"+str(date.day))
-     
+        #return (str(date.year)+"-"+str(date.month)+"-"+str(date.day))
+        return str(date)
 
 def delta_date(date1,datenaissance):
     """ renvoie le nombre de jours (entier) entre date1 et datenaissance format "datetime"
@@ -136,141 +163,251 @@ def delta_date(date1,datenaissance):
     d = date1 - datenaissance
     jours = d.days
     if jours<0:
-        erreur_continue("La différence entre les dates est négative... :/")
+        warning("La différence entre les dates est négative... :/")
         return -1
     return jours
 
 
-########### Fonctions qui gèretn les données
 
+################### On regroupe tout ce qui gère les données en une fonction
+    
+def web_vers_python(data,liste_err, court=False):
+    """ prend en argument le dictionnaire de requête et renvoie la config, et les
+    tableaux de données
+    court : si True est précisé, on ne met que le nom dans la config (enfant
+    additionnel)"""
+    
+    # Régler la configuration
+    config = gere_configuration(data,liste_err, court)    
+    
+    # récupérer les données
+    listes_jours = {}
+    listes_donnees = {}
+    for typed in CONFIG["liste_typedonnees"]:
+        listes_jours[typed],listes_donnees[typed] = gere_donnees(data,config["naissance"],typed,liste_err)
+
+    # Si on veut extrapoler au-delà du jour maxi, on adapte
+
+    # Si on a choisi la même échelle de données
+    if config.get("memechelle") == "oui":
+        config["non_sauve"]["maxi"] = calcule_max_graphique([j for lj in listes_jours.values() for j in lj])
+        # En cas d'extrapolation, on prend le maxi
+        if config["non_sauve"]["calculextradata_type"] !="" and config["non_sauve"]["calculextradata_age"]>config["non_sauve"]["maxi"]:
+            config["non_sauve"]["maxi"] = int(config["non_sauve"]["calculextradata_age"]) +1
+        config["non_sauve"]["unite"] = choix_unite(config["non_sauve"]["maxi"])
+    
+    return (config,listes_jours,listes_donnees)
+
+
+
+########### Fonctions qui gèretn les données web vers python
+
+def gere_checkbox(chaine):
+    """ prend en arg une chaine, et renvoie "oui" si c'est "on" (sortie de la checkbox)
+    et chaîne vide si n'importe quoi d'autre"""
+    if chaine == "on":
+        return "oui"
+    else:
+        return ""
+
+def gere_symbole(chaine):
+    """ prend en arg une chaîne genre "o", ">" et vérifie si c'est un symbole valide.
+    Renvoie ce symbole-là ou le défaut"""
+    if chaine in CONFIG["liste_symboles"]:
+        return chaine
+    else:
+        return DEFAUT["symbole"]
 
-def gere_configuration(data,liste_err):
+def gere_configuration(data,liste_err, court=False):
     """ prend en argument le dictionnaire de requête (configuration imparfaite), et
     construit le dictionnaire de configuration qui va bien.
-    Vérifie que chaque entrée est cohérente évidemment."""
-    configuration = {}
+    Vérifie que chaque entrée est cohérente évidemment.
+    court : si mis à True, on ne met que le nom dans la configuraion,
+    ainsi que la date de naissance et le sexe"""
+    # Initialisation
+    configuration = {"non_sauve": {}}
     
     # Pour le nom, osef qu'il soit vide
     nom = data.get("nom","")
     # Par contre s'il est trop long on le tronque   
     configuration["nom"] = nom[:CONFIG["longueur_max_nom_bebe"]]
-        
+
+    naissance = data.get("naissance","")
+    if naissance !="":
+        naissance = convertit_date_vers_python(naissance,liste_err)
+    configuration["naissance"] = naissance
+    
     sexe = data.get("sexe","")
     if not (sexe in ["F","M","N"]):
-        warning("Le sexe de l'enfant est invalide ! "+sexe,liste_err)
+        warning("Le sexe de l'enfant est invalide. "+sexe,liste_err)
         sexe = "N"
     configuration["sexe"] = sexe
+   
+    if not(court):
+              
+        prematurite = data.get("prematurite","")
+        j = convertit_jours_vers_python(prematurite,liste_err)
+        configuration["prematurite"] = convertit_age_vers_texte(j)
+        
+        configuration["agecorrige"] = gere_checkbox(data.get("agecorrige",""))
+        
+        # Type de courbe. Au pire on met P
+        tyc = data.get("typecourbe","")
+        if not (tyc in ["P","Z"]):
+            tyc = "P"
+        configuration["typecourbe"] = tyc
+        
+        # unité
+        unite = data.get("unite","")
+        if not (unite in CONFIG["liste_unites"]):
+            unite = ""
+            #warning("L'unité "+unite+" n'est pas reconnue !",liste_err)
+        configuration["unite"] = unite
     
-    naissance = data.get("naissance","")
-    if naissance !="":
-        naissance = convertit_date_vers_python(naissance,liste_err)
-    configuration["naissance"] = naissance
+        # grille
+        configuration["grille"] = gere_checkbox(data.get("grille",""))
     
-    # Type de courbe. Au pire on met P
-    tyc = data.get("typecourbe","")
-    if not (tyc in ["P","Z"]):
-        tyc = "P"
-    configuration["typecourbe"] = tyc
+        # tracer ou non les courbes vides
+        configuration["tracevide"] = gere_checkbox(data.get("tracevide",""))
+            
+        # Même échelle sur tous les graphiques
+        configuration["memechelle"] = gere_checkbox(data.get("memechelle",""))
     
-    # unité
-    unite = data.get("unite","")
-
-    if not (unite in CONFIG["liste_unites"]):
-        unite = ""
-        #warning("L'unité "+unite+" n'est pas reconnue !",liste_err)
-    configuration["unite"] = unite
-
-    # grille
-    grille = data.get("grille","")
-    if grille != "on":
-        configuration["grille"] = ""
-    else:
-        configuration["grille"] = "oui"
         
-    # maxi. 0 signifie qu'on veut pas de maxi
-    maxi = data.get("maxi","")
-    if maxi == "":
-        configuration["maxi"] = 0
-    else:
-        configuration["maxi"] = int(convertit_jours_vers_python(maxi,liste_err))
-    
-    # dimensions du graphique
-    largeur = data.get("largeur","")
-    if largeur == "":
-        largeur = DEFAUT["largeur_graphique"]
-    else:
-        try:
-            largeur = int(largeur)
-        except:
-            warning("La largeur "+largeur+"est invalide !",liste_err)
+        # maxi. 0 signifie qu'on veut pas de maxi
+        maxi = data.get("maxi","")
+        if maxi == "":
+            configuration["maxi"] = 0
+        else:
+            configuration["maxi"] = int(convertit_jours_vers_python(maxi,liste_err))
+        
+        # dimensions du graphique
+        largeur = data.get("largeur","")
+        if largeur == "":
             largeur = DEFAUT["largeur_graphique"]
-        if largeur > CONFIG["largeur_graphique_max"]:
-            largeur = CONFIG["largeur_graphique_max"]
-            warning("Largeur du graphique trop grande !",liste_err)
-        elif largeur < CONFIG["largeur_graphique_min"]:
-            largeur = CONFIG["largeur_graphique_min"]
-            warning("Largeur du graphique trop petite !",liste_err)
-    configuration["largeur"] = largeur
-    
-    hauteur = data.get("hauteur","")
-    if hauteur == "":
-        hauteur = DEFAUT["hauteur_graphique"]
-    else:
-        try:
-            hauteur = int(hauteur)
-        except:
-            warning("La hauteur "+hauteur+"est invalide !",liste_err)
+        else:
+            try:
+                largeur = int(largeur)
+            except:
+                warning("La largeur "+largeur+"est invalide !",liste_err)
+                largeur = DEFAUT["largeur_graphique"]
+            if largeur > CONFIG["largeur_graphique_max"]:
+                largeur = CONFIG["largeur_graphique_max"]
+                warning("Largeur du graphique trop grande !",liste_err)
+            elif largeur < CONFIG["largeur_graphique_min"]:
+                largeur = CONFIG["largeur_graphique_min"]
+                warning("Largeur du graphique trop petite !",liste_err)
+        configuration["largeur"] = largeur
+        
+        hauteur = data.get("hauteur","")
+        if hauteur == "":
             hauteur = DEFAUT["hauteur_graphique"]
-        if hauteur > CONFIG["hauteur_graphique_max"]:
-            hauteur = CONFIG["hauteur_graphique_max"]
-            warning("Hauteur du graphique trop grande !",liste_err)
-        elif hauteur < CONFIG["hauteur_graphique_min"]:
-            hauteur = CONFIG["hauteur_graphique_min"]
-            warning("Hauteur du graphique trop petite !",liste_err)
-    configuration["hauteur"] = hauteur   
+        else:
+            try:
+                hauteur = int(hauteur)
+            except:
+                warning("La hauteur "+hauteur+"est invalide !",liste_err)
+                hauteur = DEFAUT["hauteur_graphique"]
+            if hauteur > CONFIG["hauteur_graphique_max"]:
+                hauteur = CONFIG["hauteur_graphique_max"]
+                warning("Hauteur du graphique trop grande !",liste_err)
+            elif hauteur < CONFIG["hauteur_graphique_min"]:
+                hauteur = CONFIG["hauteur_graphique_min"]
+                warning("Hauteur du graphique trop petite !",liste_err)
+        configuration["hauteur"] = hauteur   
+        
+        # existence et position de la légende
+        configuration["legende"] = gere_checkbox(data.get("legende",""))
+        
+        positionlegende = data.get("positionlegende","")
+        if not(positionlegende in ['upper left','upper right','lower left','lower right']):
+            positionlegende = "upper left"
+        configuration["positionlegende"] = positionlegende
+        
+        configuration["couleurs"] = {}
+        # gérer les couleurs
+        for clecouleur in DEFAUT["couleurs"]:
+            coul = rgb_vers_tuple(data.get("couleur_"+clecouleur,""),DEFAUT["couleurs"].get(clecouleur, ""),liste_err)
+            configuration["couleurs"][clecouleur] = coul
+        
+        # symbole
+        configuration["symbole"] = gere_symbole( data.get("symbole", ""))
     
-    # existence et position de la légende
-    legende = data.get("legende","")
-    if legende =="":
-        legende = "non"
-    elif legende=="on":
-        legende = "oui"
-    else:
-        legende = "oui"
-    configuration["legende"] = legende
+        configuration["non_sauve"]["grilleamelio"] = gere_checkbox(data.get("grilleamelio",""))
     
-    positionlegende = data.get("positionlegende","")
-    if not(positionlegende in ['upper left','upper right','lower left','lower right']):
-        positionlegende = "upper left"
-    configuration["positionlegende"] = positionlegende
-
     
-    configuration["couleurs"] = {}
-    # gérer les couleurs
-    for clecouleur in DEFAUT["couleurs"]:
-        coul = rgb_vers_tuple(data.get("couleur_"+clecouleur,""),CONFIG["couleurs"][clecouleur],liste_err)
-        configuration["couleurs"][clecouleur] = coul
+        #### La partie extrapolation n'a pas besoin d'être sauvée
+        configuration["non_sauve"]["prolongercourbes"] = gere_checkbox(data.get("prolongercourbes",""))
+    
+        # Valeur par défaut : 1
+        debug(data.get("nbextradata", "aaargh"), liste_err)
+        nbextradata = data.get("nbextradata",1)
+        try:
+            nbextradata = int(nbextradata)
+        except:
+            warning("Le nombre de données sur lequel on extrapole est invalide : "+nbextradata, liste_err)
+            nbextradata = 1
+        configuration["non_sauve"]["nbextradata"] = nbextradata
+        
+        if data.get("calculextradata_type","") in CONFIG["liste_typedonnees"]:
+            configuration["non_sauve"]["calculextradata_type"] = data.get("calculextradata_type","")
+            configuration["non_sauve"]["calculextradata_age"] = convertit_jours_vers_python(data.get("calculextradata_age","0j"),liste_err)
+        else:
+            configuration["non_sauve"]["calculextradata_type"] = ""
+            # On ne met rien dans l'âge, pas la peine
+        
+        ctyped = data.get("calculextratemps_type","")
+        if ctyped in CONFIG["liste_typedonnees"]:
+            configuration["non_sauve"]["calculextratemps_type"] = ctyped
+            configuration["non_sauve"]["calculextratemps_val"] = convertit_donnee_vers_python(data.get("calculextratemps_val",""), ctyped, liste_err)
+        else:
+            configuration["non_sauve"]["calculextratemps_type"] = ""
+        
+        # Tracer les calculs sur la grille
+        configuration["non_sauve"]["calculextradata_trace"] = gere_checkbox(data.get("calculextradata_trace"))
+        configuration["non_sauve"]["calculextratemps_trace"] = gere_checkbox(data.get("calculextratemps_trace"))
+
     
+        ### Gestion des repères additionnels
+        configuration["liste_reperes"] = []
+        i=0
+        while "repere_texte_"+str(i) in data: # Tant qu'il y a des trucs définis
+            debug("Repère trouvé", liste_err)
+            age=data.get("repere_age_"+str(i), "")
+            trace=gere_checkbox(data.get("repere_trace_"+str(i), ""))
+            if age !="":
+                agec=convertit_jours_vers_python(age, liste_err)
+                texte = data.get("repere_texte_"+str(i), "") # Même si le texte est vide, osef
+                configuration["liste_reperes"].append({"typed": "age", "donnee": agec, "texte": texte, "trace": trace})
+            i+=1
     
     return configuration   
  
-
-def gere_donneespoids(data,naissance,liste_err):
-    """ prend en argument le dictionnaire de requête, et la date de naissance 
-    (éventuellement vide) et construit les deux listes l_jours et l_poids"""
     
-    # On construit la liste des couples
+
+## web vers python : données
+def gere_donnees(data,naissance,typedonnee,liste_err):
+    """ prend en argument le dictionnaire de requête, et la date de
+    naissance (éventuellement vide), et construit deux listes :
+    l_jours et l_data correspondantes.
+    Il faut donner en argument le type de données : voir
+    CONFIG["liste_typedonnees"]"""
+    if typedonnee not in CONFIG["liste_typedonnees"]:
+        warning("gere_donnees : le type de données : "+typedonnee+" est invalide !! Types acceptés : "+str(CONFIG["liste_typedonnees"]),liste_err)
+        return ([],[])
+
+    # On construit une liste de couples d'abord
     liste_donnees = []
 
     i = 0
-    # On va chercher si y'a des données à poids_i
-    while "poids_"+str(i) in data.keys():
-        if data["poids_"+str(i)] != "":
-            poids = convertit_poids_vers_python(data["poids_"+str(i)],liste_err)
+    # On va chercher si y'a des données à donnee_i
+    while "age_"+str(i) in data.keys():
+        if data.get(typedonnee+"_"+str(i), "") != "": # si la donne de ce type existe
+            donnee = convertit_donnee_vers_python(data[typedonnee+"_"+str(i)],typedonnee,liste_err)
             age = data.get("age_"+str(i),"")
             if age !="":
                 age = convertit_jours_vers_python(age,liste_err)
-                liste_donnees.append((age,poids))
+                liste_donnees.append((age,donnee))
             else: 
                 date = data.get("date_"+str(i),"")
                 datep = convertit_date_vers_python(date,liste_err)
@@ -279,7 +416,7 @@ def gere_donneespoids(data,naissance,liste_err):
                     warning("La date de naissance n'a pas été précisée. Du coup on ne peut pas calculer l'âge de l'enfant le "+date,liste_err)
                 elif datep != "":  # la date est valide et on a une date de naissance
                     age = delta_date(datep,naissance)
-                    liste_donnees.append((age,poids))
+                    liste_donnees.append((age,donnee))
         i+=1
         
     # Trier la liste
@@ -287,37 +424,138 @@ def gere_donneespoids(data,naissance,liste_err):
 
     # splitter la liste
     l_jours = [x[0] for x in liste_donnees]
-    l_poids = [x[1] for x in liste_donnees]   
+    l_donnee = [x[1] for x in liste_donnees]   
     
-    return (l_jours,l_poids)
+    return (l_jours,l_donnee)    
+
+# web vers python : enfants additionnels
+def gere_enfants_additionnels(data, files, liste_err):
+    """ data est le dictionnaire de requête.
+    files est le dictionnaire des fichiers (flask.request.files).
+    Renvoie les enfants additionnels sous forme de liste de dictionnaires :
+    {typed: (conf, lj, ldonnees)}
+    Dans conf y'a les infos qu'il faut pour tracer la courbe additionnelle voulue.
+    """
+
+    enfants_add = [] # Enfants additionnels
+    # Les enfants additionnels commencent à 2 (puis 3, 4, etc)
+    i=2
+    while "couleur_donnees_"+str(i) in data: # Tant qu'il y a des données de ce type
+        if 'fichier_donnees_'+str(i) in files: # Un enfant à ajouter
+            fichier = files["fichier_donnees_"+str(i)]
+            chaine = fichier.read() # On récupère la chaîne
+            if len(chaine)>=5: # Si elle a une longueur à peu près raisonnable
+                debug("Un fichier de données additionnel trouvé", liste_err)
+                formulaire_2 = fichier_json_vers_configdonnees(chaine, liste_err)
+                if formulaire_2 =={}:
+                    warning("Le fichier de données additionnel est vide ou mal formaté", liste_err)
+                else:
+                    debug("Form 2 : "+str(formulaire_2), liste_err)
+                    # Récupérer sous forme python
+                    conf2, ljours2, listes_donnees2 = web_vers_python(formulaire_2,liste_err, court=True)
+                    debug("Form 2 données travaillées "+str(ljours2)+str(listes_donnees2), liste_err)
+                    # Le symbole et la couleur
+                    symb2 = gere_symbole(data.get("symbole_donnees_"+str(i)))
+                    coul2 = rgb_vers_tuple(data.get("couleur_donnees_"+str(i),""),CONFIG["couleurs"]["cadretxt"],liste_err)
+                    enfant2 = eclate_donnees_additionnelles(conf2, ljours2, listes_donnees2, symb2, coul2)
+                    enfants_add.append(enfant2)
+        i+=1
+
+    return enfants_add
+
 
 
 
 
-def donnees_vers_json(l_jours,l_poids,config):
+# python vers Json
+#### export vers json
+
+def donnees_vers_json(l_jours,l_poids,l_jourst,l_taille,config):
     """ retourne le json à renvoyer"""
     gros_dico = copy.deepcopy(config)
+    gros_dico["version"] = CONFIG["version"]
     l_jours2 = [convertit_age_vers_texte(d) for d in l_jours]
+    l_jourst2 = [convertit_age_vers_texte(d) for d in l_jourst]
     gros_dico["data_j"] = l_jours2
     gros_dico["data_p"] = l_poids
+    gros_dico["data_jours_taille"] = l_jourst2
+    gros_dico["data_taille"] = l_taille
     # gérer la date de naissance
     if gros_dico.get("naissance","") != "":
         gros_dico["naissance"] = convertit_date_vers_texte(gros_dico["naissance"])
+        # Calcul de toutes les dates de données
+        l_dates_poids = [convertit_date_vers_texte( config["naissance"] + datetime.timedelta(days=jours) ) for jours in l_jours]
+        l_dates_taille = [convertit_date_vers_texte( config["naissance"] + datetime.timedelta(days=jours) ) for jours in l_jourst]
+        gros_dico["data_dates_poids"]= l_dates_poids
+        gros_dico["data_dates_taille"] = l_dates_taille
+            
+        
     # gérer l'age maxi
     gros_dico["maxi"] = convertit_age_vers_texte(gros_dico["maxi"])
+    # Gérer les repères
+    for i in range(len(gros_dico["liste_reperes"])):
+        gros_dico["liste_reperes"][i]["donnee"] = convertit_age_vers_texte(gros_dico["liste_reperes"][i]["donnee"])
     # gérer les couleurs
-#    for cle in ["couleur1", "couleur2", "couleur3", "couleur_fond","couleur_grille","couleur_cadretxt"]:
-#        gros_dico[cle] = tuple_vers_rgb(gros_dico[cle])
     for clecouleur in DEFAUT["couleurs"]:
         gros_dico["couleurs"][clecouleur] = tuple_vers_rgb(gros_dico["couleurs"][clecouleur])
     
-    
+    # Enlever ce qui ne se sauvegarde pas si y'a
+    if "non_sauve" in gros_dico:
+        del gros_dico["non_sauve"]
         
     return json.dumps(gros_dico, indent=2,ensure_ascii=False )
 
+def fusionne_donnees(listes_jours,listes_donnees, listes_dates):
+    """ prend en argument deux dicos de listes. Chaque liste de jours est associée à une liste 
+    de données (par la même clé de type de données). Il faut les fusionner pour avoir une liste de dictionnaires, de type
+    {"age":truc, "donnee1":truc, "donnee2":truc, ...} triée par ordre de jours. Si jamais une des données est vide,
+    le champ du dictionnaire n'est pas rempli
+    Le troisième paquet de listes (les dates) peut être vide ou bien simiaire : même clés."""
 
+    def fini(lj):
+        """ teste si les listes sont toutes vides """
+        for l in lj.values():
+            if l!=[]:
+                return False
+        return True
+    
+    def mini(lj):
+        """ renvoie la clé de la liste où il y a le min """
+        cle_mini = CONFIG["liste_typedonnees"][0]
+        for (cle,liste) in lj.items():
+            if lj[cle_mini]== []:
+                cle_mini = cle
+            elif lj[cle] != []:
+                if convertit_jours_vers_python(lj[cle][0],initialise_erreurs())<convertit_jours_vers_python(lj[cle_mini][0],initialise_erreurs()):
+                    cle_mini = cle
+        return cle_mini
+    
+    liste_f = []
+    while not(fini(listes_jours)):
+        typedonnee = mini(listes_jours)
+        # On extrait les données dans les deux listes (jours et données)
+        jour = listes_jours[typedonnee].pop(0)
+        donnee = listes_donnees[typedonnee].pop(0)
+            
+        if liste_f == [] or jour != liste_f[-1]["age"]: # Si le jour est un "nouveau" jour
+            liste_f.append({"age":jour})
+        # On met à jour l'élément (ou on l'ajoute)
+        liste_f[-1][typedonnee] = donnee
+        
+        # Si il y a une date associée, on la met !
+        if listes_dates[typedonnee] != []:
+            date = listes_dates[typedonnee].pop(0)
+            liste_f[-1]["date"] = convertit_date_vers_texte(date)
+
+        
+    return liste_f
+    
+
+### COnversion json vers formulaire
+# Json -> formulaire HTML
 def fichier_json_vers_configdonnees(chaine,liste_err):
-    """ prend le json importé (chaine) et l'exporte vers les valeurs du formulaire """
+    """ prend le json importé (chaine) et l'exporte vers les valeurs du formulaire 
+    Renvoyé sous forme de dictionnaire (mais adapté au formulaire web)"""
     debug("json vers config : Prêt à interpréter le json",liste_err)
     try:    
         valform = json.loads(chaine)
@@ -326,20 +564,48 @@ def fichier_json_vers_configdonnees(chaine,liste_err):
         return {}
     # Il faut maintenant récupérer les l_jours et l_poids puis les remettre
     # sous forme de age_i et poids_i
-    l_jours= valform.get("data_j",[])
-    l_poids=valform.get("data_p",[])
-    if len(l_poids) != len(l_jours):
-        warning("Lecture du json : les données sont incohérentes (listes de taille différentes et/ou pb de lecture")
-        long = min(len(l_jours),len(l_poids))
-    else:
-        long = len(l_jours)
-    for i in range(long):
-        valform["age_"+str(i)] = l_jours[i]
-        valform["poids_"+str(i)] = l_poids[i]
+    
+    listes_jours = {}
+    listes_donnees = {}
+    listes_dates = {}
+    for typed in CONFIG["liste_typedonnees"]:
+        if typed == "poids": # pour la rétrocompatibilité            
+            listes_jours[typed] = valform.get("data_j",[])
+            listes_donnees[typed] = valform.get("data_p",[])
+            listes_dates[typed] = valform.get("data_dates_"+typed,[])
+        else:
+            listes_jours[typed] = valform.get("data_jours_"+typed,[])
+            listes_donnees[typed] = valform.get("data_"+typed,[])
+            listes_dates[typed] = valform.get("data_dates_"+typed,[])
+            
+    
+    debug("Avant fusion : listes jours "+str(listes_jours),liste_err)
+    liste_donnees = fusionne_donnees(listes_jours,listes_donnees, listes_dates)
+    debug("Fusion de listes ok. Liste moche : "+str(liste_donnees),liste_err)
+    for i in range(len(liste_donnees)):
+        for (cle,val) in liste_donnees[i].items():
+            valform[cle+"_"+str(i)] = val        
         
-    valform["nb_data"] = max(long +2,DEFAUT["nb_data"])
+    valform["nb_data"] = max(len(liste_donnees) +2,DEFAUT["nb_data"])
     
     return valform
     
     
-    
\ No newline at end of file
+
+#### Pour l'insertion d'une 2e (ou plus) courbe sur le graphique, ue fonction qui sépare tout ça
+def eclate_donnees_additionnelles(conf, ljours, ldonnees, symb, couleur):
+    """ conf est la config (on ne garde que le nom) pour un enfant additionnel,
+    ljours et ldonnees les dictionnaires de listes contenant les données.
+    symb est le symbole choisi pour cette courbe additionnelle (déjà vérifié)
+    On fabrique un joli dictionnaire typed -> (conf, lj, ldonnee) avec le nom de l'enfant,
+    et les données pour chaque typed"""
+     #print("test conf avant "+str(ldonnees)+str(ljours))
+    
+    retour = {}
+    conf["symbole"] = symb # On ajoute le symbole additionnel
+    conf["couleurcourbe"] = couleur # la couleur
+    for typed in CONFIG["liste_typedonnees"]:
+        retour[typed] = (conf, ljours[typed], ldonnees[typed])
+        
+    #print("test "+str(retour))
+    return retour
\ No newline at end of file