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