import base64
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
-from configuration import *
+from configuration import config_init
from gestion_erreurs import *
from gestion_donnees import *
from gestion_unites import *
from faq import *
-
-
app = flask.Flask(__name__)
@app.route('/',methods=['POST','GET'])
if 'fichier_donnees' in flask.request.files:
## charger les donneés dans le formulaire
fichier = flask.request.files['fichier_donnees']
-
- val_form2 = fichier_json_vers_configdonnees(fichier,liste_err)
+ chaine = fichier.read()
+ val_form2 = fichier_json_vers_configdonnees(chaine,liste_err)
+ if val_form2 == {}:
+ return flask.render_template("index.html",err=liste_err[2]+liste_err[1]+liste_err[0],valform=val_form)
val_form.update(val_form2)
#return str(val_form['nb_data'])
return flask.render_template("index.html",err=liste_err[1],valform=val_form)
return flask.render_template("changelog.html",table = table_version,err=[])
-
if __name__ == "__main__":
+ print("Mode debug maison : "+str(niveau_debug))
app.run(host='0.0.0.0',debug=True)
\ No newline at end of file
# -*- coding: utf-8 -*-
### Toutes les "constantes" de config importés pour ce qui concerne les courbes
+CONFIG = {}
# fichiers
-chemin_oms = "data_OMS/"
+CONFIG["chemin_oms"] = "data_OMS/"
+
+CONFIG["fichiersOMS"] = {
+ "perc_garcon": "wfa_boys_p_exp.txt",
+ "perc_fille": "wfa_girls_p_exp.txt",
+ "z_garcon": "wfa_boys_z_exp.txt",
+ "z_fille": "wfa_girls_z_exp.txt",
+ "perc_mixte": "wfa_mix_p_exp.txt",
+ "z_mixte": "wfa_mix_z_exp.txt"
+ }
-f_poids_perc_garcon = chemin_oms+"wfa_boys_p_exp.txt"
-f_poids_perc_fille = chemin_oms+"wfa_girls_p_exp.txt"
-f_poids_z_garcon = chemin_oms+"wfa_boys_z_exp.txt"
-f_poids_z_fille = chemin_oms+"wfa_girls_z_exp.txt"
-f_poids_z_mixte = chemin_oms+"wfa_mix_z_exp.txt"
-f_poids_perc_mixte = chemin_oms+"wfa_mix_p_exp.txt"
+# ajouter le chemin
+for (cle,val) in CONFIG["fichiersOMS"].items():
+ CONFIG["fichiersOMS"][cle] = CONFIG["chemin_oms"]+val
# Pour les courbes percentiles
-liste_data_choisie_p = [(5,1),(7,5),(8,10),(10,25),(11,50),(12,75), (14,90),
+CONFIG["liste_data_choisie_p"] = [(5,1),(7,5),(8,10),(10,25),(11,50),(12,75), (14,90),
(15,95),(17,99)]
-liste_data_choisie_p.sort(reverse=True)
+CONFIG["liste_data_choisie_p"].sort(reverse = True)
# pour le sigma : (colonne,sigma)
-liste_data_choisie_z = [(2,-3),(3,-2),(4,-1),(5,0),(6,1),(7,2),(8,3)]
-liste_data_choisie_z.sort(reverse=True)
+CONFIG["liste_data_choisie_z"] = [(2,-3),(3,-2),(4,-1),(5,0),(6,1),(7,2),(8,3)]
+CONFIG["liste_data_choisie_z"].sort(reverse=True)
# config, côté python
-CONFIG = {}
-
# les unités acceptées
CONFIG["liste_unites"] = ["jours", "semaines", "mois", "années"]
-# nombre de jours dans autre chose
-jours_dans_mois = 30.4375
-jours_dans_annee = 365.25
-jours_dans_semaine = 7
+# nombre de jours dans les autres unités
CONFIG["jours_dans_mois"] = 30.4375
CONFIG["jours_dans_annee"] = 365.25
CONFIG["jours_dans_semaine"] = 7
CONFIG["jours_mini_courbe"] = 10
CONFIG["jours_defaut_donneesvides"]= round(6*CONFIG["jours_dans_mois"]) # si données vides, 6 mois
# poids max (protection)
-poids_maxi = 80
+CONFIG["poids_maxi"] = 80
# taille max du nom
-longueur_max_nom_bebe = 100
+CONFIG["longueur_max_nom_bebe"] = 100
#largeurs et hauteurs min et max
-largeur_graphique_max = 40
-largeur_graphique_min = 3
-hauteur_graphique_max = 40
-hauteur_graphique_min = 2
+CONFIG["largeur_graphique_max"] = 30
+CONFIG["largeur_graphique_min"] = 3
+
+CONFIG["hauteur_graphique_max"] = 30
+CONFIG["hauteur_graphique_min"] = 2
CONFIG["couleurs"] = {
"courbe1" : (0,0,1),
DEFAUT["couleurs"]["fond"]= "#FFFFFF" # blanc
DEFAUT["couleurs"]["cadretxt"] = "#000000" # noir
DEFAUT["couleurs"]["grille"] = "#7f7f7f" # gris
-#couleur_defaut_1 = "#0000FF" # bleu
-#couleur_defaut_2 = "#00FF00" # vert
-#couleur_defaut_3 = "#FF0000" # rouge
-
-#couleur_defaut_1_tuple = (0,0,1)
-#couleur_defaut_2_tuple = (0,1,0)
-#couleur_defaut_3_tuple = (1,0,0)
-
-# couleurs par défaut fond
-#couleur_defaut_fond = "#FFFFFF"
-#couleur_defaut_fond_tuple = (1,1,1)
-#couleur_defaut_cadretxt = "#000000"
-#couleur_defaut_cadretxt_tuple = (0,0,0)
-#couleur_defaut_grille = "#7f7f7f"
-#couleur_defaut_grille_tuple = (0.5,0.5,0.5)
-#couleur_defaut_erreur = (0,0,0) # noir
+# Remplissage du formulaire, autres
+DEFAUT["age_0"]= "0j"
+DEFAUT["legende"] = "oui"
+DEFAUT["positionlegende"] = "hg"
# initialiser la config
def config_init():
- c = DEFAUT.copy()
- c.update({
- "age_0": "0j",
- "legende": "oui",
- "positionlegende": "hg",
-# "couleur1": couleur_defaut_1,
-# "couleur2": couleur_defaut_2,
-# "couleur3": couleur_defaut_3,
-# "couleur_fond": couleur_defaut_fond,
-# "couleur_grille": couleur_defaut_grille,
-# "couleur_cadretxt": couleur_defaut_cadretxt
- })
-
- return c
+ return DEFAUT.copy()
"Utilisation","Comment sauvegarder mes données pour une prochaine fois ?","L'outil ne propose pas de sauvegarde en ligne, car il ne garde aucune donnée personnelle. Vous pouvez télécharger vos propres données (dans un format adapté) en cliquant sur « Télécharger les données ». Enregistrez ce fichier quelque part. Pour les réutiliser, utilisez la partie ""Importer le fichier de données"" puis ""Charger les données"" : les champs seront alors pré-remplis et vous pourrez les modifier ou ajouter des données (par exemple une pesée récente)."
-"Utilisation","Je ne comprends pas comment saisir les données âge/poids !","Pour chaque pesée, vous avez le choix entre donner son âge, ou donner la date de la pesée. Pour la date de la pesée, il faut que vous ayiez rempli la date de naissance (sinon l'outil ne peut pas calculer l'âge !). Pour l'âge, vous pouvez indiquer en années, mois, semaines, jours, et mélanger plusieurs unités. Par exemple 2m3j pour « 2 mois 3 jours ». Ne remplissez qu'un des deux champs : âge ou date. Puis saisissez le poids en kilogrammes dans la colonne de droite. <br>
+"Utilisation","Je ne comprends pas comment saisir les données âge/poids !","Pour chaque pesée, vous avez le choix entre donner son âge, ou donner la date de la pesée. Pour la date de la pesée, il faut que vous ayez rempli la date de naissance (sinon l'outil ne peut pas calculer l'âge !). Pour l'âge, vous pouvez indiquer en années, mois, semaines, jours, et mélanger plusieurs unités. Par exemple 2m3j pour « 2 mois 3 jours ». Ne remplissez qu'un des deux champs : âge ou date. Puis saisissez le poids en kilogrammes dans la colonne de droite. <br>
Si vous manquez de lignes, vous pouvez cliquer sur ""Ajouter des lignes"" pour avoir plus de champs de données. Il n'y a pas de problème à laisser des champs vides, ils seront simplement ignorés."
"Utilisation","Une erreur s'affiche en bas de ma courbe, je ne comprends pas.","Si le message d'erreur n'est pas clair pour vous, c'est qu'il est probablement dû à un souci interne. Si vous le pouvez, contactez l'administratrice, en citant le message d'erreur et en donnant les données qui ont amené à cette erreur (fichier de données, ou données saisies). Merci d'avance ! :)"
-"Courbe","À quoi sert ce site ?","Il sert à tracer la courbe de poids des bébés et jeunes enfants en fonction de leur âge, comme sur le carnet de santé, et la compare aux courbes de références de l'Organisation Mondiale de la Santé."
+"Courbe","À quoi sert ce site ?","Il sert à tracer la courbe de poids des bébés et jeunes enfants en fonction de leur âge, comme sur le carnet de santé, et la compare aux courbes de références de l'Organisation Mondiale de la Santé. Ces courbes sont a priori prévues pour des enfants nés à terme en bonne santé. Pour un enfant prématuré ou à situation particulière, référez vous à un.e professionnel.le de santé."
"Courbe","Pourquoi des courbes OMS et pas des courbes françaises ?","Les particularités de ces données sont les suivantes : bébés et enfants choisis dans différents pays du monde (et non dans un seul pays), dans des familles raisonnablement aisées (pour éviter les problèmes liés à la malnutrition), et des bébés allaités. Plus d'infos <a href=""https://www.who.int/childgrowth/standards/technical_report/en/"">ici</a> (en anglais). L'idée générale était de donner une « référence » de croissance de l'enfant de l'espèce humaine. Cela ne veut pas dire que les courbes du carnet de santé français sont mauvaises (si vous avez celles datant d'avant 2018, elles sont un peu anciennes par contre), de fait, elles sont très peu différentes. Pour les autres pays je ne sais pas, je n'ai pas comparé."
"Courbe","C'est quoi la différence entre la courbe percentiles et moyenne/écarts-types ?","Ce sont deux manières de comparer son enfant à la « référence ». Si vous ne savez pas la différence entre une moyenne et une médiane, retenez juste que ce sont des calculs un peu différents, mais cela ne change pas l'allure de la courbe, si votre enfant « suit » un couloir sur un type de courbe ce sera pareil sur l'autre. Vous pouvez au pire choisir celle que vous préférez. ;)
<br>Pour les curieuses et les curieux :
<ul>
-<li>Médiane et percentiles : si votre enfant est (par exemple) au 30e percentile, cela signifie que 29% des enfants sont plus petits que lui, et 31% sont plus grands. Cela donne une idée de où se situe l'enfant par rapport aux autres. <a href=""https://fr.wikipedia.org/wiki/Centile"">Plus d'infos (maths)</a></li>
-<li>Moyenne et écart-types : la moyenne est obtenue en faisant la somme et en divisant par le nombre d'enfants. L'écart-type (noté σ ou z) est une sorte d'« écart moyen » à la moyenne. On regarde donc généralement la moyenne, la moyenne + 1σ, moyenne +2σ, moyenne -1 σ, etc. <a href=""https://fr.wikipedia.org/wiki/%C3%89cart_type"">Plus d'infos (maths)</a></li>
+<li>Médiane et percentiles : si votre enfant est (par exemple) au 30e percentile, cela signifie qu'il est dans la 30e « tranche » sur 100 tranches. Autrement dit 29% des enfants sont plus légers que lui, et 70% sont plus lourds. Cela donne une idée de où il se situe par rapport aux autres. <a href=""https://fr.wikipedia.org/wiki/Centile"">Plus d'infos (maths)</a></li>
+<li>Moyenne et écart-types : la moyenne est obtenue en faisant la somme et en divisant par le nombre d'enfants. L'écart-type (noté σ) est une sorte d'« écart moyen » à la moyenne. On regarde donc généralement la moyenne, la moyenne + 1σ, moyenne +2σ, moyenne -1 σ, etc. <a href=""https://fr.wikipedia.org/wiki/%C3%89cart_type"">Plus d'infos (maths)</a></li>
</ul>
Si la répartition des données est « bonne » (on parle de gaussienne entre matheuses et matheux), alors la moyenne correspond à la médiane, la moyenne + 1σ correspond environ au 84e percentile, la moyenne -1σ correspond au 16e percentile. Pour ces données, il semble que ce soit assez proche d'une gaussienne."
+"Version 0.6","21/06/2020","<ul>
+<li>Amélioration des boutons par cerise</li>
+<li>Encore du code amélioré derrière</li>
+</ul>"
+
"Version 0.5","19/06/2020","<ul>
<li>Amélioration du code (ça se voit pas mais...)</li>
<li>Des changements dans le format d'export des données. Les anciens fichiers json vont probablement bugguer un peu.</li>
@author: sekhmet
"""
from gestion_erreurs import *
-from configuration import *
from gestion_couleurs import *
import csv
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from configuration import *
+from configuration import CONFIG,DEFAUT
from gestion_erreurs import *
from gestion_couleurs import *
import datetime
else:
if lettre == 'a' or lettre == 'A':
# On a trouvé l'année, on ajoute tout ce qui est trouvé jusque là
- agejours += int(chainenombre)*jours_dans_annee
+ agejours += int(chainenombre)*CONFIG["jours_dans_annee"]
chainenombre = ""
elif lettre == 'm' or lettre == 'M':
# On a trouvé le mois
- agejours += int(chainenombre)*jours_dans_mois
+ agejours += int(chainenombre)*CONFIG["jours_dans_mois"]
chainenombre = ""
elif lettre == 's' or lettre == 'S':
# la semaine
- agejours += int(chainenombre)*jours_dans_semaine
+ agejours += int(chainenombre)*CONFIG["jours_dans_semaine"]
chainenombre = ""
elif lettre == 'j' or lettre == 'J':
# On a trouvé le jour
def convertit_age_vers_texte(nombre):
""" convertit un nombre de jours en un truc plus lisible en mois, années, jours
et renvoie une chaîne sous la forme 3a2m1j par exemple"""
- annees = int(nombre / jours_dans_annee)
- restant = nombre - annees*jours_dans_annee
- mois = int(restant/jours_dans_mois)
- jours= round(nombre - mois*jours_dans_mois - annees*jours_dans_annee)
+ annees = int(nombre / CONFIG["jours_dans_annee"])
+ restant = nombre - annees*CONFIG["jours_dans_annee"]
+ mois = int(restant/CONFIG["jours_dans_mois"])
+ jours= round(nombre - mois*CONFIG["jours_dans_mois"] - annees*CONFIG["jours_dans_annee"])
chaine = ""
if annees >0:
except:
warning("Poids impossible à lire : "+chaine,liste_err)
poids = 0
- if not( 0<=poids<poids_maxi):
+ if not( 0<=poids<CONFIG["poids_maxi"]):
warning("Poids incohérent : "+str(poids),liste_err)
poids = 0
return poids
# Pour le nom, osef qu'il soit vide
nom = data.get("nom","")
# Par contre s'il est trop long on le tronque
- configuration["nom"] = nom[:longueur_max_nom_bebe]
+ configuration["nom"] = nom[:CONFIG["longueur_max_nom_bebe"]]
sexe = data.get("sexe","")
if not (sexe in ["F","M","N"]):
except:
warning("La largeur "+largeur+"est invalide !",liste_err)
largeur = DEFAUT["largeur_graphique"]
- if largeur > largeur_graphique_max:
- largeur = largeur_graphique_max
- warning("Largeur trop grande !",liste_err)
- elif largeur < largeur_graphique_min:
- largeur = largeur_graphique_min
- warning("Largeur trop petite !",liste_err)
+ 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","")
except:
warning("La hauteur "+hauteur+"est invalide !",liste_err)
hauteur = DEFAUT["hauteur_graphique"]
- if hauteur > hauteur_graphique_max:
- hauteur = hauteur_graphique_max
- warning("Hauteur trop grande !",liste_err)
- elif hauteur < hauteur_graphique_min:
- hauteur = hauteur_graphique_min
- warning("Hauteur trop petite !",liste_err)
+ 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
return json.dumps(gros_dico, indent=2,ensure_ascii=False )
-def fichier_json_vers_configdonnees(fichier,liste_err):
- """ prend le json importé et l'exporte vers les valeurs du formulaire """
- chaine = fichier.read()
- valform = json.loads(chaine)
+def fichier_json_vers_configdonnees(chaine,liste_err):
+ """ prend le json importé (chaine) et l'exporte vers les valeurs du formulaire """
+ debug("json vers config : Prêt à interpréter le json",liste_err)
+ try:
+ valform = json.loads(chaine)
+ except :
+ erreur("Impossible de lire le fichier json !",liste_err)
+ return {}
# Il faut maintenant récupérer les l_jours et l_poids puis les remettre
# sous forme de age_i et poids_i
l_jours= valform.get("data_j",[])
# -*- coding: utf-8 -*-
## Gestion des erreurs en flask
+import sys
-
-niveau_debug = True
-
+niveau_debug = ("debug" in sys.argv)
def initialise_erreurs():
""" retourne deux trois vides, erreurs fatales (0), warnings(1), debug:"""
listeerreurs[1].append("Alerte : "+message)
def debug(message,liste_erreurs):
+ global niveau_debug
if niveau_debug:
+ #if app.debug == True:
print("##Debug : "+message)
liste_erreurs[2].append("# Debug : "+message)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from configuration import *
+from configuration import CONFIG
from gestion_erreurs import *
##################### outils pour affichage et choix de l'unité
""" en fonction de l'âge maxi, on choisit une unité pertinente : jours, mois, année"""
if maxi<40:
return "jours"
- elif maxi<25*jours_dans_mois:
+ elif maxi<25*CONFIG["jours_dans_mois"]:
return "mois"
else:
return "années"
if unite=="jours":
return jours
elif unite=="mois":
- return jours/jours_dans_mois
+ return jours/CONFIG["jours_dans_mois"]
elif unite=="années":
- return jours/jours_dans_annee
+ return jours/CONFIG["jours_dans_annee"]
elif unite=="semaines":
- return jours/jours_dans_semaine
+ return jours/CONFIG["jours_dans_semaine"]
else:
warning("erreur sur l'unité : "+unite+" On laisse en jours",liste_err)
return jours
}
.icon_button {
- display: inline-block;
+ display: block;
cursor: pointer;
position: relative;
}
{% extends "base.html" %}
{% block contenu %}
<p>Si vous avez une question, un commentaire, une idée, vous pouvez me contacter à l'adresse suivante : oms arobase le nom de domaine.</p>
-<p>Vous pouvez également me retrouver sur le <a href="https://forum.lllfrance.org/">forum LLL</a>, sous le nom de Sekhmet.</p>
+<p>Vous pouvez également me retrouver sur le <a href="https://forum.lllfrance.org/">forum LLL</a>, sous le pseudo de Sekhmet.</p>
<p><strong>Merci</strong> à celles et ceux qui m'ont aidée à mettre en place cet outil :
<ul>
<h2>Données de l'enfant</h2>
-<!--<h3>Importer un fichier </h3>
-<div class="bouton" onclick="affiche_cache('import_donnees',this)">Afficher</div>-->
<div id="import_donnees">
<form action="/" method="post" enctype="multipart/form-data" id="form_import_donnees">
<span class="icon_legend">Importer un fichier</span>
</label>
<input type="file" name="fichier_donnees" id="fichier_donnees" oninput="upload_file()">
- <!--<input type="submit" name="valider_fichier" value="Charger les données">-->
</form>
</div>
<form id="donnees_enfant">
-
+<!-- saisie des données -->
<h3>Informations sur l'enfant</h3>
<div>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from configuration import *
+from configuration import CONFIG
from gestionOMS import *
from gestion_unites import *
import matplotlib.pyplot as plt
+# Essentiellement, la fonction qui trace la courbe
+
+
def cree_figure(conf,l_jours,l_poids,liste_err):
debug("debut de cree_figure",liste_err)
try:
- liste_data_labels_p,liste_data_labels_z = renvoie_liste_labels(conf,liste_data_choisie_p,liste_data_choisie_z,liste_err)
+ liste_data_labels_p,liste_data_labels_z = renvoie_liste_labels(conf,CONFIG["liste_data_choisie_p"],CONFIG["liste_data_choisie_z"],liste_err)
except:
erreur("bug avec liste data labels",liste_err)
return ""
# percentiles
liste_data_labels = liste_data_labels_p
if conf["sexe"] == "M":
- fichier_oms = f_poids_perc_garcon
+ fichier_oms = CONFIG["fichiersOMS"]["perc_garcon"]#f_poids_perc_garcon
titre += " (percentiles, garçon)"
elif conf["sexe"] == "F":
- fichier_oms = f_poids_perc_fille
+ fichier_oms = CONFIG["fichiersOMS"]["perc_fille"]
titre += " (percentiles, fille)"
else:
- fichier_oms = f_poids_perc_mixte
+ fichier_oms = CONFIG["fichiersOMS"]["perc_mixte"]
titre += " (percentiles)"
elif conf["typecourbe"] == "Z":
liste_data_labels = liste_data_labels_z
- if conf["sexe"] == "G":
- fichier_oms = f_poids_z_garcon
+ if conf["sexe"] == "M":
+ fichier_oms = CONFIG["fichiersOMS"]["z_garcon"]
titre += " (moyenne et écarts-types, garçon)"
elif conf["sexe"] == "F":
- fichier_oms = f_poids_z_fille
+ fichier_oms = CONFIG["fichiersOMS"]["z_fille"]
titre += " (moyenne et écarts-types, fille)"
else:
- fichier_oms = f_poids_z_mixte
+ fichier_oms = CONFIG["fichiersOMS"]["z_mixte"]
titre += " (moyenne et écarts-types)"
else:
erreur("Type de courbe invalide"+conf["typecourbe"],liste_err)
return ""
- debug("cree_figure : géré le type de courbe ok",liste_err)
+ debug("cree_figure : géré le type de courbe ok. Liste des data labels : "+str(liste_data_labels),liste_err)
+ debug("Fichier d'où extraire les données : "+fichier_oms,liste_err)
# Si y'a un nom on met "courbe de machin"
if conf["nom"] !="":
(colonne_min,_,_) = liste_data_labels[-1]
(colonne_max,_,_) = liste_data_labels[0]
+ debug("cree_figure : colonnes min et max : "+str(colonne_min)+" "+str(colonne_max),liste_err)
+
poids_min = min(extraire_colonne(t,colonne_min,jour_maxi))
poids_max = max(extraire_colonne(t,colonne_max,jour_maxi))
if l_jours != []: