]>
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 debug
, warning
, erreur
, initialise_erreurs
6 from gestion_couleurs
import rgb_vers_tuple
, tuple_vers_rgb
7 from gestion_unites
import choix_unite
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"
77 ##########################
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"""
83 return CONFIG
["jours_defaut_donneesvides"]
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
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"""
98 chaine2
= unidecode
.unidecode(chaine2
)
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 chaine2
= chaine
.replace(",",".")
106 chaine2
= chaine2
.replace(" ","")
109 donnee
= float(chaine2
)
111 warning(typedonnee
+" impossible à lire : "+chaine
,liste_err
)
114 # Pour le poids, un cas particulier
115 if typedonnee
== "poids" and donnee
> CONFIG
["poids_maxi_conversion"]:
116 donnee
= donnee
/1000 # conversion en grammes
117 if not( 0<=donnee
<CONFIG
[typedonnee
+"_maxi"]):
118 warning(typedonnee
+" incohérent(e) : "+str(donnee
),liste_err
)
123 #########################
126 def convertit_date_vers_python(chaine
,liste_err
):
127 """ prend une chaine comme renvoyée par un champ de formulaire date
128 aaaa-mm-jj et en fait une date python
129 renvoie "" si ne marche pas"""
130 liste
= chaine
.split("-")
132 warning("La date : "+chaine
+" est invalide !",liste_err
)
135 debug("Conversion de la date "+chaine
+". Découpage : "+str(liste
),liste_err
)
137 date
= datetime
.date(int(liste
[0]),int(liste
[1]),int(liste
[2]))
140 warning("Impossible de lire la date "+chaine
+". Format accepté : aaaa-mm-jj",liste_err
)
144 def convertit_date_vers_texte(date
):
145 """ convertit une date python en format texte aaaa-mm-jj"""
149 return (str(date
.year
)+"-"+str(date
.month
)+"-"+str(date
.day
))
152 def delta_date(date1
,datenaissance
):
153 """ renvoie le nombre de jours (entier) entre date1 et datenaissance format "datetime"
154 datenaissance est supposée antérieure. Erreur sinon."""
155 d
= date1
- datenaissance
158 warning("La différence entre les dates est négative... :/")
163 ################### On regroupe tout ce qui gère les données en une fonction
165 def web_vers_python(data
,liste_err
):
166 """ prend en argument le dictionnaire de requête et renvoie la config, et les
167 tableaux de donnée"""
169 # Régler la configuration
170 config
= gere_configuration(data
,liste_err
)
172 # récupérer les données
175 for typed
in CONFIG
["liste_typedonnees"]:
176 listes_jours
[typed
],listes_donnees
[typed
] = gere_donnees(data
,config
["naissance"],typed
,liste_err
)
178 # Si on veut extrapoler au-delà du jour maxi, on adapte
180 # Si on a choisi la même échelle de données
181 if config
["memechelle"] == "oui":
182 config
["non_sauve"]["maxi"] = calcule_max_graphique([j
for lj
in listes_jours
.values() for j
in lj
])
183 # En cas d'extrapolation, on prend le maxi
184 if config
["non_sauve"]["calculextradata_type"] !="" and config
["non_sauve"]["calculextradata_age"]>config
["non_sauve"]["maxi"]:
185 config
["non_sauve"]["maxi"] = int(config
["non_sauve"]["calculextradata_age"]) +1
186 config
["non_sauve"]["unite"] = choix_unite(config
["non_sauve"]["maxi"])
188 return (config
,listes_jours
,listes_donnees
)
192 ########### Fonctions qui gèretn les données web vers python
194 def gere_checkbox(chaine
):
195 """ prend en arg une chaine, et renvoie "oui" si c'est "on" (sortie de la checkbox)
196 et chaîne vide si n'importe quoi d'autre"""
202 def gere_configuration(data
,liste_err
):
203 """ prend en argument le dictionnaire de requête (configuration imparfaite), et
204 construit le dictionnaire de configuration qui va bien.
205 Vérifie que chaque entrée est cohérente évidemment."""
207 configuration
= {"non_sauve": {}
}
209 # Pour le nom, osef qu'il soit vide
210 nom
= data
.get("nom","")
211 # Par contre s'il est trop long on le tronque
212 configuration
["nom"] = nom
[:CONFIG
["longueur_max_nom_bebe"]]
214 sexe
= data
.get("sexe","")
215 if not (sexe
in ["F","M","N"]):
216 warning("Le sexe de l'enfant est invalide ! "+sexe
,liste_err
)
218 configuration
["sexe"] = sexe
220 naissance
= data
.get("naissance","")
222 naissance
= convertit_date_vers_python(naissance
,liste_err
)
223 configuration
["naissance"] = naissance
225 prematurite
= data
.get("prematurite","")
226 j
= convertit_jours_vers_python(prematurite
,liste_err
)
227 configuration
["prematurite"] = convertit_age_vers_texte(j
)
229 configuration
["agecorrige"] = gere_checkbox(data
.get("agecorrige",""))
231 # Type de courbe. Au pire on met P
232 tyc
= data
.get("typecourbe","")
233 if not (tyc
in ["P","Z"]):
235 configuration
["typecourbe"] = tyc
238 unite
= data
.get("unite","")
239 if not (unite
in CONFIG
["liste_unites"]):
241 #warning("L'unité "+unite+" n'est pas reconnue !",liste_err)
242 configuration
["unite"] = unite
245 configuration
["grille"] = gere_checkbox(data
.get("grille",""))
247 # tracer ou non les courbes vides
248 configuration
["tracevide"] = gere_checkbox(data
.get("tracevide",""))
250 # Même échelle sur tous les graphiques
251 configuration
["memechelle"] = gere_checkbox(data
.get("memechelle",""))
254 # maxi. 0 signifie qu'on veut pas de maxi
255 maxi
= data
.get("maxi","")
257 configuration
["maxi"] = 0
259 configuration
["maxi"] = int(convertit_jours_vers_python(maxi
,liste_err
))
261 # dimensions du graphique
262 largeur
= data
.get("largeur","")
264 largeur
= DEFAUT
["largeur_graphique"]
267 largeur
= int(largeur
)
269 warning("La largeur "+largeur
+"est invalide !",liste_err
)
270 largeur
= DEFAUT
["largeur_graphique"]
271 if largeur
> CONFIG
["largeur_graphique_max"]:
272 largeur
= CONFIG
["largeur_graphique_max"]
273 warning("Largeur du graphique trop grande !",liste_err
)
274 elif largeur
< CONFIG
["largeur_graphique_min"]:
275 largeur
= CONFIG
["largeur_graphique_min"]
276 warning("Largeur du graphique trop petite !",liste_err
)
277 configuration
["largeur"] = largeur
279 hauteur
= data
.get("hauteur","")
281 hauteur
= DEFAUT
["hauteur_graphique"]
284 hauteur
= int(hauteur
)
286 warning("La hauteur "+hauteur
+"est invalide !",liste_err
)
287 hauteur
= DEFAUT
["hauteur_graphique"]
288 if hauteur
> CONFIG
["hauteur_graphique_max"]:
289 hauteur
= CONFIG
["hauteur_graphique_max"]
290 warning("Hauteur du graphique trop grande !",liste_err
)
291 elif hauteur
< CONFIG
["hauteur_graphique_min"]:
292 hauteur
= CONFIG
["hauteur_graphique_min"]
293 warning("Hauteur du graphique trop petite !",liste_err
)
294 configuration
["hauteur"] = hauteur
296 # existence et position de la légende
297 configuration
["legende"] = gere_checkbox(data
.get("legende",""))
299 positionlegende
= data
.get("positionlegende","")
300 if not(positionlegende
in ['upper left','upper right','lower left','lower right']):
301 positionlegende
= "upper left"
302 configuration
["positionlegende"] = positionlegende
304 configuration
["couleurs"] = {}
306 for clecouleur
in DEFAUT
["couleurs"]:
307 coul
= rgb_vers_tuple(data
.get("couleur_"+clecouleur
,""),CONFIG
["couleurs"][clecouleur
],liste_err
)
308 configuration
["couleurs"][clecouleur
] = coul
311 configuration
["non_sauve"]["grilleamelio"] = gere_checkbox(data
.get("grilleamelio",""))
314 #### La partie extrapolation n'a pas besoin d'être sauvée
315 configuration
["non_sauve"]["prolongercourbes"] = gere_checkbox(data
.get("prolongercourbes",""))
317 # Valeur par défaut : 1
318 debug(data
.get("nbextradata", "aaargh"), liste_err
)
319 nbextradata
= data
.get("nbextradata",1)
321 nbextradata
= int(nbextradata
)
323 warning("Le nombre de données sur lequel on extrapole est invalide : "+nbextradata
, liste_err
)
325 configuration
["non_sauve"]["nbextradata"] = nbextradata
327 if data
.get("calculextradata_type","") in CONFIG
["liste_typedonnees"]:
328 configuration
["non_sauve"]["calculextradata_type"] = data
.get("calculextradata_type","")
329 configuration
["non_sauve"]["calculextradata_age"] = convertit_jours_vers_python(data
.get("calculextradata_age","0j"),liste_err
)
331 configuration
["non_sauve"]["calculextradata_type"] = ""
332 # On ne met rien dans l'âge, pas la peine
334 ctyped
= data
.get("calculextratemps_type","")
335 if ctyped
in CONFIG
["liste_typedonnees"]:
336 configuration
["non_sauve"]["calculextratemps_type"] = ctyped
337 configuration
["non_sauve"]["calculextratemps_val"] = convertit_donnee_vers_python(data
.get("calculextratemps_val",""), ctyped
, liste_err
)
339 configuration
["non_sauve"]["calculextratemps_type"] = ""
341 # Tracer les calculs sur la grille
342 configuration
["non_sauve"]["calculextradata_trace"] = gere_checkbox(data
.get("calculextradata_trace"))
343 configuration
["non_sauve"]["calculextratemps_trace"] = gere_checkbox(data
.get("calculextratemps_trace"))
350 ## web vers python : données
351 def gere_donnees(data
,naissance
,typedonnee
,liste_err
):
352 """ prend en argument le dictionnaire de requête, et la date de
353 naissance (éventuellement vide), et construit deux listes :
354 l_jours et l_data correspondantes.
355 Il faut donner en argument le type de données : voir
356 CONFIG["liste_typedonnees"]"""
357 if typedonnee
not in CONFIG
["liste_typedonnees"]:
358 warning("gere_donnees : le type de données : "+typedonnee
+" est invalide !! Types acceptés : "+str(CONFIG
["liste_typedonnees"]),liste_err
)
361 # On construit une liste de couples d'abord
365 # On va chercher si y'a des données à donnee_i
366 while typedonnee
+"_"+str(i
) in data
.keys():
367 if data
[typedonnee
+"_"+str(i
)] != "":
368 donnee
= convertit_donnee_vers_python(data
[typedonnee
+"_"+str(i
)],typedonnee
,liste_err
)
369 age
= data
.get("age_"+str(i
),"")
371 age
= convertit_jours_vers_python(age
,liste_err
)
372 liste_donnees
.append((age
,donnee
))
374 date
= data
.get("date_"+str(i
),"")
375 datep
= convertit_date_vers_python(date
,liste_err
)
378 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
)
379 elif datep
!= "": # la date est valide et on a une date de naissance
380 age
= delta_date(datep
,naissance
)
381 liste_donnees
.append((age
,donnee
))
385 liste_donnees
.sort(key
=lambda x
: x
[0])
388 l_jours
= [x
[0] for x
in liste_donnees
]
389 l_donnee
= [x
[1] for x
in liste_donnees
]
391 return (l_jours
,l_donnee
)
395 #### export vers json
397 def donnees_vers_json(l_jours
,l_poids
,l_jourst
,l_taille
,config
):
398 """ retourne le json à renvoyer"""
399 gros_dico
= copy
.deepcopy(config
)
400 l_jours2
= [convertit_age_vers_texte(d
) for d
in l_jours
]
401 l_jourst2
= [convertit_age_vers_texte(d
) for d
in l_jourst
]
402 gros_dico
["data_j"] = l_jours2
403 gros_dico
["data_p"] = l_poids
404 gros_dico
["data_jours_taille"] = l_jourst2
405 gros_dico
["data_taille"] = l_taille
406 # gérer la date de naissance
407 if gros_dico
.get("naissance","") != "":
408 gros_dico
["naissance"] = convertit_date_vers_texte(gros_dico
["naissance"])
410 gros_dico
["maxi"] = convertit_age_vers_texte(gros_dico
["maxi"])
412 for clecouleur
in DEFAUT
["couleurs"]:
413 gros_dico
["couleurs"][clecouleur
] = tuple_vers_rgb(gros_dico
["couleurs"][clecouleur
])
415 # Enlever ce qui ne se sauvegarde pas si y'a
416 if "non_sauve" in gros_dico
:
417 del gros_dico
["non_sauve"]
419 return json
.dumps(gros_dico
, indent
=2,ensure_ascii
=False )
421 def fusionne_donnees(listes_jours
,listes_donnees
):
422 """ prend en argument deux dicos de listes. Chaque liste de jours est associée à une liste
423 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
424 ("age":truc, "donnee1":truc, "donnee2":truc, ...) triée par ordre de jours. Si jamais une des données est vide,
425 le champ du dictionnaire n'est pas rempli"""
428 """ teste si les listes sont toutes vides """
429 for l
in lj
.values():
435 """ renvoie la clé de la liste où il y a le min """
436 cle_mini
= CONFIG
["liste_typedonnees"][0]
437 for (cle
,liste
) in lj
.items():
438 if lj
[cle_mini
]== []:
441 if convertit_jours_vers_python(lj
[cle
][0],initialise_erreurs())<convertit_jours_vers_python(lj
[cle_mini
][0],initialise_erreurs()):
446 while not(fini(listes_jours
)):
447 typedonnee
= mini(listes_jours
)
448 # On extrait les données dans les deux listes (jours et données)
449 jour
= listes_jours
[typedonnee
].pop(0)
450 donnee
= listes_donnees
[typedonnee
].pop(0)
451 if liste_f
== [] or jour
!= liste_f
[-1]["age"]: # Si le jour est un "nouveau" jour
452 liste_f
.append({"age":jour}
)
453 # On met à jour l'élément
454 liste_f
[-1][typedonnee
] = donnee
459 ### COnversion json vers formulaire
460 # Json -> formulaire HTML
461 def fichier_json_vers_configdonnees(chaine
,liste_err
):
462 """ prend le json importé (chaine) et l'exporte vers les valeurs du formulaire
463 Renvoyé sous forme de dictionnaire (mais adapté au formulaire web)"""
464 debug("json vers config : Prêt à interpréter le json",liste_err
)
466 valform
= json
.loads(chaine
)
468 erreur("Impossible de lire le fichier json !",liste_err
)
470 # Il faut maintenant récupérer les l_jours et l_poids puis les remettre
471 # sous forme de age_i et poids_i
475 for typed
in CONFIG
["liste_typedonnees"]:
476 if typed
== "poids": # pour la rétrocompatibilité
477 listes_jours
[typed
] = valform
.get("data_j",[])
478 listes_donnees
[typed
] = valform
.get("data_p",[])
480 listes_jours
[typed
] = valform
.get("data_jours_"+typed
,[])
481 listes_donnees
[typed
] = valform
.get("data_"+typed
,[])
483 debug("Avant fusion : listes jours "+str(listes_jours
),liste_err
)
484 liste_donnees
= fusionne_donnees(listes_jours
,listes_donnees
)
485 debug("Fusion de listes ok. Liste moche : "+str(liste_donnees
),liste_err
)
486 for i
in range(len(liste_donnees
)):
487 for (cle
,val
) in liste_donnees
[i
].items():
488 valform
[cle
+"_"+str(i
)] = val
490 valform
["nb_data"] = max(len(liste_donnees
) +2,DEFAUT
["nb_data"])