]> git.immae.eu Git - perso/Denise/oms.git/blob - gestion_donnees.py
priorité des dates sur les âges
[perso/Denise/oms.git] / gestion_donnees.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 from configuration import CONFIG,DEFAUT
5 from gestion_erreurs import debug, warning, erreur, initialise_erreurs
6 from gestion_couleurs import rgb_vers_tuple, tuple_vers_rgb
7 from gestion_unites import choix_unite
8 import datetime
9 import json
10 import unidecode
11 import copy
12
13 ### Les données "tournent" selon :
14 ### python -> json -> (export/import) -> formulaire HTML -> données POST -> python etc
15
16 ############ Fonctions de conversion
17
18 def convertit_jours_vers_python(chaine,liste_err):
19 """ convertit une chaine de type 1a 3m 1s 10j en jours
20 Renvoie un nombre de jours en float
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 """
24 # debug("conversion de "+chaine+" vers un nb de jours",liste_err)
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à
33 agejours += int(chainenombre)*CONFIG["jours_dans_annee"]
34 chainenombre = ""
35 elif lettre == 'm' or lettre == 'M':
36 # On a trouvé le mois
37 agejours += int(chainenombre)*CONFIG["jours_dans_mois"]
38 chainenombre = ""
39 elif lettre == 's' or lettre == 'S':
40 # la semaine
41 agejours += int(chainenombre)*CONFIG["jours_dans_semaine"]
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 ?
49 warning("problème à la conversion de "+chaine+". Caractère invalide : "+lettre,liste_err)
50 # à la fin s'il reste qqch on le garde dans les jours
51 if chainenombre != "":
52 agejours += int(chainenombre)
53 if agejours<0:
54 warning("L'âge est négatif !",liste_err)
55 agejours = 0
56 # debug("On a convertit ! Résultat : "+str(agejours),liste_err)
57 return agejours
58
59 # python -> json
60 def 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"""
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"])
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 bien écrire 0j quand même
74 chaine += str(jours)+"j"
75 return chaine
76
77 ##########################
78
79 # fonction qui calcule "auto" le maxi du graphique en fonction du max
80 def 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
86 jour_maxi = int(jour_maxi* 1.2)+3 # on rajoute un peu
87 return jour_maxi
88
89
90 def 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]
100
101 def 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.
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
108 chaine2 = chaine.replace(",",".")
109 chaine2 = chaine2.replace(" ","")
110
111
112 try:
113 donnee = float(chaine2)
114 except:
115 warning(typedonnee+" impossible à lire : "+chaine,liste_err)
116 donnee = 0
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
121 if not( 0<=donnee<CONFIG[typedonnee+"_maxi"]):
122 warning(typedonnee+" incohérent(e) : "+str(donnee),liste_err)
123 donnee = 0
124 return donnee
125
126
127 #########################
128
129 # web -> python
130 def 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
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("-")
139 if len(liste) != 3:
140 warning("La date : "+chaine+" est invalide !",liste_err)
141 return ""
142 else:
143 debug("Conversion de la date "+chaine+". Découpage : "+str(liste),liste_err)
144 try:
145 date = datetime.date(int(liste[0]),int(liste[1]),int(liste[2]))
146 except:
147 date = ""
148 warning("Impossible de lire la date "+chaine+". Format accepté : aaaa-mm-jj",liste_err)
149 return date
150
151 # python -> json
152 def convertit_date_vers_texte(date):
153 """ convertit une date python en format texte aaaa-mm-jj"""
154 if date == "":
155 return ""
156 else:
157 #return (str(date.year)+"-"+str(date.month)+"-"+str(date.day))
158 return str(date)
159
160 def delta_date(date1,datenaissance, liste_err):
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:
166 warning("Une des dates saisies ("+str(date1)+") est inférieure à la date de naissance (la donnée est donc ignorée)", liste_err)
167 return -1
168 return jours
169
170
171
172 ################### On regroupe tout ce qui gère les données en une fonction
173
174 def web_vers_python(data,liste_err, court=False):
175 """ prend en argument le dictionnaire de requête et renvoie la config, et les
176 tableaux de données
177 court : si True est précisé, on ne met que le nom dans la config (enfant
178 additionnel)"""
179
180 # Régler la configuration
181 config = gere_configuration(data,liste_err, court)
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
189 # Si on veut extrapoler au-delà du jour maxi, on adapte
190
191 # Si on a choisi la même échelle de données
192 if config.get("memechelle") == "oui":
193 config["non_sauve"]["maxi"] = calcule_max_graphique([j for lj in listes_jours.values() for j in lj])
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
197 config["non_sauve"]["unite"] = choix_unite(config["non_sauve"]["maxi"])
198
199 return (config,listes_jours,listes_donnees)
200
201
202
203 ########### Fonctions qui gèretn les données web vers python
204
205 def 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 ""
212
213 def 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
221 def gere_configuration(data,liste_err, court=False):
222 """ prend en argument le dictionnaire de requête (configuration imparfaite), et
223 construit le dictionnaire de configuration qui va bien.
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"""
227 # Initialisation
228 configuration = {"non_sauve": {}}
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
233 configuration["nom"] = nom[:CONFIG["longueur_max_nom_bebe"]]
234
235 naissance = data.get("naissance","")
236 if naissance !="":
237 naissance = convertit_date_vers_python(naissance,liste_err)
238 configuration["naissance"] = naissance
239
240 sexe = data.get("sexe","")
241 if not (sexe in ["F","M","N"]):
242 warning("Le sexe de l'enfant est invalide. "+sexe,liste_err)
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)
251
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",""))
275
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 == "":
287 largeur = DEFAUT["largeur_graphique"]
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 == "":
304 hauteur = DEFAUT["hauteur_graphique"]
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"]:
330 coul = rgb_vers_tuple(data.get("couleur_"+clecouleur,""),DEFAUT["couleurs"].get(clecouleur, ""),liste_err)
331 configuration["couleurs"][clecouleur] = coul
332
333 # symbole
334 configuration["symbole"] = gere_symbole( data.get("symbole", ""))
335
336 configuration["non_sauve"]["grilleamelio"] = gere_checkbox(data.get("grilleamelio",""))
337
338
339 #### La partie extrapolation n'a pas besoin d'être sauvée
340 configuration["non_sauve"]["prolongercourbes"] = gere_checkbox(data.get("prolongercourbes",""))
341
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"))
369
370
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
384 return configuration
385
386
387
388 ## web vers python : données
389 def 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
400 liste_donnees = []
401
402 i = 0
403 # On va chercher si y'a des données à donnee_i
404 while "age_"+str(i) in data.keys():
405 if data.get(typedonnee+"_"+str(i), "") != "": # si la donne de ce type existe
406 donnee = convertit_donnee_vers_python(data[typedonnee+"_"+str(i)],typedonnee,liste_err)
407
408 ## Si une date est saisie, on la prend en priorité car c'est des entiers et les entiers c'est BIEN
409 date = data.get("date_"+str(i),"")
410 datep = convertit_date_vers_python(date,liste_err)
411
412 if naissance != "" and datep != "": # On ne peut pas calculer l'âge si on n'a pas ces deux données
413 age = delta_date(datep,naissance, liste_err)
414 if age != -1: # -1 signifie une erreur donc on ne garde pas la donnée
415 liste_donnees.append((age,donnee))
416 else: # Sinon, on regarde si on a saisi l'âge
417 age = data.get("age_"+str(i),"")
418 if age == "":
419 warning("gere_donnees : ni l'âge ni la date ne sont saisies... donnée ignorée", liste_err)
420 else:
421 age = convertit_jours_vers_python(age,liste_err)
422 liste_donnees.append((age,donnee))
423 i+=1
424
425 # Trier la liste
426 liste_donnees.sort(key=lambda x : x[0])
427
428 # splitter la liste
429 l_jours = [x[0] for x in liste_donnees]
430 l_donnee = [x[1] for x in liste_donnees]
431
432 return (l_jours,l_donnee)
433
434 # web vers python : enfants additionnels
435 def gere_enfants_additionnels(data, files, liste_err):
436 """ data est le dictionnaire de requête.
437 files est le dictionnaire des fichiers (flask.request.files).
438 Renvoie les enfants additionnels sous forme de liste de dictionnaires :
439 {typed: (conf, lj, ldonnees)}
440 Dans conf y'a les infos qu'il faut pour tracer la courbe additionnelle voulue.
441 """
442
443 enfants_add = [] # Enfants additionnels
444 # Les enfants additionnels commencent à 2 (puis 3, 4, etc)
445 i=2
446 while "couleur_donnees_"+str(i) in data: # Tant qu'il y a des données de ce type
447 if 'fichier_donnees_'+str(i) in files: # Un enfant à ajouter
448 fichier = files["fichier_donnees_"+str(i)]
449 chaine = fichier.read() # On récupère la chaîne
450 if len(chaine)>=5: # Si elle a une longueur à peu près raisonnable
451 debug("Un fichier de données additionnel trouvé", liste_err)
452 formulaire_2 = fichier_json_vers_configdonnees(chaine, liste_err)
453 if formulaire_2 =={}:
454 warning("Le fichier de données additionnel est vide ou mal formaté", liste_err)
455 else:
456 debug("Form 2 : "+str(formulaire_2), liste_err)
457 # Récupérer sous forme python
458 conf2, ljours2, listes_donnees2 = web_vers_python(formulaire_2,liste_err, court=True)
459 debug("Form 2 données travaillées "+str(ljours2)+str(listes_donnees2), liste_err)
460 # Le symbole et la couleur
461 symb2 = gere_symbole(data.get("symbole_donnees_"+str(i)))
462 coul2 = rgb_vers_tuple(data.get("couleur_donnees_"+str(i),""),CONFIG["couleurs"]["cadretxt"],liste_err)
463 enfant2 = eclate_donnees_additionnelles(conf2, ljours2, listes_donnees2, symb2, coul2)
464 enfants_add.append(enfant2)
465 i+=1
466
467 return enfants_add
468
469
470
471
472
473 # python vers Json
474 #### export vers json
475
476 def donnees_vers_json(l_jours,l_poids,l_jourst,l_taille,config):
477 """ retourne le json à renvoyer"""
478 gros_dico = copy.deepcopy(config)
479 gros_dico["version"] = CONFIG["version"]
480 l_jours2 = [convertit_age_vers_texte(d) for d in l_jours]
481 l_jourst2 = [convertit_age_vers_texte(d) for d in l_jourst]
482 gros_dico["data_j"] = l_jours2
483 gros_dico["data_p"] = l_poids
484 gros_dico["data_jours_taille"] = l_jourst2
485 gros_dico["data_taille"] = l_taille
486 # gérer la date de naissance
487 if gros_dico.get("naissance","") != "":
488 gros_dico["naissance"] = convertit_date_vers_texte(gros_dico["naissance"])
489 # Calcul de toutes les dates de données
490 l_dates_poids = [convertit_date_vers_texte( config["naissance"] + datetime.timedelta(days=jours) ) for jours in l_jours]
491 l_dates_taille = [convertit_date_vers_texte( config["naissance"] + datetime.timedelta(days=jours) ) for jours in l_jourst]
492 gros_dico["data_dates_poids"]= l_dates_poids
493 gros_dico["data_dates_taille"] = l_dates_taille
494
495
496 # gérer l'age maxi
497 gros_dico["maxi"] = convertit_age_vers_texte(gros_dico["maxi"])
498 # Gérer les repères
499 for i in range(len(gros_dico["liste_reperes"])):
500 gros_dico["liste_reperes"][i]["donnee"] = convertit_age_vers_texte(gros_dico["liste_reperes"][i]["donnee"])
501 # gérer les couleurs
502 for clecouleur in DEFAUT["couleurs"]:
503 gros_dico["couleurs"][clecouleur] = tuple_vers_rgb(gros_dico["couleurs"][clecouleur])
504
505 # Enlever ce qui ne se sauvegarde pas si y'a
506 if "non_sauve" in gros_dico:
507 del gros_dico["non_sauve"]
508
509 return json.dumps(gros_dico, indent=2,ensure_ascii=False )
510
511 def fusionne_donnees(listes_jours,listes_donnees, listes_dates):
512 """ prend en argument deux dicos de listes. Chaque liste de jours est associée à une liste
513 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
514 {"age":truc, "donnee1":truc, "donnee2":truc, ...} triée par ordre de jours. Si jamais une des données est vide,
515 le champ du dictionnaire n'est pas rempli
516 Le troisième paquet de listes (les dates) peut être vide ou bien simiaire : même clés."""
517
518 def fini(lj):
519 """ teste si les listes sont toutes vides """
520 for l in lj.values():
521 if l!=[]:
522 return False
523 return True
524
525 def mini(lj):
526 """ renvoie la clé de la liste où il y a le min """
527 cle_mini = CONFIG["liste_typedonnees"][0]
528 for (cle,liste) in lj.items():
529 if lj[cle_mini]== []:
530 cle_mini = cle
531 elif lj[cle] != []:
532 if convertit_jours_vers_python(lj[cle][0],initialise_erreurs())<convertit_jours_vers_python(lj[cle_mini][0],initialise_erreurs()):
533 cle_mini = cle
534 return cle_mini
535
536 liste_f = []
537 while not(fini(listes_jours)):
538 typedonnee = mini(listes_jours)
539 # On extrait les données dans les deux listes (jours et données)
540 jour = listes_jours[typedonnee].pop(0)
541 donnee = listes_donnees[typedonnee].pop(0)
542
543 if liste_f == [] or jour != liste_f[-1]["age"]: # Si le jour est un "nouveau" jour
544 liste_f.append({"age":jour})
545 # On met à jour l'élément (ou on l'ajoute)
546 liste_f[-1][typedonnee] = donnee
547
548 # Si il y a une date associée, on la met !
549 if listes_dates[typedonnee] != []:
550 date = listes_dates[typedonnee].pop(0)
551 liste_f[-1]["date"] = convertit_date_vers_texte(date)
552
553
554 return liste_f
555
556
557 ### COnversion json vers formulaire
558 # Json -> formulaire HTML
559 def fichier_json_vers_configdonnees(chaine,liste_err):
560 """ prend le json importé (chaine) et l'exporte vers les valeurs du formulaire
561 Renvoyé sous forme de dictionnaire (mais adapté au formulaire web)"""
562 debug("json vers config : Prêt à interpréter le json",liste_err)
563 try:
564 valform = json.loads(chaine)
565 except :
566 erreur("Impossible de lire le fichier json !",liste_err)
567 return {}
568 # Il faut maintenant récupérer les l_jours et l_poids puis les remettre
569 # sous forme de age_i et poids_i
570
571 listes_jours = {}
572 listes_donnees = {}
573 listes_dates = {}
574 for typed in CONFIG["liste_typedonnees"]:
575 if typed == "poids": # pour la rétrocompatibilité
576 listes_jours[typed] = valform.get("data_j",[])
577 listes_donnees[typed] = valform.get("data_p",[])
578 listes_dates[typed] = valform.get("data_dates_"+typed,[])
579 else:
580 listes_jours[typed] = valform.get("data_jours_"+typed,[])
581 listes_donnees[typed] = valform.get("data_"+typed,[])
582 listes_dates[typed] = valform.get("data_dates_"+typed,[])
583
584
585 debug("Avant fusion : listes jours "+str(listes_jours),liste_err)
586 liste_donnees = fusionne_donnees(listes_jours,listes_donnees, listes_dates)
587 debug("Fusion de listes ok. Liste moche : "+str(liste_donnees),liste_err)
588 for i in range(len(liste_donnees)):
589 for (cle,val) in liste_donnees[i].items():
590 valform[cle+"_"+str(i)] = val
591
592 valform["nb_data"] = max(len(liste_donnees) +2,DEFAUT["nb_data"])
593
594 return valform
595
596
597
598 #### Pour l'insertion d'une 2e (ou plus) courbe sur le graphique, ue fonction qui sépare tout ça
599 def eclate_donnees_additionnelles(conf, ljours, ldonnees, symb, couleur):
600 """ conf est la config (on ne garde que le nom) pour un enfant additionnel,
601 ljours et ldonnees les dictionnaires de listes contenant les données.
602 symb est le symbole choisi pour cette courbe additionnelle (déjà vérifié)
603 On fabrique un joli dictionnaire typed -> (conf, lj, ldonnee) avec le nom de l'enfant,
604 et les données pour chaque typed"""
605 #print("test conf avant "+str(ldonnees)+str(ljours))
606
607 retour = {}
608 conf["symbole"] = symb # On ajoute le symbole additionnel
609 conf["couleurcourbe"] = couleur # la couleur
610 for typed in CONFIG["liste_typedonnees"]:
611 retour[typed] = (conf, ljours[typed], ldonnees[typed])
612
613 #print("test "+str(retour))
614 return retour