]> git.immae.eu Git - perso/Denise/oms.git/blob - gestion_donnees.py
bouton pour changer les dates bougé
[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 *
6 from gestion_couleurs import *
7 import datetime
8 import json
9 import unidecode
10 import copy
11
12 ### Les données "tournent" selon :
13 ### python -> json -> (export/import) -> formulaire HTML -> données POST -> python etc
14
15 ############ Fonctions de conversion
16
17 def convertit_jours_vers_python(chaine,liste_err):
18 """ convertit une chaine de type 1a 3m 1s 10j en jours
19 Renvoie un nombre de jours en float
20 Si un des caractères n'est ni un nombre, ni une lettre "autorisée" ni une espace,
21 on affiche un warning et on ignore ce caractère
22 """
23 chainenombre = ""
24 agejours = 0.
25 for lettre in chaine:
26 if lettre.isdigit():
27 chainenombre += lettre
28 else:
29 if lettre == 'a' or lettre == 'A':
30 # On a trouvé l'année, on ajoute tout ce qui est trouvé jusque là
31 agejours += int(chainenombre)*CONFIG["jours_dans_annee"]
32 chainenombre = ""
33 elif lettre == 'm' or lettre == 'M':
34 # On a trouvé le mois
35 agejours += int(chainenombre)*CONFIG["jours_dans_mois"]
36 chainenombre = ""
37 elif lettre == 's' or lettre == 'S':
38 # la semaine
39 agejours += int(chainenombre)*CONFIG["jours_dans_semaine"]
40 chainenombre = ""
41 elif lettre == 'j' or lettre == 'J':
42 # On a trouvé le jour
43 agejours += int(chainenombre)
44 chainenombre = ""
45 elif lettre != ' ':
46 # autre caractère : bizarre ?
47 warning("convertit_jour_vers_python : caractère invalide : "+lettre,liste_err)
48 # à la fin s'il reste qqch on le garde dans les jours
49 if chainenombre != "":
50 agejours += int(chainenombre)
51 if agejours<0:
52 warning("L'âge est négatif !",liste_err)
53 agejours = 0
54 return agejours
55
56 def convertit_age_vers_texte(nombre):
57 """ convertit un nombre de jours en un truc plus lisible en mois, années, jours
58 et renvoie une chaîne sous la forme 3a2m1j par exemple"""
59 annees = int(nombre / CONFIG["jours_dans_annee"])
60 restant = nombre - annees*CONFIG["jours_dans_annee"]
61 mois = int(restant/CONFIG["jours_dans_mois"])
62 jours= round(nombre - mois*CONFIG["jours_dans_mois"] - annees*CONFIG["jours_dans_annee"])
63
64 chaine = ""
65 if annees >0:
66 chaine += str(annees)+"a"
67 if mois >0:
68 chaine += str(mois)+"m"
69 if jours>0 or nombre ==0: # si c'est la naissance, faut beien écrire 0j quand même
70 chaine += str(jours)+"j"
71 return chaine
72 ##########################
73
74
75 def simplifie_nom(chaine):
76 """ simplifie le nom chaine afin d'en faire une extension
77 pour le nom du fichier. Met tout en minuscules et vire les caractères spéciaux
78 et max 15 caractères"""
79 chaine2 = ""
80 for l in chaine:
81 if l.isalpha():
82 chaine2+=l
83 chaine2 = unidecode.unidecode(chaine2)
84 return chaine2[:15]
85
86 def convertit_poids_vers_python(chaine,liste_err):
87 """ convertit une chaine vers un float qui est le poids.
88 On gère notamment la virgule, et on enlève les espaces
89 Un poids invalide -> on renvoie 0 avec un warning"""
90 chaine2 = chaine.replace(",",".")
91 chaine2 = chaine2.replace(" ","")
92
93 try:
94 poids = float(chaine2)
95 except:
96 warning("Poids impossible à lire : "+chaine,liste_err)
97 poids = 0
98 if not( 0<=poids<CONFIG["poids_maxi"]):
99 warning("Poids incohérent : "+str(poids),liste_err)
100 poids = 0
101 return poids
102
103 #def convertit_poids_vers_texte(poids):
104 # """ convertit un poids vers du texte. Rien à dire là pour l'instant """
105 # return str(poids)
106
107 #########################
108
109 def convertit_date_vers_python(chaine,liste_err):
110 """ prend une chaine comme renvoyée par un champ de formulaire date
111 aaaa-mm-jj et en fait une date python
112 renvoie "" si ne marche pas"""
113 liste = chaine.split("-")
114 if len(liste) != 3:
115 warning("La date : "+chaine+" est invalide !",liste_err)
116 return ""
117 else:
118 debug("Conversion de la date "+chaine+". Découpage : "+str(liste),liste_err)
119 try:
120 date = datetime.date(int(liste[0]),int(liste[1]),int(liste[2]))
121 except:
122 date = ""
123 warning("Impossible de lire la date "+chaine+". Format accepté : aaaa-mm-jj",liste_err)
124 return date
125
126 def convertit_date_vers_texte(date):
127 """ convertit une date python en format texte aaaa-mm-jj"""
128 if date == "":
129 return ""
130 else:
131 return (str(date.year)+"-"+str(date.month)+"-"+str(date.day))
132
133
134 def delta_date(date1,datenaissance):
135 """ renvoie le nombre de jours (entier) entre date1 et datenaissance format "datetime"
136 datenaissance est supposée antérieure. Erreur sinon."""
137 d = date1 - datenaissance
138 jours = d.days
139 if jours<0:
140 erreur_continue("La différence entre les dates est négative... :/")
141 return -1
142 return jours
143
144
145 ########### Fonctions qui gèretn les données
146
147
148 def gere_configuration(data,liste_err):
149 """ prend en argument le dictionnaire de requête (configuration imparfaite), et
150 construit le dictionnaire de configuration qui va bien.
151 Vérifie que chaque entrée est cohérente évidemment."""
152 configuration = {}
153
154 # Pour le nom, osef qu'il soit vide
155 nom = data.get("nom","")
156 # Par contre s'il est trop long on le tronque
157 configuration["nom"] = nom[:CONFIG["longueur_max_nom_bebe"]]
158
159 sexe = data.get("sexe","")
160 if not (sexe in ["F","M","N"]):
161 warning("Le sexe de l'enfant est invalide ! "+sexe,liste_err)
162 sexe = "N"
163 configuration["sexe"] = sexe
164
165 naissance = data.get("naissance","")
166 if naissance !="":
167 naissance = convertit_date_vers_python(naissance,liste_err)
168 configuration["naissance"] = naissance
169
170 # Type de courbe. Au pire on met P
171 tyc = data.get("typecourbe","")
172 if not (tyc in ["P","Z"]):
173 tyc = "P"
174 configuration["typecourbe"] = tyc
175
176 # unité
177 unite = data.get("unite","")
178
179 if not (unite in CONFIG["liste_unites"]):
180 unite = ""
181 #warning("L'unité "+unite+" n'est pas reconnue !",liste_err)
182 configuration["unite"] = unite
183
184 # grille
185 grille = data.get("grille","")
186 if grille != "on":
187 configuration["grille"] = ""
188 else:
189 configuration["grille"] = "oui"
190
191 # maxi. 0 signifie qu'on veut pas de maxi
192 maxi = data.get("maxi","")
193 if maxi == "":
194 configuration["maxi"] = 0
195 else:
196 configuration["maxi"] = int(convertit_jours_vers_python(maxi,liste_err))
197
198 # dimensions du graphique
199 largeur = data.get("largeur","")
200 if largeur == "":
201 largeur = DEFAUT["largeur_graphique"]
202 else:
203 try:
204 largeur = int(largeur)
205 except:
206 warning("La largeur "+largeur+"est invalide !",liste_err)
207 largeur = DEFAUT["largeur_graphique"]
208 if largeur > CONFIG["largeur_graphique_max"]:
209 largeur = CONFIG["largeur_graphique_max"]
210 warning("Largeur du graphique trop grande !",liste_err)
211 elif largeur < CONFIG["largeur_graphique_min"]:
212 largeur = CONFIG["largeur_graphique_min"]
213 warning("Largeur du graphique trop petite !",liste_err)
214 configuration["largeur"] = largeur
215
216 hauteur = data.get("hauteur","")
217 if hauteur == "":
218 hauteur = DEFAUT["hauteur_graphique"]
219 else:
220 try:
221 hauteur = int(hauteur)
222 except:
223 warning("La hauteur "+hauteur+"est invalide !",liste_err)
224 hauteur = DEFAUT["hauteur_graphique"]
225 if hauteur > CONFIG["hauteur_graphique_max"]:
226 hauteur = CONFIG["hauteur_graphique_max"]
227 warning("Hauteur du graphique trop grande !",liste_err)
228 elif hauteur < CONFIG["hauteur_graphique_min"]:
229 hauteur = CONFIG["hauteur_graphique_min"]
230 warning("Hauteur du graphique trop petite !",liste_err)
231 configuration["hauteur"] = hauteur
232
233 # existence et position de la légende
234 legende = data.get("legende","")
235 if legende =="":
236 legende = "non"
237 elif legende=="on":
238 legende = "oui"
239 else:
240 legende = "oui"
241 configuration["legende"] = legende
242
243 positionlegende = data.get("positionlegende","")
244 if not(positionlegende in ['upper left','upper right','lower left','lower right']):
245 positionlegende = "upper left"
246 configuration["positionlegende"] = positionlegende
247
248
249 configuration["couleurs"] = {}
250 # gérer les couleurs
251 for clecouleur in DEFAUT["couleurs"]:
252 coul = rgb_vers_tuple(data.get("couleur_"+clecouleur,""),CONFIG["couleurs"][clecouleur],liste_err)
253 configuration["couleurs"][clecouleur] = coul
254
255
256 return configuration
257
258
259 def gere_donneespoids(data,naissance,liste_err):
260 """ prend en argument le dictionnaire de requête, et la date de naissance
261 (éventuellement vide) et construit les deux listes l_jours et l_poids"""
262
263 # On construit la liste des couples
264 liste_donnees = []
265
266 i = 0
267 # On va chercher si y'a des données à poids_i
268 while "poids_"+str(i) in data.keys():
269 if data["poids_"+str(i)] != "":
270 poids = convertit_poids_vers_python(data["poids_"+str(i)],liste_err)
271 age = data.get("age_"+str(i),"")
272 if age !="":
273 age = convertit_jours_vers_python(age,liste_err)
274 liste_donnees.append((age,poids))
275 else:
276 date = data.get("date_"+str(i),"")
277 datep = convertit_date_vers_python(date,liste_err)
278 # on vérifie la date
279 if naissance == "":
280 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)
281 elif datep != "": # la date est valide et on a une date de naissance
282 age = delta_date(datep,naissance)
283 liste_donnees.append((age,poids))
284 i+=1
285
286 # Trier la liste
287 liste_donnees.sort(key=lambda x : x[0])
288
289 # splitter la liste
290 l_jours = [x[0] for x in liste_donnees]
291 l_poids = [x[1] for x in liste_donnees]
292
293 return (l_jours,l_poids)
294
295
296
297
298 def donnees_vers_json(l_jours,l_poids,config):
299 """ retourne le json à renvoyer"""
300 gros_dico = copy.deepcopy(config)
301 l_jours2 = [convertit_age_vers_texte(d) for d in l_jours]
302 gros_dico["data_j"] = l_jours2
303 gros_dico["data_p"] = l_poids
304 # gérer la date de naissance
305 if gros_dico.get("naissance","") != "":
306 gros_dico["naissance"] = convertit_date_vers_texte(gros_dico["naissance"])
307 # gérer l'age maxi
308 gros_dico["maxi"] = convertit_age_vers_texte(gros_dico["maxi"])
309 # gérer les couleurs
310 # for cle in ["couleur1", "couleur2", "couleur3", "couleur_fond","couleur_grille","couleur_cadretxt"]:
311 # gros_dico[cle] = tuple_vers_rgb(gros_dico[cle])
312 for clecouleur in DEFAUT["couleurs"]:
313 gros_dico["couleurs"][clecouleur] = tuple_vers_rgb(gros_dico["couleurs"][clecouleur])
314
315
316
317 return json.dumps(gros_dico, indent=2,ensure_ascii=False )
318
319
320 def fichier_json_vers_configdonnees(chaine,liste_err):
321 """ prend le json importé (chaine) et l'exporte vers les valeurs du formulaire """
322 debug("json vers config : Prêt à interpréter le json",liste_err)
323 try:
324 valform = json.loads(chaine)
325 except :
326 erreur("Impossible de lire le fichier json !",liste_err)
327 return {}
328 # Il faut maintenant récupérer les l_jours et l_poids puis les remettre
329 # sous forme de age_i et poids_i
330 l_jours= valform.get("data_j",[])
331 l_poids=valform.get("data_p",[])
332 if len(l_poids) != len(l_jours):
333 warning("Lecture du json : les données sont incohérentes (listes de taille différentes et/ou pb de lecture")
334 long = min(len(l_jours),len(l_poids))
335 else:
336 long = len(l_jours)
337 for i in range(long):
338 valform["age_"+str(i)] = l_jours[i]
339 valform["poids_"+str(i)] = l_poids[i]
340
341 valform["nb_data"] = max(long +2,DEFAUT["nb_data"])
342
343 return valform
344
345
346