X-Git-Url: https://git.immae.eu/?p=perso%2FDenise%2Foms.git;a=blobdiff_plain;f=gestion_donnees.py;h=6911320ecbc1309072bff8883ae282e4a9bb5da7;hp=2153a55f814cf0cb6660d71ca4842e475b3a5544;hb=a63fb483bebf4574bc2f515d7dedf260fbf8092c;hpb=3d7da80ac11bb87ab441fb51f93b738be02de2c3 diff --git a/gestion_donnees.py b/gestion_donnees.py index 2153a55..6911320 100644 --- a/gestion_donnees.py +++ b/gestion_donnees.py @@ -46,7 +46,7 @@ def convertit_jours_vers_python(chaine,liste_err): chainenombre = "" elif lettre != ' ': # autre caractère : bizarre ? - warning("convertit_jour_vers_python : caractère invalide : "+lettre,liste_err) + warning("problème à la conversion de "+chaine+". Caractère invalide : "+lettre,liste_err) # à la fin s'il reste qqch on le garde dans les jours if chainenombre != "": agejours += int(chainenombre) @@ -101,9 +101,13 @@ def simplifie_nom(chaine): def convertit_donnee_vers_python(chaine,typedonnee,liste_err): """ convertit une chaine vers un float qui est le type donnee voulu. La virgule peut être . ou , et on vire d'éventuels espaces. - Taille invalide : on renvoie 0 avec un warning.""" + Taille invalide : on renvoie 0 avec un warning. + Si la chaine est en fait déjà au format float, on laisse tel quel""" + if type(chaine) == float: + return chaine chaine2 = chaine.replace(",",".") chaine2 = chaine2.replace(" ","") + try: donnee = float(chaine2) @@ -126,8 +130,12 @@ def convertit_donnee_vers_python(chaine,typedonnee,liste_err): def convertit_date_vers_python(chaine,liste_err): """ prend une chaine comme renvoyée par un champ de formulaire date aaaa-mm-jj et en fait une date python - renvoie "" si ne marche pas""" - liste = chaine.split("-") + renvoie "" si ne marche pas. + Si jamais la date est au format avec des / ça devrait passer aussi.""" + if "/" in chaine: + liste = chaine.split("/") + else: + liste = chaine.split("-") if len(liste) != 3: warning("La date : "+chaine+" est invalide !",liste_err) return "" @@ -146,8 +154,8 @@ def convertit_date_vers_texte(date): if date == "": return "" else: - return (str(date.year)+"-"+str(date.month)+"-"+str(date.day)) - + #return (str(date.year)+"-"+str(date.month)+"-"+str(date.day)) + return str(date) def delta_date(date1,datenaissance): """ renvoie le nombre de jours (entier) entre date1 et datenaissance format "datetime" @@ -160,14 +168,17 @@ def delta_date(date1,datenaissance): return jours + ################### On regroupe tout ce qui gère les données en une fonction -def web_vers_python(data,liste_err): +def web_vers_python(data,liste_err, court=False): """ prend en argument le dictionnaire de requête et renvoie la config, et les - tableaux de donnée""" + tableaux de données + court : si True est précisé, on ne met que le nom dans la config (enfant + additionnel)""" # Régler la configuration - config = gere_configuration(data,liste_err) + config = gere_configuration(data,liste_err, court) # récupérer les données listes_jours = {} @@ -175,9 +186,14 @@ def web_vers_python(data,liste_err): for typed in CONFIG["liste_typedonnees"]: listes_jours[typed],listes_donnees[typed] = gere_donnees(data,config["naissance"],typed,liste_err) + # Si on veut extrapoler au-delà du jour maxi, on adapte + # Si on a choisi la même échelle de données - if config["memechelle"] == "oui": + if config.get("memechelle") == "oui": config["non_sauve"]["maxi"] = calcule_max_graphique([j for lj in listes_jours.values() for j in lj]) + # En cas d'extrapolation, on prend le maxi + if config["non_sauve"]["calculextradata_type"] !="" and config["non_sauve"]["calculextradata_age"]>config["non_sauve"]["maxi"]: + config["non_sauve"]["maxi"] = int(config["non_sauve"]["calculextradata_age"]) +1 config["non_sauve"]["unite"] = choix_unite(config["non_sauve"]["maxi"]) return (config,listes_jours,listes_donnees) @@ -194,10 +210,20 @@ def gere_checkbox(chaine): else: return "" -def gere_configuration(data,liste_err): +def gere_symbole(chaine): + """ prend en arg une chaîne genre "o", ">" et vérifie si c'est un symbole valide. + Renvoie ce symbole-là ou le défaut""" + if chaine in CONFIG["liste_symboles"]: + return chaine + else: + return DEFAUT["symbole"] + +def gere_configuration(data,liste_err, court=False): """ prend en argument le dictionnaire de requête (configuration imparfaite), et construit le dictionnaire de configuration qui va bien. - Vérifie que chaque entrée est cohérente évidemment.""" + Vérifie que chaque entrée est cohérente évidemment. + court : si mis à True, on ne met que le nom dans la configuraion, + ainsi que la date de naissance et le sexe""" # Initialisation configuration = {"non_sauve": {}} @@ -205,138 +231,155 @@ def gere_configuration(data,liste_err): nom = data.get("nom","") # Par contre s'il est trop long on le tronque configuration["nom"] = nom[:CONFIG["longueur_max_nom_bebe"]] - - sexe = data.get("sexe","") - if not (sexe in ["F","M","N"]): - warning("Le sexe de l'enfant est invalide ! "+sexe,liste_err) - sexe = "N" - configuration["sexe"] = sexe - + naissance = data.get("naissance","") if naissance !="": naissance = convertit_date_vers_python(naissance,liste_err) configuration["naissance"] = naissance - prematurite = data.get("prematurite","") - j = convertit_jours_vers_python(prematurite,liste_err) - configuration["prematurite"] = convertit_age_vers_texte(j) - - configuration["agecorrige"] = gere_checkbox(data.get("agecorrige","")) - - # Type de courbe. Au pire on met P - tyc = data.get("typecourbe","") - if not (tyc in ["P","Z"]): - tyc = "P" - configuration["typecourbe"] = tyc - - # unité - unite = data.get("unite","") - if not (unite in CONFIG["liste_unites"]): - unite = "" - #warning("L'unité "+unite+" n'est pas reconnue !",liste_err) - configuration["unite"] = unite - - # grille - configuration["grille"] = gere_checkbox(data.get("grille","")) - - # tracer ou non les courbes vides - configuration["tracevide"] = gere_checkbox(data.get("tracevide","")) + sexe = data.get("sexe","") + if not (sexe in ["F","M","N"]): + warning("Le sexe de l'enfant est invalide. "+sexe,liste_err) + sexe = "N" + configuration["sexe"] = sexe + + if not(court): + + prematurite = data.get("prematurite","") + j = convertit_jours_vers_python(prematurite,liste_err) + configuration["prematurite"] = convertit_age_vers_texte(j) - # Même échelle sur tous les graphiques - configuration["memechelle"] = gere_checkbox(data.get("memechelle","")) - - - # maxi. 0 signifie qu'on veut pas de maxi - maxi = data.get("maxi","") - if maxi == "": - configuration["maxi"] = 0 - else: - configuration["maxi"] = int(convertit_jours_vers_python(maxi,liste_err)) + configuration["agecorrige"] = gere_checkbox(data.get("agecorrige","")) + + # Type de courbe. Au pire on met P + tyc = data.get("typecourbe","") + if not (tyc in ["P","Z"]): + tyc = "P" + configuration["typecourbe"] = tyc + + # unité + unite = data.get("unite","") + if not (unite in CONFIG["liste_unites"]): + unite = "" + #warning("L'unité "+unite+" n'est pas reconnue !",liste_err) + configuration["unite"] = unite + + # grille + configuration["grille"] = gere_checkbox(data.get("grille","")) + + # tracer ou non les courbes vides + configuration["tracevide"] = gere_checkbox(data.get("tracevide","")) + + # Même échelle sur tous les graphiques + configuration["memechelle"] = gere_checkbox(data.get("memechelle","")) - # dimensions du graphique - largeur = data.get("largeur","") - if largeur == "": - largeur = DEFAUT["largeur_graphique"] - else: - try: - largeur = int(largeur) - except: - warning("La largeur "+largeur+"est invalide !",liste_err) + + # maxi. 0 signifie qu'on veut pas de maxi + maxi = data.get("maxi","") + if maxi == "": + configuration["maxi"] = 0 + else: + configuration["maxi"] = int(convertit_jours_vers_python(maxi,liste_err)) + + # dimensions du graphique + largeur = data.get("largeur","") + if largeur == "": largeur = DEFAUT["largeur_graphique"] - if largeur > CONFIG["largeur_graphique_max"]: - largeur = CONFIG["largeur_graphique_max"] - warning("Largeur du graphique trop grande !",liste_err) - elif largeur < CONFIG["largeur_graphique_min"]: - largeur = CONFIG["largeur_graphique_min"] - warning("Largeur du graphique trop petite !",liste_err) - configuration["largeur"] = largeur - - hauteur = data.get("hauteur","") - if hauteur == "": - hauteur = DEFAUT["hauteur_graphique"] - else: - try: - hauteur = int(hauteur) - except: - warning("La hauteur "+hauteur+"est invalide !",liste_err) + else: + try: + largeur = int(largeur) + except: + warning("La largeur "+largeur+"est invalide !",liste_err) + largeur = DEFAUT["largeur_graphique"] + if largeur > CONFIG["largeur_graphique_max"]: + largeur = CONFIG["largeur_graphique_max"] + warning("Largeur du graphique trop grande !",liste_err) + elif largeur < CONFIG["largeur_graphique_min"]: + largeur = CONFIG["largeur_graphique_min"] + warning("Largeur du graphique trop petite !",liste_err) + configuration["largeur"] = largeur + + hauteur = data.get("hauteur","") + if hauteur == "": hauteur = DEFAUT["hauteur_graphique"] - if hauteur > CONFIG["hauteur_graphique_max"]: - hauteur = CONFIG["hauteur_graphique_max"] - warning("Hauteur du graphique trop grande !",liste_err) - elif hauteur < CONFIG["hauteur_graphique_min"]: - hauteur = CONFIG["hauteur_graphique_min"] - warning("Hauteur du graphique trop petite !",liste_err) - configuration["hauteur"] = hauteur - - # existence et position de la légende - configuration["legende"] = gere_checkbox(data.get("legende","")) - - positionlegende = data.get("positionlegende","") - if not(positionlegende in ['upper left','upper right','lower left','lower right']): - positionlegende = "upper left" - configuration["positionlegende"] = positionlegende + else: + try: + hauteur = int(hauteur) + except: + warning("La hauteur "+hauteur+"est invalide !",liste_err) + hauteur = DEFAUT["hauteur_graphique"] + if hauteur > CONFIG["hauteur_graphique_max"]: + hauteur = CONFIG["hauteur_graphique_max"] + warning("Hauteur du graphique trop grande !",liste_err) + elif hauteur < CONFIG["hauteur_graphique_min"]: + hauteur = CONFIG["hauteur_graphique_min"] + warning("Hauteur du graphique trop petite !",liste_err) + configuration["hauteur"] = hauteur + + # existence et position de la légende + configuration["legende"] = gere_checkbox(data.get("legende","")) + + positionlegende = data.get("positionlegende","") + if not(positionlegende in ['upper left','upper right','lower left','lower right']): + positionlegende = "upper left" + configuration["positionlegende"] = positionlegende + + configuration["couleurs"] = {} + # gérer les couleurs + for clecouleur in DEFAUT["couleurs"]: + coul = rgb_vers_tuple(data.get("couleur_"+clecouleur,""),DEFAUT["couleurs"].get(clecouleur, ""),liste_err) + configuration["couleurs"][clecouleur] = coul + + # symbole + configuration["symbole"] = gere_symbole( data.get("symbole", "")) - configuration["couleurs"] = {} - # gérer les couleurs - for clecouleur in DEFAUT["couleurs"]: - coul = rgb_vers_tuple(data.get("couleur_"+clecouleur,""),CONFIG["couleurs"][clecouleur],liste_err) - configuration["couleurs"][clecouleur] = coul - - - configuration["non_sauve"]["grilleamelio"] = gere_checkbox(data.get("grilleamelio","")) - - - #### La partie extrapolation n'a pas besoin d'être sauvée - configuration["non_sauve"]["prolongercourbes"] = gere_checkbox(data.get("prolongercourbes","")) - - # Valeur par défaut : 1 - debug(data.get("nbextradata", "aaargh"), liste_err) - nbextradata = data.get("nbextradata",1) - try: - nbextradata = int(nbextradata) - except: - warning("Le nombre de données sur lequel on extrapole est invalide : "+nbextradata, liste_err) - nbextradata = 1 - configuration["non_sauve"]["nbextradata"] = nbextradata + configuration["non_sauve"]["grilleamelio"] = gere_checkbox(data.get("grilleamelio","")) - if data.get("calculextradata_type","") in CONFIG["liste_typedonnees"]: - configuration["non_sauve"]["calculextradata_type"] = data.get("calculextradata_type","") - configuration["non_sauve"]["calculextradata_age"] = convertit_jours_vers_python(data.get("calculextradata_age","0j"),liste_err) - else: - configuration["non_sauve"]["calculextradata_type"] = "" - # On ne met rien dans l'âge, pas la peine - ctyped = data.get("calculextratemps_type","") - if ctyped in CONFIG["liste_typedonnees"]: - configuration["non_sauve"]["calculextratemps_type"] = ctyped - configuration["non_sauve"]["calculextratemps_val"] = convertit_donnee_vers_python(data.get("calculextratemps_val",""), ctyped, liste_err) - else: - configuration["non_sauve"]["calculextratemps_type"] = "" + #### La partie extrapolation n'a pas besoin d'être sauvée + configuration["non_sauve"]["prolongercourbes"] = gere_checkbox(data.get("prolongercourbes","")) - # Tracer les calculs sur la grille - configuration["non_sauve"]["calculextradata_trace"] = gere_checkbox(data.get("calculextradata_trace")) - configuration["non_sauve"]["calculextratemps_trace"] = gere_checkbox(data.get("calculextratemps_trace")) - + # Valeur par défaut : 1 + debug(data.get("nbextradata", "aaargh"), liste_err) + nbextradata = data.get("nbextradata",1) + try: + nbextradata = int(nbextradata) + except: + warning("Le nombre de données sur lequel on extrapole est invalide : "+nbextradata, liste_err) + nbextradata = 1 + configuration["non_sauve"]["nbextradata"] = nbextradata + + if data.get("calculextradata_type","") in CONFIG["liste_typedonnees"]: + configuration["non_sauve"]["calculextradata_type"] = data.get("calculextradata_type","") + configuration["non_sauve"]["calculextradata_age"] = convertit_jours_vers_python(data.get("calculextradata_age","0j"),liste_err) + else: + configuration["non_sauve"]["calculextradata_type"] = "" + # On ne met rien dans l'âge, pas la peine + + ctyped = data.get("calculextratemps_type","") + if ctyped in CONFIG["liste_typedonnees"]: + configuration["non_sauve"]["calculextratemps_type"] = ctyped + configuration["non_sauve"]["calculextratemps_val"] = convertit_donnee_vers_python(data.get("calculextratemps_val",""), ctyped, liste_err) + else: + configuration["non_sauve"]["calculextratemps_type"] = "" + + # Tracer les calculs sur la grille + configuration["non_sauve"]["calculextradata_trace"] = gere_checkbox(data.get("calculextradata_trace")) + configuration["non_sauve"]["calculextratemps_trace"] = gere_checkbox(data.get("calculextratemps_trace")) + + + ### Gestion des repères additionnels + configuration["liste_reperes"] = [] + i=0 + while "repere_texte_"+str(i) in data: # Tant qu'il y a des trucs définis + debug("Repère trouvé", liste_err) + age=data.get("repere_age_"+str(i), "") + trace=gere_checkbox(data.get("repere_trace_"+str(i), "")) + if age !="": + agec=convertit_jours_vers_python(age, liste_err) + texte = data.get("repere_texte_"+str(i), "") # Même si le texte est vide, osef + configuration["liste_reperes"].append({"typed": "age", "donnee": agec, "texte": texte, "trace": trace}) + i+=1 return configuration @@ -358,8 +401,8 @@ def gere_donnees(data,naissance,typedonnee,liste_err): i = 0 # On va chercher si y'a des données à donnee_i - while typedonnee+"_"+str(i) in data.keys(): - if data[typedonnee+"_"+str(i)] != "": + while "age_"+str(i) in data.keys(): + if data.get(typedonnee+"_"+str(i), "") != "": # si la donne de ce type existe donnee = convertit_donnee_vers_python(data[typedonnee+"_"+str(i)],typedonnee,liste_err) age = data.get("age_"+str(i),"") if age !="": @@ -385,6 +428,44 @@ def gere_donnees(data,naissance,typedonnee,liste_err): return (l_jours,l_donnee) +# web vers python : enfants additionnels +def gere_enfants_additionnels(data, files, liste_err): + """ data est le dictionnaire de requête. + files est le dictionnaire des fichiers (flask.request.files). + Renvoie les enfants additionnels sous forme de liste de dictionnaires : + {typed: (conf, lj, ldonnees)} + Dans conf y'a les infos qu'il faut pour tracer la courbe additionnelle voulue. + """ + + enfants_add = [] # Enfants additionnels + # Les enfants additionnels commencent à 2 (puis 3, 4, etc) + i=2 + while "couleur_donnees_"+str(i) in data: # Tant qu'il y a des données de ce type + if 'fichier_donnees_'+str(i) in files: # Un enfant à ajouter + fichier = files["fichier_donnees_"+str(i)] + chaine = fichier.read() # On récupère la chaîne + if len(chaine)>=5: # Si elle a une longueur à peu près raisonnable + debug("Un fichier de données additionnel trouvé", liste_err) + formulaire_2 = fichier_json_vers_configdonnees(chaine, liste_err) + if formulaire_2 =={}: + warning("Le fichier de données additionnel est vide ou mal formaté", liste_err) + else: + debug("Form 2 : "+str(formulaire_2), liste_err) + # Récupérer sous forme python + conf2, ljours2, listes_donnees2 = web_vers_python(formulaire_2,liste_err, court=True) + debug("Form 2 données travaillées "+str(ljours2)+str(listes_donnees2), liste_err) + # Le symbole et la couleur + symb2 = gere_symbole(data.get("symbole_donnees_"+str(i))) + coul2 = rgb_vers_tuple(data.get("couleur_donnees_"+str(i),""),CONFIG["couleurs"]["cadretxt"],liste_err) + enfant2 = eclate_donnees_additionnelles(conf2, ljours2, listes_donnees2, symb2, coul2) + enfants_add.append(enfant2) + i+=1 + + return enfants_add + + + + # python vers Json #### export vers json @@ -392,6 +473,7 @@ def gere_donnees(data,naissance,typedonnee,liste_err): def donnees_vers_json(l_jours,l_poids,l_jourst,l_taille,config): """ retourne le json à renvoyer""" gros_dico = copy.deepcopy(config) + gros_dico["version"] = CONFIG["version"] l_jours2 = [convertit_age_vers_texte(d) for d in l_jours] l_jourst2 = [convertit_age_vers_texte(d) for d in l_jourst] gros_dico["data_j"] = l_jours2 @@ -401,8 +483,18 @@ def donnees_vers_json(l_jours,l_poids,l_jourst,l_taille,config): # gérer la date de naissance if gros_dico.get("naissance","") != "": gros_dico["naissance"] = convertit_date_vers_texte(gros_dico["naissance"]) + # Calcul de toutes les dates de données + l_dates_poids = [convertit_date_vers_texte( config["naissance"] + datetime.timedelta(days=jours) ) for jours in l_jours] + l_dates_taille = [convertit_date_vers_texte( config["naissance"] + datetime.timedelta(days=jours) ) for jours in l_jourst] + gros_dico["data_dates_poids"]= l_dates_poids + gros_dico["data_dates_taille"] = l_dates_taille + + # gérer l'age maxi gros_dico["maxi"] = convertit_age_vers_texte(gros_dico["maxi"]) + # Gérer les repères + for i in range(len(gros_dico["liste_reperes"])): + gros_dico["liste_reperes"][i]["donnee"] = convertit_age_vers_texte(gros_dico["liste_reperes"][i]["donnee"]) # gérer les couleurs for clecouleur in DEFAUT["couleurs"]: gros_dico["couleurs"][clecouleur] = tuple_vers_rgb(gros_dico["couleurs"][clecouleur]) @@ -413,11 +505,12 @@ def donnees_vers_json(l_jours,l_poids,l_jourst,l_taille,config): return json.dumps(gros_dico, indent=2,ensure_ascii=False ) -def fusionne_donnees(listes_jours,listes_donnees): +def fusionne_donnees(listes_jours,listes_donnees, listes_dates): """ prend en argument deux dicos de listes. Chaque liste de jours est associée à une liste 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 - ("age":truc, "donnee1":truc, "donnee2":truc, ...) triée par ordre de jours. Si jamais une des données est vide, - le champ du dictionnaire n'est pas rempli""" + {"age":truc, "donnee1":truc, "donnee2":truc, ...} triée par ordre de jours. Si jamais une des données est vide, + le champ du dictionnaire n'est pas rempli + Le troisième paquet de listes (les dates) peut être vide ou bien simiaire : même clés.""" def fini(lj): """ teste si les listes sont toutes vides """ @@ -443,11 +536,18 @@ def fusionne_donnees(listes_jours,listes_donnees): # On extrait les données dans les deux listes (jours et données) jour = listes_jours[typedonnee].pop(0) donnee = listes_donnees[typedonnee].pop(0) + if liste_f == [] or jour != liste_f[-1]["age"]: # Si le jour est un "nouveau" jour liste_f.append({"age":jour}) - # On met à jour l'élément + # On met à jour l'élément (ou on l'ajoute) liste_f[-1][typedonnee] = donnee + # Si il y a une date associée, on la met ! + if listes_dates[typedonnee] != []: + date = listes_dates[typedonnee].pop(0) + liste_f[-1]["date"] = convertit_date_vers_texte(date) + + return liste_f @@ -467,16 +567,20 @@ def fichier_json_vers_configdonnees(chaine,liste_err): listes_jours = {} listes_donnees = {} + listes_dates = {} for typed in CONFIG["liste_typedonnees"]: if typed == "poids": # pour la rétrocompatibilité listes_jours[typed] = valform.get("data_j",[]) listes_donnees[typed] = valform.get("data_p",[]) + listes_dates[typed] = valform.get("data_dates_"+typed,[]) else: listes_jours[typed] = valform.get("data_jours_"+typed,[]) listes_donnees[typed] = valform.get("data_"+typed,[]) + listes_dates[typed] = valform.get("data_dates_"+typed,[]) + debug("Avant fusion : listes jours "+str(listes_jours),liste_err) - liste_donnees = fusionne_donnees(listes_jours,listes_donnees) + liste_donnees = fusionne_donnees(listes_jours,listes_donnees, listes_dates) debug("Fusion de listes ok. Liste moche : "+str(liste_donnees),liste_err) for i in range(len(liste_donnees)): for (cle,val) in liste_donnees[i].items(): @@ -487,4 +591,21 @@ def fichier_json_vers_configdonnees(chaine,liste_err): return valform - \ No newline at end of file + +#### Pour l'insertion d'une 2e (ou plus) courbe sur le graphique, ue fonction qui sépare tout ça +def eclate_donnees_additionnelles(conf, ljours, ldonnees, symb, couleur): + """ conf est la config (on ne garde que le nom) pour un enfant additionnel, + ljours et ldonnees les dictionnaires de listes contenant les données. + symb est le symbole choisi pour cette courbe additionnelle (déjà vérifié) + On fabrique un joli dictionnaire typed -> (conf, lj, ldonnee) avec le nom de l'enfant, + et les données pour chaque typed""" + #print("test conf avant "+str(ldonnees)+str(ljours)) + + retour = {} + conf["symbole"] = symb # On ajoute le symbole additionnel + conf["couleurcourbe"] = couleur # la couleur + for typed in CONFIG["liste_typedonnees"]: + retour[typed] = (conf, ljours[typed], ldonnees[typed]) + + #print("test "+str(retour)) + return retour \ No newline at end of file