]> git.immae.eu Git - perso/Denise/oms.git/blame - gestion_donnees.py
Bug mineur corrigé (date inférieure à la date de naissance)
[perso/Denise/oms.git] / gestion_donnees.py
CommitLineData
5679dfd0
DL
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
d03279e7 4from configuration import CONFIG,DEFAUT
a680b2f7
DL
5from gestion_erreurs import debug, warning, erreur, initialise_erreurs
6from gestion_couleurs import rgb_vers_tuple, tuple_vers_rgb
7from gestion_unites import choix_unite
5679dfd0 8import datetime
be2bf515
DL
9import json
10import unidecode
915e90bb 11import copy
5679dfd0 12
61020126
DL
13### Les données "tournent" selon :
14### python -> json -> (export/import) -> formulaire HTML -> données POST -> python etc
5679dfd0
DL
15
16############ Fonctions de conversion
17
18def convertit_jours_vers_python(chaine,liste_err):
19 """ convertit une chaine de type 1a 3m 1s 10j en jours
be2bf515 20 Renvoie un nombre de jours en float
5679dfd0
DL
21 Si un des caractères n'est ni un nombre, ni une lettre "autorisée" ni une espace,
22 on affiche un warning et on ignore ce caractère
23 """
b5ac625b 24# debug("conversion de "+chaine+" vers un nb de jours",liste_err)
5679dfd0
DL
25 chainenombre = ""
26 agejours = 0.
27 for lettre in chaine:
28 if lettre.isdigit():
29 chainenombre += lettre
30 else:
31 if lettre == 'a' or lettre == 'A':
32 # On a trouvé l'année, on ajoute tout ce qui est trouvé jusque là
d03279e7 33 agejours += int(chainenombre)*CONFIG["jours_dans_annee"]
5679dfd0
DL
34 chainenombre = ""
35 elif lettre == 'm' or lettre == 'M':
36 # On a trouvé le mois
d03279e7 37 agejours += int(chainenombre)*CONFIG["jours_dans_mois"]
5679dfd0
DL
38 chainenombre = ""
39 elif lettre == 's' or lettre == 'S':
40 # la semaine
d03279e7 41 agejours += int(chainenombre)*CONFIG["jours_dans_semaine"]
5679dfd0
DL
42 chainenombre = ""
43 elif lettre == 'j' or lettre == 'J':
44 # On a trouvé le jour
45 agejours += int(chainenombre)
46 chainenombre = ""
47 elif lettre != ' ':
48 # autre caractère : bizarre ?
2dc9eb43 49 warning("problème à la conversion de "+chaine+". Caractère invalide : "+lettre,liste_err)
5679dfd0
DL
50 # à la fin s'il reste qqch on le garde dans les jours
51 if chainenombre != "":
be2bf515 52 agejours += int(chainenombre)
5679dfd0
DL
53 if agejours<0:
54 warning("L'âge est négatif !",liste_err)
55 agejours = 0
b5ac625b 56# debug("On a convertit ! Résultat : "+str(agejours),liste_err)
be2bf515 57 return agejours
5679dfd0 58
8b5845ff 59# python -> json
5679dfd0
DL
60def convertit_age_vers_texte(nombre):
61 """ convertit un nombre de jours en un truc plus lisible en mois, années, jours
62 et renvoie une chaîne sous la forme 3a2m1j par exemple"""
d03279e7
DL
63 annees = int(nombre / CONFIG["jours_dans_annee"])
64 restant = nombre - annees*CONFIG["jours_dans_annee"]
65 mois = int(restant/CONFIG["jours_dans_mois"])
66 jours= round(nombre - mois*CONFIG["jours_dans_mois"] - annees*CONFIG["jours_dans_annee"])
5679dfd0
DL
67
68 chaine = ""
69 if annees >0:
70 chaine += str(annees)+"a"
71 if mois >0:
72 chaine += str(mois)+"m"
73 if jours>0 or nombre ==0: # si c'est la naissance, faut beien écrire 0j quand même
74 chaine += str(jours)+"j"
75 return chaine
a680b2f7 76
5679dfd0 77##########################
be2bf515 78
8b5845ff
DL
79# fonction qui calcule "auto" le maxi du graphique en fonction du max
80def calcule_max_graphique(l_jours):
81 """ calcule l'age maxi sur le graphique"""
82 if l_jours == []:
83 return CONFIG["jours_defaut_donneesvides"]
84 else:
85 jour_maxi = max(l_jours)# pas la peine d'aller très au delà du jour max
c2fe511b 86 jour_maxi = int(jour_maxi* 1.2)+3 # on rajoute un peu
8b5845ff
DL
87 return jour_maxi
88
be2bf515
DL
89
90def simplifie_nom(chaine):
91 """ simplifie le nom chaine afin d'en faire une extension
92 pour le nom du fichier. Met tout en minuscules et vire les caractères spéciaux
93 et max 15 caractères"""
94 chaine2 = ""
95 for l in chaine:
96 if l.isalpha():
97 chaine2+=l
98 chaine2 = unidecode.unidecode(chaine2)
99 return chaine2[:15]
5679dfd0 100
8b5845ff
DL
101def convertit_donnee_vers_python(chaine,typedonnee,liste_err):
102 """ convertit une chaine vers un float qui est le type donnee voulu.
103 La virgule peut être . ou , et on vire d'éventuels espaces.
ca61f310
DL
104 Taille invalide : on renvoie 0 avec un warning.
105 Si la chaine est en fait déjà au format float, on laisse tel quel"""
106 if type(chaine) == float:
107 return chaine
5679dfd0
DL
108 chaine2 = chaine.replace(",",".")
109 chaine2 = chaine2.replace(" ","")
ca61f310 110
5679dfd0
DL
111
112 try:
8b5845ff 113 donnee = float(chaine2)
5679dfd0 114 except:
8b5845ff
DL
115 warning(typedonnee+" impossible à lire : "+chaine,liste_err)
116 donnee = 0
d9eaf2e2
DL
117
118 # Pour le poids, un cas particulier
119 if typedonnee == "poids" and donnee > CONFIG["poids_maxi_conversion"]:
120 donnee = donnee/1000 # conversion en grammes
8b5845ff 121 if not( 0<=donnee<CONFIG[typedonnee+"_maxi"]):
d9eaf2e2 122 warning(typedonnee+" incohérent(e) : "+str(donnee),liste_err)
8b5845ff
DL
123 donnee = 0
124 return donnee
5679dfd0 125
5679dfd0
DL
126
127#########################
128
8b5845ff 129# web -> python
5679dfd0
DL
130def convertit_date_vers_python(chaine,liste_err):
131 """ prend une chaine comme renvoyée par un champ de formulaire date
132 aaaa-mm-jj et en fait une date python
64be08b2
DL
133 renvoie "" si ne marche pas.
134 Si jamais la date est au format avec des / ça devrait passer aussi."""
135 if "/" in chaine:
136 liste = chaine.split("/")
137 else:
138 liste = chaine.split("-")
5679dfd0
DL
139 if len(liste) != 3:
140 warning("La date : "+chaine+" est invalide !",liste_err)
141 return ""
142 else:
30158504 143 debug("Conversion de la date "+chaine+". Découpage : "+str(liste),liste_err)
9cb3c31c
DL
144 try:
145 date = datetime.date(int(liste[0]),int(liste[1]),int(liste[2]))
146 except:
147 date = ""
30158504 148 warning("Impossible de lire la date "+chaine+". Format accepté : aaaa-mm-jj",liste_err)
9cb3c31c 149 return date
8b5845ff
DL
150
151# python -> json
5679dfd0
DL
152def convertit_date_vers_texte(date):
153 """ convertit une date python en format texte aaaa-mm-jj"""
154 if date == "":
155 return ""
156 else:
2dc9eb43
DL
157 #return (str(date.year)+"-"+str(date.month)+"-"+str(date.day))
158 return str(date)
5679dfd0 159
9d7f1506 160def delta_date(date1,datenaissance, liste_err):
5679dfd0
DL
161 """ renvoie le nombre de jours (entier) entre date1 et datenaissance format "datetime"
162 datenaissance est supposée antérieure. Erreur sinon."""
163 d = date1 - datenaissance
164 jours = d.days
165 if jours<0:
9d7f1506 166 warning("Une des dates saisies ("+str(date1)+") est inférieure à la date de naissance (la donnée est donc ignorée)", liste_err)
5679dfd0
DL
167 return -1
168 return jours
169
170
2dc9eb43 171
8b5845ff
DL
172################### On regroupe tout ce qui gère les données en une fonction
173
ca61f310 174def web_vers_python(data,liste_err, court=False):
8b5845ff 175 """ prend en argument le dictionnaire de requête et renvoie la config, et les
a63ef1b0 176 tableaux de données
ca61f310
DL
177 court : si True est précisé, on ne met que le nom dans la config (enfant
178 additionnel)"""
8b5845ff
DL
179
180 # Régler la configuration
ca61f310 181 config = gere_configuration(data,liste_err, court)
8b5845ff
DL
182
183 # récupérer les données
184 listes_jours = {}
185 listes_donnees = {}
186 for typed in CONFIG["liste_typedonnees"]:
187 listes_jours[typed],listes_donnees[typed] = gere_donnees(data,config["naissance"],typed,liste_err)
188
438ef56d
DL
189 # Si on veut extrapoler au-delà du jour maxi, on adapte
190
8b5845ff 191 # Si on a choisi la même échelle de données
ca61f310 192 if config.get("memechelle") == "oui":
8b5845ff 193 config["non_sauve"]["maxi"] = calcule_max_graphique([j for lj in listes_jours.values() for j in lj])
438ef56d
DL
194 # En cas d'extrapolation, on prend le maxi
195 if config["non_sauve"]["calculextradata_type"] !="" and config["non_sauve"]["calculextradata_age"]>config["non_sauve"]["maxi"]:
196 config["non_sauve"]["maxi"] = int(config["non_sauve"]["calculextradata_age"]) +1
8b5845ff
DL
197 config["non_sauve"]["unite"] = choix_unite(config["non_sauve"]["maxi"])
198
199 return (config,listes_jours,listes_donnees)
200
201
202
a680b2f7 203########### Fonctions qui gèretn les données web vers python
5679dfd0 204
8b5845ff
DL
205def gere_checkbox(chaine):
206 """ prend en arg une chaine, et renvoie "oui" si c'est "on" (sortie de la checkbox)
207 et chaîne vide si n'importe quoi d'autre"""
208 if chaine == "on":
209 return "oui"
210 else:
211 return ""
5679dfd0 212
ca61f310
DL
213def gere_symbole(chaine):
214 """ prend en arg une chaîne genre "o", ">" et vérifie si c'est un symbole valide.
215 Renvoie ce symbole-là ou le défaut"""
216 if chaine in CONFIG["liste_symboles"]:
217 return chaine
218 else:
219 return DEFAUT["symbole"]
220
221def gere_configuration(data,liste_err, court=False):
be2bf515 222 """ prend en argument le dictionnaire de requête (configuration imparfaite), et
5679dfd0 223 construit le dictionnaire de configuration qui va bien.
ca61f310
DL
224 Vérifie que chaque entrée est cohérente évidemment.
225 court : si mis à True, on ne met que le nom dans la configuraion,
226 ainsi que la date de naissance et le sexe"""
a680b2f7 227 # Initialisation
685a5f75 228 configuration = {"non_sauve": {}}
5679dfd0
DL
229
230 # Pour le nom, osef qu'il soit vide
231 nom = data.get("nom","")
232 # Par contre s'il est trop long on le tronque
d03279e7 233 configuration["nom"] = nom[:CONFIG["longueur_max_nom_bebe"]]
ca61f310 234
5679dfd0
DL
235 naissance = data.get("naissance","")
236 if naissance !="":
237 naissance = convertit_date_vers_python(naissance,liste_err)
238 configuration["naissance"] = naissance
239
ca61f310
DL
240 sexe = data.get("sexe","")
241 if not (sexe in ["F","M","N"]):
a63fb483 242 warning("Le sexe de l'enfant est invalide. "+sexe,liste_err)
ca61f310
DL
243 sexe = "N"
244 configuration["sexe"] = sexe
245
246 if not(court):
247
248 prematurite = data.get("prematurite","")
249 j = convertit_jours_vers_python(prematurite,liste_err)
250 configuration["prematurite"] = convertit_age_vers_texte(j)
5679dfd0 251
ca61f310
DL
252 configuration["agecorrige"] = gere_checkbox(data.get("agecorrige",""))
253
254 # Type de courbe. Au pire on met P
255 tyc = data.get("typecourbe","")
256 if not (tyc in ["P","Z"]):
257 tyc = "P"
258 configuration["typecourbe"] = tyc
259
260 # unité
261 unite = data.get("unite","")
262 if not (unite in CONFIG["liste_unites"]):
263 unite = ""
264 #warning("L'unité "+unite+" n'est pas reconnue !",liste_err)
265 configuration["unite"] = unite
266
267 # grille
268 configuration["grille"] = gere_checkbox(data.get("grille",""))
269
270 # tracer ou non les courbes vides
271 configuration["tracevide"] = gere_checkbox(data.get("tracevide",""))
272
273 # Même échelle sur tous les graphiques
274 configuration["memechelle"] = gere_checkbox(data.get("memechelle",""))
5679dfd0 275
ca61f310
DL
276
277 # maxi. 0 signifie qu'on veut pas de maxi
278 maxi = data.get("maxi","")
279 if maxi == "":
280 configuration["maxi"] = 0
281 else:
282 configuration["maxi"] = int(convertit_jours_vers_python(maxi,liste_err))
283
284 # dimensions du graphique
285 largeur = data.get("largeur","")
286 if largeur == "":
915e90bb 287 largeur = DEFAUT["largeur_graphique"]
ca61f310
DL
288 else:
289 try:
290 largeur = int(largeur)
291 except:
292 warning("La largeur "+largeur+"est invalide !",liste_err)
293 largeur = DEFAUT["largeur_graphique"]
294 if largeur > CONFIG["largeur_graphique_max"]:
295 largeur = CONFIG["largeur_graphique_max"]
296 warning("Largeur du graphique trop grande !",liste_err)
297 elif largeur < CONFIG["largeur_graphique_min"]:
298 largeur = CONFIG["largeur_graphique_min"]
299 warning("Largeur du graphique trop petite !",liste_err)
300 configuration["largeur"] = largeur
301
302 hauteur = data.get("hauteur","")
303 if hauteur == "":
915e90bb 304 hauteur = DEFAUT["hauteur_graphique"]
ca61f310
DL
305 else:
306 try:
307 hauteur = int(hauteur)
308 except:
309 warning("La hauteur "+hauteur+"est invalide !",liste_err)
310 hauteur = DEFAUT["hauteur_graphique"]
311 if hauteur > CONFIG["hauteur_graphique_max"]:
312 hauteur = CONFIG["hauteur_graphique_max"]
313 warning("Hauteur du graphique trop grande !",liste_err)
314 elif hauteur < CONFIG["hauteur_graphique_min"]:
315 hauteur = CONFIG["hauteur_graphique_min"]
316 warning("Hauteur du graphique trop petite !",liste_err)
317 configuration["hauteur"] = hauteur
318
319 # existence et position de la légende
320 configuration["legende"] = gere_checkbox(data.get("legende",""))
321
322 positionlegende = data.get("positionlegende","")
323 if not(positionlegende in ['upper left','upper right','lower left','lower right']):
324 positionlegende = "upper left"
325 configuration["positionlegende"] = positionlegende
326
327 configuration["couleurs"] = {}
328 # gérer les couleurs
329 for clecouleur in DEFAUT["couleurs"]:
38b5e10a 330 coul = rgb_vers_tuple(data.get("couleur_"+clecouleur,""),DEFAUT["couleurs"].get(clecouleur, ""),liste_err)
ca61f310
DL
331 configuration["couleurs"][clecouleur] = coul
332
333 # symbole
334 configuration["symbole"] = gere_symbole( data.get("symbole", ""))
fd69b6b5 335
ca61f310 336 configuration["non_sauve"]["grilleamelio"] = gere_checkbox(data.get("grilleamelio",""))
cf0d4c8c 337
cf0d4c8c 338
ca61f310
DL
339 #### La partie extrapolation n'a pas besoin d'être sauvée
340 configuration["non_sauve"]["prolongercourbes"] = gere_checkbox(data.get("prolongercourbes",""))
fd69b6b5 341
ca61f310
DL
342 # Valeur par défaut : 1
343 debug(data.get("nbextradata", "aaargh"), liste_err)
344 nbextradata = data.get("nbextradata",1)
345 try:
346 nbextradata = int(nbextradata)
347 except:
348 warning("Le nombre de données sur lequel on extrapole est invalide : "+nbextradata, liste_err)
349 nbextradata = 1
350 configuration["non_sauve"]["nbextradata"] = nbextradata
351
352 if data.get("calculextradata_type","") in CONFIG["liste_typedonnees"]:
353 configuration["non_sauve"]["calculextradata_type"] = data.get("calculextradata_type","")
354 configuration["non_sauve"]["calculextradata_age"] = convertit_jours_vers_python(data.get("calculextradata_age","0j"),liste_err)
355 else:
356 configuration["non_sauve"]["calculextradata_type"] = ""
357 # On ne met rien dans l'âge, pas la peine
358
359 ctyped = data.get("calculextratemps_type","")
360 if ctyped in CONFIG["liste_typedonnees"]:
361 configuration["non_sauve"]["calculextratemps_type"] = ctyped
362 configuration["non_sauve"]["calculextratemps_val"] = convertit_donnee_vers_python(data.get("calculextratemps_val",""), ctyped, liste_err)
363 else:
364 configuration["non_sauve"]["calculextratemps_type"] = ""
365
366 # Tracer les calculs sur la grille
367 configuration["non_sauve"]["calculextradata_trace"] = gere_checkbox(data.get("calculextradata_trace"))
368 configuration["non_sauve"]["calculextratemps_trace"] = gere_checkbox(data.get("calculextratemps_trace"))
3d7da80a
DL
369
370
6ac2173a
DL
371 ### Gestion des repères additionnels
372 configuration["liste_reperes"] = []
373 i=0
374 while "repere_texte_"+str(i) in data: # Tant qu'il y a des trucs définis
375 debug("Repère trouvé", liste_err)
376 age=data.get("repere_age_"+str(i), "")
377 trace=gere_checkbox(data.get("repere_trace_"+str(i), ""))
378 if age !="":
379 agec=convertit_jours_vers_python(age, liste_err)
380 texte = data.get("repere_texte_"+str(i), "") # Même si le texte est vide, osef
381 configuration["liste_reperes"].append({"typed": "age", "donnee": agec, "texte": texte, "trace": trace})
382 i+=1
383
5679dfd0 384 return configuration
5679dfd0 385
cf0d4c8c
DL
386
387
a680b2f7 388## web vers python : données
8b5845ff
DL
389def gere_donnees(data,naissance,typedonnee,liste_err):
390 """ prend en argument le dictionnaire de requête, et la date de
391 naissance (éventuellement vide), et construit deux listes :
392 l_jours et l_data correspondantes.
393 Il faut donner en argument le type de données : voir
394 CONFIG["liste_typedonnees"]"""
395 if typedonnee not in CONFIG["liste_typedonnees"]:
396 warning("gere_donnees : le type de données : "+typedonnee+" est invalide !! Types acceptés : "+str(CONFIG["liste_typedonnees"]),liste_err)
397 return ([],[])
398
399 # On construit une liste de couples d'abord
5679dfd0
DL
400 liste_donnees = []
401
402 i = 0
8b5845ff 403 # On va chercher si y'a des données à donnee_i
a63ef1b0
DL
404 while "age_"+str(i) in data.keys():
405 if data.get(typedonnee+"_"+str(i), "") != "": # si la donne de ce type existe
8b5845ff 406 donnee = convertit_donnee_vers_python(data[typedonnee+"_"+str(i)],typedonnee,liste_err)
5679dfd0
DL
407 age = data.get("age_"+str(i),"")
408 if age !="":
409 age = convertit_jours_vers_python(age,liste_err)
8b5845ff 410 liste_donnees.append((age,donnee))
5679dfd0
DL
411 else:
412 date = data.get("date_"+str(i),"")
413 datep = convertit_date_vers_python(date,liste_err)
414 # on vérifie la date
415 if naissance == "":
416 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)
417 elif datep != "": # la date est valide et on a une date de naissance
9d7f1506
DL
418 age = delta_date(datep,naissance, liste_err)
419 if age != -1: # -1 signifie une erreur donc on ne garde pas la donnée
420 liste_donnees.append((age,donnee))
5679dfd0
DL
421 i+=1
422
423 # Trier la liste
424 liste_donnees.sort(key=lambda x : x[0])
425
426 # splitter la liste
427 l_jours = [x[0] for x in liste_donnees]
8b5845ff 428 l_donnee = [x[1] for x in liste_donnees]
5679dfd0 429
8b5845ff 430 return (l_jours,l_donnee)
be2bf515 431
e4ec2d3d
DL
432# web vers python : enfants additionnels
433def gere_enfants_additionnels(data, files, liste_err):
434 """ data est le dictionnaire de requête.
435 files est le dictionnaire des fichiers (flask.request.files).
436 Renvoie les enfants additionnels sous forme de liste de dictionnaires :
437 {typed: (conf, lj, ldonnees)}
438 Dans conf y'a les infos qu'il faut pour tracer la courbe additionnelle voulue.
439 """
440
441 enfants_add = [] # Enfants additionnels
442 # Les enfants additionnels commencent à 2 (puis 3, 4, etc)
443 i=2
444 while "couleur_donnees_"+str(i) in data: # Tant qu'il y a des données de ce type
445 if 'fichier_donnees_'+str(i) in files: # Un enfant à ajouter
446 fichier = files["fichier_donnees_"+str(i)]
447 chaine = fichier.read() # On récupère la chaîne
448 if len(chaine)>=5: # Si elle a une longueur à peu près raisonnable
449 debug("Un fichier de données additionnel trouvé", liste_err)
450 formulaire_2 = fichier_json_vers_configdonnees(chaine, liste_err)
451 if formulaire_2 =={}:
452 warning("Le fichier de données additionnel est vide ou mal formaté", liste_err)
453 else:
454 debug("Form 2 : "+str(formulaire_2), liste_err)
455 # Récupérer sous forme python
456 conf2, ljours2, listes_donnees2 = web_vers_python(formulaire_2,liste_err, court=True)
457 debug("Form 2 données travaillées "+str(ljours2)+str(listes_donnees2), liste_err)
458 # Le symbole et la couleur
459 symb2 = gere_symbole(data.get("symbole_donnees_"+str(i)))
460 coul2 = rgb_vers_tuple(data.get("couleur_donnees_"+str(i),""),CONFIG["couleurs"]["cadretxt"],liste_err)
461 enfant2 = eclate_donnees_additionnelles(conf2, ljours2, listes_donnees2, symb2, coul2)
462 enfants_add.append(enfant2)
463 i+=1
464
465 return enfants_add
466
467
468
469
be2bf515 470
a680b2f7 471# python vers Json
8b5845ff 472#### export vers json
be2bf515 473
8b5845ff 474def donnees_vers_json(l_jours,l_poids,l_jourst,l_taille,config):
be2bf515 475 """ retourne le json à renvoyer"""
915e90bb 476 gros_dico = copy.deepcopy(config)
2dc9eb43 477 gros_dico["version"] = CONFIG["version"]
be2bf515 478 l_jours2 = [convertit_age_vers_texte(d) for d in l_jours]
8b5845ff 479 l_jourst2 = [convertit_age_vers_texte(d) for d in l_jourst]
be2bf515
DL
480 gros_dico["data_j"] = l_jours2
481 gros_dico["data_p"] = l_poids
8b5845ff
DL
482 gros_dico["data_jours_taille"] = l_jourst2
483 gros_dico["data_taille"] = l_taille
be2bf515
DL
484 # gérer la date de naissance
485 if gros_dico.get("naissance","") != "":
486 gros_dico["naissance"] = convertit_date_vers_texte(gros_dico["naissance"])
2dc9eb43
DL
487 # Calcul de toutes les dates de données
488 l_dates_poids = [convertit_date_vers_texte( config["naissance"] + datetime.timedelta(days=jours) ) for jours in l_jours]
489 l_dates_taille = [convertit_date_vers_texte( config["naissance"] + datetime.timedelta(days=jours) ) for jours in l_jourst]
490 gros_dico["data_dates_poids"]= l_dates_poids
491 gros_dico["data_dates_taille"] = l_dates_taille
492
493
be2bf515
DL
494 # gérer l'age maxi
495 gros_dico["maxi"] = convertit_age_vers_texte(gros_dico["maxi"])
6ac2173a
DL
496 # Gérer les repères
497 for i in range(len(gros_dico["liste_reperes"])):
498 gros_dico["liste_reperes"][i]["donnee"] = convertit_age_vers_texte(gros_dico["liste_reperes"][i]["donnee"])
fd69b6b5 499 # gérer les couleurs
915e90bb
DL
500 for clecouleur in DEFAUT["couleurs"]:
501 gros_dico["couleurs"][clecouleur] = tuple_vers_rgb(gros_dico["couleurs"][clecouleur])
fd69b6b5 502
8b5845ff
DL
503 # Enlever ce qui ne se sauvegarde pas si y'a
504 if "non_sauve" in gros_dico:
505 del gros_dico["non_sauve"]
5679dfd0 506
be2bf515 507 return json.dumps(gros_dico, indent=2,ensure_ascii=False )
5679dfd0 508
2dc9eb43 509def fusionne_donnees(listes_jours,listes_donnees, listes_dates):
8b5845ff
DL
510 """ prend en argument deux dicos de listes. Chaque liste de jours est associée à une liste
511 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
2dc9eb43
DL
512 {"age":truc, "donnee1":truc, "donnee2":truc, ...} triée par ordre de jours. Si jamais une des données est vide,
513 le champ du dictionnaire n'est pas rempli
514 Le troisième paquet de listes (les dates) peut être vide ou bien simiaire : même clés."""
a680b2f7 515
8b5845ff
DL
516 def fini(lj):
517 """ teste si les listes sont toutes vides """
518 for l in lj.values():
519 if l!=[]:
520 return False
521 return True
522
523 def mini(lj):
524 """ renvoie la clé de la liste où il y a le min """
525 cle_mini = CONFIG["liste_typedonnees"][0]
526 for (cle,liste) in lj.items():
527 if lj[cle_mini]== []:
528 cle_mini = cle
529 elif lj[cle] != []:
b5ac625b 530 if convertit_jours_vers_python(lj[cle][0],initialise_erreurs())<convertit_jours_vers_python(lj[cle_mini][0],initialise_erreurs()):
8b5845ff
DL
531 cle_mini = cle
532 return cle_mini
533
534 liste_f = []
535 while not(fini(listes_jours)):
536 typedonnee = mini(listes_jours)
537 # On extrait les données dans les deux listes (jours et données)
538 jour = listes_jours[typedonnee].pop(0)
539 donnee = listes_donnees[typedonnee].pop(0)
2dc9eb43 540
8b5845ff
DL
541 if liste_f == [] or jour != liste_f[-1]["age"]: # Si le jour est un "nouveau" jour
542 liste_f.append({"age":jour})
2dc9eb43 543 # On met à jour l'élément (ou on l'ajoute)
8b5845ff
DL
544 liste_f[-1][typedonnee] = donnee
545
2dc9eb43
DL
546 # Si il y a une date associée, on la met !
547 if listes_dates[typedonnee] != []:
548 date = listes_dates[typedonnee].pop(0)
549 liste_f[-1]["date"] = convertit_date_vers_texte(date)
550
551
8b5845ff
DL
552 return liste_f
553
5679dfd0 554
a680b2f7 555### COnversion json vers formulaire
8b5845ff 556# Json -> formulaire HTML
d03279e7 557def fichier_json_vers_configdonnees(chaine,liste_err):
a680b2f7
DL
558 """ prend le json importé (chaine) et l'exporte vers les valeurs du formulaire
559 Renvoyé sous forme de dictionnaire (mais adapté au formulaire web)"""
d03279e7
DL
560 debug("json vers config : Prêt à interpréter le json",liste_err)
561 try:
562 valform = json.loads(chaine)
563 except :
564 erreur("Impossible de lire le fichier json !",liste_err)
565 return {}
be2bf515
DL
566 # Il faut maintenant récupérer les l_jours et l_poids puis les remettre
567 # sous forme de age_i et poids_i
8b5845ff
DL
568
569 listes_jours = {}
570 listes_donnees = {}
2dc9eb43 571 listes_dates = {}
8b5845ff
DL
572 for typed in CONFIG["liste_typedonnees"]:
573 if typed == "poids": # pour la rétrocompatibilité
574 listes_jours[typed] = valform.get("data_j",[])
575 listes_donnees[typed] = valform.get("data_p",[])
2dc9eb43 576 listes_dates[typed] = valform.get("data_dates_"+typed,[])
8b5845ff
DL
577 else:
578 listes_jours[typed] = valform.get("data_jours_"+typed,[])
579 listes_donnees[typed] = valform.get("data_"+typed,[])
2dc9eb43
DL
580 listes_dates[typed] = valform.get("data_dates_"+typed,[])
581
8b5845ff
DL
582
583 debug("Avant fusion : listes jours "+str(listes_jours),liste_err)
2dc9eb43 584 liste_donnees = fusionne_donnees(listes_jours,listes_donnees, listes_dates)
8b5845ff
DL
585 debug("Fusion de listes ok. Liste moche : "+str(liste_donnees),liste_err)
586 for i in range(len(liste_donnees)):
587 for (cle,val) in liste_donnees[i].items():
588 valform[cle+"_"+str(i)] = val
be2bf515 589
8b5845ff 590 valform["nb_data"] = max(len(liste_donnees) +2,DEFAUT["nb_data"])
5679dfd0 591
be2bf515 592 return valform
5679dfd0 593
5679dfd0 594
ca61f310
DL
595
596#### Pour l'insertion d'une 2e (ou plus) courbe sur le graphique, ue fonction qui sépare tout ça
38b5e10a 597def eclate_donnees_additionnelles(conf, ljours, ldonnees, symb, couleur):
ca61f310
DL
598 """ conf est la config (on ne garde que le nom) pour un enfant additionnel,
599 ljours et ldonnees les dictionnaires de listes contenant les données.
600 symb est le symbole choisi pour cette courbe additionnelle (déjà vérifié)
e4ec2d3d 601 On fabrique un joli dictionnaire typed -> (conf, lj, ldonnee) avec le nom de l'enfant,
ca61f310 602 et les données pour chaque typed"""
e4ec2d3d 603 #print("test conf avant "+str(ldonnees)+str(ljours))
ca61f310
DL
604
605 retour = {}
606 conf["symbole"] = symb # On ajoute le symbole additionnel
38b5e10a 607 conf["couleurcourbe"] = couleur # la couleur
ca61f310
DL
608 for typed in CONFIG["liste_typedonnees"]:
609 retour[typed] = (conf, ljours[typed], ldonnees[typed])
610
e4ec2d3d 611 #print("test "+str(retour))
ca61f310 612 return retour