]>
git.immae.eu Git - perso/Denise/oms.git/blob - gestion_donnees.py
2 # -*- coding: utf-8 -*-
4 from configuration
import CONFIG
,DEFAUT
5 from gestion_erreurs
import *
6 from gestion_couleurs
import *
7 from gestion_unites
import *
13 ### Les données "tournent" selon :
14 ### python -> json -> (export/import) -> formulaire HTML -> données POST -> python etc
16 ############ Fonctions de conversion
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
24 # debug("conversion de "+chaine+" vers un nb de jours",liste_err)
29 chainenombre
+= lettre
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"]
35 elif lettre
== 'm' or lettre
== 'M':
37 agejours
+= int(chainenombre
)*CONFIG
["jours_dans_mois"]
39 elif lettre
== 's' or lettre
== 'S':
41 agejours
+= int(chainenombre
)*CONFIG
["jours_dans_semaine"]
43 elif lettre
== 'j' or lettre
== 'J':
45 agejours
+= int(chainenombre
)
48 # autre caractère : bizarre ?
49 warning("convertit_jour_vers_python : 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
)
54 warning("L'âge est négatif !",liste_err
)
56 # debug("On a convertit ! Résultat : "+str(agejours),liste_err)
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"])
70 chaine
+= str(annees
)+"a"
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"
76 ##########################
78 # fonction qui calcule "auto" le maxi du graphique en fonction du max
79 def calcule_max_graphique(l_jours
):
80 """ calcule l'age maxi sur le graphique"""
82 return CONFIG
["jours_defaut_donneesvides"]
84 jour_maxi
= max(l_jours
)# pas la peine d'aller très au delà du jour max
85 jour_maxi
= int(jour_maxi
* 1.2)+3 # on rajoute un peu
89 def simplifie_nom(chaine
):
90 """ simplifie le nom chaine afin d'en faire une extension
91 pour le nom du fichier. Met tout en minuscules et vire les caractères spéciaux
92 et max 15 caractères"""
97 chaine2
= unidecode
.unidecode(chaine2
)
100 def convertit_donnee_vers_python(chaine
,typedonnee
,liste_err
):
101 """ convertit une chaine vers un float qui est le type donnee voulu.
102 La virgule peut être . ou , et on vire d'éventuels espaces.
103 Taille invalide : on renvoie 0 avec un warning."""
104 chaine2
= chaine
.replace(",",".")
105 chaine2
= chaine2
.replace(" ","")
108 donnee
= float(chaine2
)
110 warning(typedonnee
+" impossible à lire : "+chaine
,liste_err
)
112 if not( 0<=donnee
<CONFIG
[typedonnee
+"_maxi"]):
113 warning(typedonnee
+"incohérent(e) : "+str(donnee
),liste_err
)
118 #########################
121 def convertit_date_vers_python(chaine
,liste_err
):
122 """ prend une chaine comme renvoyée par un champ de formulaire date
123 aaaa-mm-jj et en fait une date python
124 renvoie "" si ne marche pas"""
125 liste
= chaine
.split("-")
127 warning("La date : "+chaine
+" est invalide !",liste_err
)
130 debug("Conversion de la date "+chaine
+". Découpage : "+str(liste
),liste_err
)
132 date
= datetime
.date(int(liste
[0]),int(liste
[1]),int(liste
[2]))
135 warning("Impossible de lire la date "+chaine
+". Format accepté : aaaa-mm-jj",liste_err
)
139 def convertit_date_vers_texte(date
):
140 """ convertit une date python en format texte aaaa-mm-jj"""
144 return (str(date
.year
)+"-"+str(date
.month
)+"-"+str(date
.day
))
147 def delta_date(date1
,datenaissance
):
148 """ renvoie le nombre de jours (entier) entre date1 et datenaissance format "datetime"
149 datenaissance est supposée antérieure. Erreur sinon."""
150 d
= date1
- datenaissance
153 erreur_continue("La différence entre les dates est négative... :/")
158 ################### On regroupe tout ce qui gère les données en une fonction
160 def web_vers_python(data
,liste_err
):
161 """ prend en argument le dictionnaire de requête et renvoie la config, et les
162 tableaux de donnée"""
164 # Régler la configuration
165 config
= gere_configuration(data
,liste_err
)
167 # récupérer les données
170 for typed
in CONFIG
["liste_typedonnees"]:
171 listes_jours
[typed
],listes_donnees
[typed
] = gere_donnees(data
,config
["naissance"],typed
,liste_err
)
173 # Si on a choisi la même échelle de données
174 if config
["memechelle"] == "oui":
175 config
["non_sauve"]["maxi"] = calcule_max_graphique([j
for lj
in listes_jours
.values() for j
in lj
])
176 config
["non_sauve"]["unite"] = choix_unite(config
["non_sauve"]["maxi"])
178 return (config
,listes_jours
,listes_donnees
)
182 ########### Fonctions qui gèretn les données
184 def gere_checkbox(chaine
):
185 """ prend en arg une chaine, et renvoie "oui" si c'est "on" (sortie de la checkbox)
186 et chaîne vide si n'importe quoi d'autre"""
192 def gere_configuration(data
,liste_err
):
193 """ prend en argument le dictionnaire de requête (configuration imparfaite), et
194 construit le dictionnaire de configuration qui va bien.
195 Vérifie que chaque entrée est cohérente évidemment."""
196 configuration
= {"non_sauve": {}
}
198 # Pour le nom, osef qu'il soit vide
199 nom
= data
.get("nom","")
200 # Par contre s'il est trop long on le tronque
201 configuration
["nom"] = nom
[:CONFIG
["longueur_max_nom_bebe"]]
203 sexe
= data
.get("sexe","")
204 if not (sexe
in ["F","M","N"]):
205 warning("Le sexe de l'enfant est invalide ! "+sexe
,liste_err
)
207 configuration
["sexe"] = sexe
209 naissance
= data
.get("naissance","")
211 naissance
= convertit_date_vers_python(naissance
,liste_err
)
212 configuration
["naissance"] = naissance
214 prematurite
= data
.get("prematurite","")
215 j
= convertit_jours_vers_python(prematurite
,liste_err
)
216 configuration
["prematurite"] = convertit_age_vers_texte(j
)
218 configuration
["agecorrige"] = gere_checkbox(data
.get("agecorrige",""))
220 # Type de courbe. Au pire on met P
221 tyc
= data
.get("typecourbe","")
222 if not (tyc
in ["P","Z"]):
224 configuration
["typecourbe"] = tyc
227 unite
= data
.get("unite","")
228 if not (unite
in CONFIG
["liste_unites"]):
230 #warning("L'unité "+unite+" n'est pas reconnue !",liste_err)
231 configuration
["unite"] = unite
234 configuration
["grille"] = gere_checkbox(data
.get("grille",""))
236 # tracer ou non les courbes vides
237 configuration
["tracevide"] = gere_checkbox(data
.get("tracevide",""))
239 # Même échelle sur tous les graphiques
240 configuration
["memechelle"] = gere_checkbox(data
.get("memechelle",""))
243 # maxi. 0 signifie qu'on veut pas de maxi
244 maxi
= data
.get("maxi","")
246 configuration
["maxi"] = 0
248 configuration
["maxi"] = int(convertit_jours_vers_python(maxi
,liste_err
))
250 # dimensions du graphique
251 largeur
= data
.get("largeur","")
253 largeur
= DEFAUT
["largeur_graphique"]
256 largeur
= int(largeur
)
258 warning("La largeur "+largeur
+"est invalide !",liste_err
)
259 largeur
= DEFAUT
["largeur_graphique"]
260 if largeur
> CONFIG
["largeur_graphique_max"]:
261 largeur
= CONFIG
["largeur_graphique_max"]
262 warning("Largeur du graphique trop grande !",liste_err
)
263 elif largeur
< CONFIG
["largeur_graphique_min"]:
264 largeur
= CONFIG
["largeur_graphique_min"]
265 warning("Largeur du graphique trop petite !",liste_err
)
266 configuration
["largeur"] = largeur
268 hauteur
= data
.get("hauteur","")
270 hauteur
= DEFAUT
["hauteur_graphique"]
273 hauteur
= int(hauteur
)
275 warning("La hauteur "+hauteur
+"est invalide !",liste_err
)
276 hauteur
= DEFAUT
["hauteur_graphique"]
277 if hauteur
> CONFIG
["hauteur_graphique_max"]:
278 hauteur
= CONFIG
["hauteur_graphique_max"]
279 warning("Hauteur du graphique trop grande !",liste_err
)
280 elif hauteur
< CONFIG
["hauteur_graphique_min"]:
281 hauteur
= CONFIG
["hauteur_graphique_min"]
282 warning("Hauteur du graphique trop petite !",liste_err
)
283 configuration
["hauteur"] = hauteur
285 # existence et position de la légende
286 configuration
["legende"] = gere_checkbox(data
.get("legende",""))
288 positionlegende
= data
.get("positionlegende","")
289 if not(positionlegende
in ['upper left','upper right','lower left','lower right']):
290 positionlegende
= "upper left"
291 configuration
["positionlegende"] = positionlegende
293 configuration
["prolongercourbes"] = gere_checkbox(data
.get("prolongercourbes",""))
295 configuration
["non_sauve"]["grilleamelio"] = gere_checkbox(data
.get("grilleamelio",""))
297 configuration
["couleurs"] = {}
299 for clecouleur
in DEFAUT
["couleurs"]:
300 coul
= rgb_vers_tuple(data
.get("couleur_"+clecouleur
,""),CONFIG
["couleurs"][clecouleur
],liste_err
)
301 configuration
["couleurs"][clecouleur
] = coul
307 def gere_donnees(data
,naissance
,typedonnee
,liste_err
):
308 """ prend en argument le dictionnaire de requête, et la date de
309 naissance (éventuellement vide), et construit deux listes :
310 l_jours et l_data correspondantes.
311 Il faut donner en argument le type de données : voir
312 CONFIG["liste_typedonnees"]"""
313 if typedonnee
not in CONFIG
["liste_typedonnees"]:
314 warning("gere_donnees : le type de données : "+typedonnee
+" est invalide !! Types acceptés : "+str(CONFIG
["liste_typedonnees"]),liste_err
)
317 # On construit une liste de couples d'abord
321 # On va chercher si y'a des données à donnee_i
322 while typedonnee
+"_"+str(i
) in data
.keys():
323 if data
[typedonnee
+"_"+str(i
)] != "":
324 donnee
= convertit_donnee_vers_python(data
[typedonnee
+"_"+str(i
)],typedonnee
,liste_err
)
325 age
= data
.get("age_"+str(i
),"")
327 age
= convertit_jours_vers_python(age
,liste_err
)
328 liste_donnees
.append((age
,donnee
))
330 date
= data
.get("date_"+str(i
),"")
331 datep
= convertit_date_vers_python(date
,liste_err
)
334 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
)
335 elif datep
!= "": # la date est valide et on a une date de naissance
336 age
= delta_date(datep
,naissance
)
337 liste_donnees
.append((age
,donnee
))
341 liste_donnees
.sort(key
=lambda x
: x
[0])
344 l_jours
= [x
[0] for x
in liste_donnees
]
345 l_donnee
= [x
[1] for x
in liste_donnees
]
347 return (l_jours
,l_donnee
)
350 #### export vers json
352 def donnees_vers_json(l_jours
,l_poids
,l_jourst
,l_taille
,config
):
353 """ retourne le json à renvoyer"""
354 gros_dico
= copy
.deepcopy(config
)
355 l_jours2
= [convertit_age_vers_texte(d
) for d
in l_jours
]
356 l_jourst2
= [convertit_age_vers_texte(d
) for d
in l_jourst
]
357 gros_dico
["data_j"] = l_jours2
358 gros_dico
["data_p"] = l_poids
359 gros_dico
["data_jours_taille"] = l_jourst2
360 gros_dico
["data_taille"] = l_taille
361 # gérer la date de naissance
362 if gros_dico
.get("naissance","") != "":
363 gros_dico
["naissance"] = convertit_date_vers_texte(gros_dico
["naissance"])
365 gros_dico
["maxi"] = convertit_age_vers_texte(gros_dico
["maxi"])
367 for clecouleur
in DEFAUT
["couleurs"]:
368 gros_dico
["couleurs"][clecouleur
] = tuple_vers_rgb(gros_dico
["couleurs"][clecouleur
])
370 # Enlever ce qui ne se sauvegarde pas si y'a
371 if "non_sauve" in gros_dico
:
372 del gros_dico
["non_sauve"]
374 return json
.dumps(gros_dico
, indent
=2,ensure_ascii
=False )
376 def fusionne_donnees(listes_jours
,listes_donnees
):
377 """ prend en argument deux dicos de listes. Chaque liste de jours est associée à une liste
378 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
379 ("age":truc, "donnee1":truc, "donnee2":truc, ...) triée par ordre de jours. Si jamais une des données est vide,
380 le champ du dictionnaire n'est pas rempli"""
381 # nb_donnees = len(listes_jours)
383 """ teste si les listes sont toutes vides """
384 for l
in lj
.values():
390 """ renvoie la clé de la liste où il y a le min """
391 cle_mini
= CONFIG
["liste_typedonnees"][0]
392 for (cle
,liste
) in lj
.items():
393 if lj
[cle_mini
]== []:
396 if convertit_jours_vers_python(lj
[cle
][0],initialise_erreurs())<convertit_jours_vers_python(lj
[cle_mini
][0],initialise_erreurs()):
401 while not(fini(listes_jours
)):
402 typedonnee
= mini(listes_jours
)
403 # On extrait les données dans les deux listes (jours et données)
404 jour
= listes_jours
[typedonnee
].pop(0)
405 donnee
= listes_donnees
[typedonnee
].pop(0)
406 if liste_f
== [] or jour
!= liste_f
[-1]["age"]: # Si le jour est un "nouveau" jour
407 liste_f
.append({"age":jour}
)
408 # On met à jour l'élément
409 liste_f
[-1][typedonnee
] = donnee
415 # Json -> formulaire HTML
416 def fichier_json_vers_configdonnees(chaine
,liste_err
):
417 """ prend le json importé (chaine) et l'exporte vers les valeurs du formulaire """
418 debug("json vers config : Prêt à interpréter le json",liste_err
)
420 valform
= json
.loads(chaine
)
422 erreur("Impossible de lire le fichier json !",liste_err
)
424 # Il faut maintenant récupérer les l_jours et l_poids puis les remettre
425 # sous forme de age_i et poids_i
429 for typed
in CONFIG
["liste_typedonnees"]:
430 if typed
== "poids": # pour la rétrocompatibilité
431 listes_jours
[typed
] = valform
.get("data_j",[])
432 listes_donnees
[typed
] = valform
.get("data_p",[])
434 listes_jours
[typed
] = valform
.get("data_jours_"+typed
,[])
435 listes_donnees
[typed
] = valform
.get("data_"+typed
,[])
437 debug("Avant fusion : listes jours "+str(listes_jours
),liste_err
)
438 liste_donnees
= fusionne_donnees(listes_jours
,listes_donnees
)
439 debug("Fusion de listes ok. Liste moche : "+str(liste_donnees
),liste_err
)
440 for i
in range(len(liste_donnees
)):
441 for (cle
,val
) in liste_donnees
[i
].items():
442 valform
[cle
+"_"+str(i
)] = val
444 valform
["nb_data"] = max(len(liste_donnees
) +2,DEFAUT
["nb_data"])