## charger les donneés dans le formulaire
fichier = flask.request.files['fichier_donnees']
- val_form = fichier_texte_vers_configdonnees(fichier,liste_err)
-
+ val_form = fichier_json_vers_configdonnees(fichier,liste_err)
+ #return str(val_form['nb_data'])
return flask.render_template("index.html",err=liste_err[1],valform=val_form)
@app.route('/apropos')
config = gere_configuration(data,liste_err)
l_jours,l_poids = gere_donneespoids(data,config["naissance"],liste_err)
- # préparer l'export
- texte = configuration_vers_texte(config)
- texte += donnees_poids_vers_texte(l_jours,l_poids)
+ texte = donnees_vers_json(l_jours,l_poids,config)
+
+ # noter le nom de l'enfant pour l'export
+ nomenfant = simplifie_nom(config['nom'])
# créer la figure
try:
reponse = flask.jsonify({ "result":result,
"messages": liste_err[1],
"image": base64.b64encode(output.getvalue()).decode("ascii"),
- "export_txt": texte})
+ "export_txt": texte,
+ "nomenfant": nomenfant})
else:
reponse = flask.jsonify({ "result":result,
"messages": liste_err[1]+liste_err[0],
"image": "",
- "export_txt": texte})
+ "export_txt": texte,
+ "nomenfant": nomenfant})
return reponse
#return flask.Response(base64.b64encode(output.getvalue()), mimetype='text/plain')
elif ext == "png" and result == "success":
#return flask.Response(base64.b64encode(output.getvalue()), mimetype='text/plain')
-@app.route("/export_donnees",methods=['POST'])
-def export_donnees():
- # exporte les données au format texte
- liste_err = initialise_erreurs()
- data = flask.request.form
-
- config = gere_configuration(data,liste_err)
- l_jours,l_poids = gere_donneespoids(data,config["naissance"],liste_err)
-
- texte = configuration_vers_texte(config)
- texte += donnees_poids_vers_texte(l_jours,l_poids)
-
- return flask.Response(texte,mimetype="text/plain")
+#@app.route("/export_donnees",methods=['POST'])
+#def export_donnees():
+# # exporte les données au format texte
+# liste_err = initialise_erreurs()
+# data = flask.request.form
+#
+# config = gere_configuration(data,liste_err)
+# l_jours,l_poids = gere_donneespoids(data,config["naissance"],liste_err)
+#
+# texte = configuration_vers_texte(config)
+# texte += donnees_poids_vers_texte(l_jours,l_poids)
+#
+# #texte = donnees_vers_json(l_jours,l_poids,config)
+# #return flask.Response(texte,mimetype="application/json")
+# return flask.Response(texte,mimetype="text/plain")
@app.route("/faq")
def faq():
-"Utilisation","Comment tracer la courbe ?","Remplissez le formulaire (vous pouvez laisser par défaut tous les champs qui ne vous « parlent » pas), puis cliquez sur ""Je veux la courbe !"". La courbe s'affiche plus bas. Si vous modifiez les données du formulaire, vous pouvez re-cliquer sur ce bouton pour la mettre à jour. Et c'est tout !"
+"Utilisation","Comment tracer la courbe ?","Remplissez le formulaire (vous pouvez laisser par défaut tous les champs qui ne vous « parlent » pas), puis cliquez sur ""Je veux la courbe !"". La courbe s'affiche plus bas. Si vous modifiez les données du formulaire, vous pouvez re-cliquer sur ce bouton pour la mettre à jour. Et c'est tout ! Vous pouvez également télécharger la courbe en cliquant sur le bouton juste en dessous."
-"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. Quand vous demandez à tracer la courbe, il s'affiche en bas un champ de texte. Si vous copiez-collez ce contenu et le gardez dans un fichier, vous n'aurez pas à re-saisir les données. 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","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>
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","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","Pourquoi faire des courbes de référence différenciées selon le sexe ?","Il se trouve que l'évolution de poids diffère selon ce critère. S'il s'agissait d'une simple différence de poids en moyenne, une courbe mixte serait adaptée (puisqu'on s'intéresse à l'allure de la courbe et non au poids absolu), mais les courbes sont légèrement différentes.<br>
Vous trouverez <a href=""static/courbe_oms_gf_1an.png"">ici</a> et <a href=""static/courbe_oms_gf_5ans.png"">là</a> deux graphiques où on voit les courbes de poids garçons et filles superposées."
-"Courbe","Pourquoi une courbe de « neutre » ? Et c'est quoi ?","Pourquoi pas ? C'est simplement une courbe moyennée des courbes garçon/fille. Ce n'est absolument pas une référence réelle, mais ça ne coûtait rien d'essayer. Je n'ai aucune connaissance dans les données de croissance des enfants intersexes, et s'il existe pour ces enfants des courbes de référence, peut-être que celle-ci aurait du sens, peut-être pas. Elle permet accessoirement de générer une courbe « acceptable » même si on n'a pas spécifié sexe masculin/féminin. Au pire vous pouvez considérer que c'est une « courbe jouet »."
+"Courbe","Pourquoi une courbe de « neutre » ? Et c'est quoi ?","Pourquoi pas ? C'est simplement une courbe moyennée des courbes garçon/fille. Ce n'est absolument pas une vraie référence, mais ça ne coûtait rien d'essayer. Je n'ai aucune connaissance dans les données de croissance des enfants intersexes, et s'il existe pour ces enfants des courbes de référence, peut-être que celle-ci aurait du sens, peut-être pas. Elle permet accessoirement de générer une courbe « acceptable » même si on n'a pas spécifié sexe masculin/féminin. Au pire vous pouvez considérer que c'est une « courbe jouet »."
+
+"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>
+</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."
"Divers","Pourquoi cet outil ?","Tout a commencé sur le <a href=""https://forum.lllfrance.org/"">forum de La Leche League</a>, où de nombreuses mamans (et parfois quelques papas) viennent poser des questions sur l'allaitement. Souvent, se pose la question de la prise de poids du bébé, qui est un bon indicateur de si le bébé reçoit assez de lait. Le poids dans l'absolu étant peu pertinent, il est utile de « tracer » la courbe de poids. Il n'existe actuellement que peu d'outils qui permettent de le faire (l'OMS en fournit un qui ne fonctionne que sous windows), en voici un."
"Divers","Mais qui es tu donc ?","Je suis une de ces nombreuses mamans qui s'est posée un jour (et un autre jour aussi) la question de la prise de poids de son enfant. Adepte de solutions libres et multi-plateforme, je n'ai rien trouvée qui me permette de tracer cette courbe, que ce soit pour mon enfant ou celui des autres. Je suis par ailleurs férue de programmation (et je l'enseigne d'ailleurs), et donc... voilà."
"Technique","Quelle est la technologie utilisée derrière ?","Il s'agit de <a href=""https://flask.palletsprojects.com/en/1.1.x/"">flask</a>, un petit framework de développement web en <a href=""https://www.python.org/"">python</a>. Il y a une petite dose de <a href=""https://developer.mozilla.org/fr/docs/Web/JavaScript"">JavaScript</a> pour assaisonner le tout, et voilà."
+
+"Technique","J'ai trouvé un affreux bug !","N'hésitez pas à me contacter avec le plus de détails possibles à ce sujet. Par exemple quelles données, quelles manipulations vous ont amené.e à ce bug. Vous pouvez aussi préciser votre navigateur. Plus vous me fournissez d'informations, plus j'ai de chances de résoudre le bug !"
+
+"Version 0.4","18/06/2020","<ul>
+<li>Mis certaines sections en masquées par défaut, on peut cliquer pour les afficher</li>
+<li>Encore des ajouts dans la FAQ</li>
+<li>Si je continue comme ça je vais arriver à une version 1.0 avant d'avoir fini !</li>
+</ul>"
+
+"Version 0.3","17/06/2020","<ul>
+<li>Le format d'export est maintenant du json (vos anciennes sauvegardes ne marchent plus, désolée !)</li>
+<li>Bouton de téléchargement des données et de la courbe, avec un nom sympa en plus (« courbe_nomenfant.png » par exemple)</li>
+<li>Le texte ""explicite"" d'export est maintenant caché par défaut (on peut quand même l'avoir en cliquant dessus)</li>
+</ul>"
+
"Version 0.2","16/06/2020","<ul>
<li>Mise en place du changelog (sans blague)</li>
<li>Courbe ""sexe neutre"" expérimentale</li>
<li>Mise à jour de la FAQ</li>
</ul>"
-"Version 0.1","14/06/2020","Mise en place du site web, en cours de développement.
+"Version 0.1","14/06/2020","<p>Mise en place du site web, en cours de développement.</p>
<ul>
<li>Tracé de la courbe</li>
<li>Export/import des données</li>
from configuration import *
from gestion_erreurs import *
import datetime
+import json
+import unidecode
############ Fonctions de conversion
def convertit_jours_vers_python(chaine,liste_err):
""" convertit une chaine de type 1a 3m 1s 10j en jours
- Renvoie un nombre de jours en entiers.
+ Renvoie un nombre de jours en float
Si un des caractères n'est ni un nombre, ni une lettre "autorisée" ni une espace,
on affiche un warning et on ignore ce caractère
"""
warning("convertit_jour_vers_python : caractère invalide : "+lettre,liste_err)
# à la fin s'il reste qqch on le garde dans les jours
if chainenombre != "":
- agejour += int(chainenombre)
+ agejours += int(chainenombre)
if agejours<0:
warning("L'âge est négatif !",liste_err)
agejours = 0
- return round(agejours)
+ return agejours
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 - round(annees*jours_dans_annee)
+ restant = nombre - annees*jours_dans_annee
mois = int(restant/jours_dans_mois)
- jours= nombre - round(mois*jours_dans_mois + annees*jours_dans_annee)
+ jours= round(nombre - mois*jours_dans_mois - annees*jours_dans_annee)
chaine = ""
if annees >0:
chaine += str(jours)+"j"
return chaine
##########################
+
+
+def simplifie_nom(chaine):
+ """ simplifie le nom chaine afin d'en faire une extension
+ pour le nom du fichier. Met tout en minuscules et vire les caractères spéciaux
+ et max 15 caractères"""
+ chaine2 = ""
+ for l in chaine:
+ if l.isalpha():
+ chaine2+=l
+ chaine2 = unidecode.unidecode(chaine2)
+ return chaine2[:15]
def convertit_poids_vers_python(chaine,liste_err):
""" convertit une chaine vers un float qui est le poids.
poids = 0
return poids
-def convertit_poids_vers_texte(poids):
- """ convertit un poids vers du texte. Rien à dire là pour l'instant """
- return str(poids)
+#def convertit_poids_vers_texte(poids):
+# """ convertit un poids vers du texte. Rien à dire là pour l'instant """
+# return str(poids)
#########################
date = datetime.date(int(liste[0]),int(liste[1]),int(liste[2]))
except:
date = ""
- warning("Impossible de lire la date "+chaine)
+ warning("Impossible de lire la date "+chaine,liste_err)
return date
def convertit_date_vers_texte(date):
def gere_configuration(data,liste_err):
- """ prend en argument le dictionnaire de requête (configuratio imparfaite), et
+ """ 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."""
configuration = {}
configuration["maxi"] = convertit_jours_vers_python(maxi,liste_err)
# dimensions du graphique
- largeur = data.get("largeur")
+ largeur = data.get("largeur","")
if largeur == "":
largeur = largeur_graphique
else:
warning("Largeur trop petite !",liste_err)
configuration["largeur"] = largeur
- hauteur = data.get("hauteur")
+ hauteur = data.get("hauteur","")
if hauteur == "":
hauteur = hauteur_graphique
else:
positionlegende = "upper left"
configuration["positionlegende"] = positionlegende
-
-
return configuration
-def configuration_vers_texte(config):
- """ exporte le texte associé à une configuration
- on dumpe simplement sauf pour maxi """
- texte = "# Section configuration\n"
- for (cle,val) in config.items():
- if cle != "maxi":
- texte+= cle + "=" + str(val) + "\n"
- else:
- texte+= cle + "=" + convertit_age_vers_texte(val)+"\n"
- texte +="\n"
- return texte
+#def configuration_vers_texte(config):
+# """ exporte le texte associé à une configuration
+# on dumpe simplement sauf pour maxi """
+# texte = "# Section configuration\n"
+# for (cle,val) in config.items():
+# if cle != "maxi":
+# texte+= cle + "=" + str(val) + "\n"
+# else:
+# texte+= cle + "=" + convertit_age_vers_texte(val)+"\n"
+# texte +="\n"
+# return texte
def gere_donneespoids(data,naissance,liste_err):
return (l_jours,l_poids)
-def donnees_poids_vers_texte(l_jours,l_poids):
- """ retourne le texte correspondant aux données de poids """
- texte = "# Section données\n"
-
- for i in range(len(l_poids)):
- texte +=convertit_age_vers_texte(l_jours[i])+","+convertit_poids_vers_texte(l_poids[i])+"\n"
+#def donnees_poids_vers_texte(l_jours,l_poids):
+# """ retourne le texte correspondant aux données de poids """
+# texte = "# Section données\n"
+#
+# for i in range(len(l_poids)):
+# texte +=convertit_age_vers_texte(l_jours[i])+","+convertit_poids_vers_texte(l_poids[i])+"\n"
+#
+# texte+="\n"
+# return texte
+
+
+
+
+
+def donnees_vers_json(l_jours,l_poids,config):
+ """ retourne le json à renvoyer"""
+ gros_dico = config.copy()
+ l_jours2 = [convertit_age_vers_texte(d) for d in l_jours]
+ gros_dico["data_j"] = l_jours2
+ gros_dico["data_p"] = l_poids
+ # gérer la date de naissance
+ if gros_dico.get("naissance","") != "":
+ gros_dico["naissance"] = convertit_date_vers_texte(gros_dico["naissance"])
+ # gérer l'age maxi
+ gros_dico["maxi"] = convertit_age_vers_texte(gros_dico["maxi"])
- texte+="\n"
- return texte
+ return json.dumps(gros_dico, indent=2,ensure_ascii=False )
+#def fichier_texte_vers_configdonnees(fichier,liste_err):
+# """ prend le texte importé et l'exporte vers configuration et données
+# sous forme de valeurs du formulaire """
+#
+# valform = {}
+# indice_formulaire = 0 # l'indice du formulaire pour les données : age_1, date_1, poids_1 etc
+# num_ligne = 0
+# lignes = fichier.readlines()
+# for ligne in lignes:
+# num_ligne +=1
+# ligne = str(ligne,"utf8")
+# ligne = ligne.rstrip("\n")
+# if ligne != "" and ligne[0] != "#" and not(ligne.isspace()): # les lignes commençant par # sont des commentaires
+# # On essaie de partitionner pour voir
+# (var,egal,val) = ligne.partition("=")
+# if egal == "=": # c'est une ligne de config
+# valform[var] = val
+# else:
+# (age,virgule,poids) = ligne.partition(",") # On partitionne avec ,
+# if virgule == ",":
+# # c'est une ligne de data
+# valform["age_"+str(indice_formulaire)] = age
+# valform["poids_"+str(indice_formulaire)] = poids
+# indice_formulaire +=1
+#
+# else:
+# warning("La ligne "+str(num_ligne)+" n'est pas reconnue et sera ignorée : <"+ligne+">",liste_err)
+#
+# #le nb max du formulaire
+# valform["nb_data"] = max(indice_formulaire +2,nombre_lignes_form)
+#
+# return valform
-def fichier_texte_vers_configdonnees(fichier,liste_err):
- """ prend le texte importé et l'exporte vers configuration et données
- sous forme de valeurs du formulaire """
+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)
+ # 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",[])
+ l_poids=valform.get("data_p",[])
+ if len(l_poids) != len(l_jours):
+ warning("Lecture du json : les données sont incohérentes (listes de taille différentes et/ou pb de lecture")
+ long = min(len(l_jours),len(l_poids))
+ else:
+ long = len(l_jours)
+ for i in range(long):
+ valform["age_"+str(i)] = l_jours[i]
+ valform["poids_"+str(i)] = l_poids[i]
+
+ valform["nb_data"] = max(long +2,nombre_lignes_form)
- valform = {}
- indice_formulaire = 0 # l'indice du formulaire pour les données : age_1, date_1, poids_1 etc
- num_ligne = 0
- lignes = fichier.readlines()
- for ligne in lignes:
- num_ligne +=1
- ligne = str(ligne,"utf8")
- ligne = ligne.rstrip("\n")
- if ligne != "" and ligne[0] != "#" and not(ligne.isspace()): # les lignes commençant par # sont des commentaires
- # On essaie de partitionner pour voir
- (var,egal,val) = ligne.partition("=")
- if egal == "=": # c'est une ligne de config
- valform[var] = val
- else:
- (age,virgule,poids) = ligne.partition(",") # On partitionne avec ,
- if virgule == ",":
- # c'est une ligne de data
- valform["age_"+str(indice_formulaire)] = age
- valform["poids_"+str(indice_formulaire)] = poids
- indice_formulaire +=1
-
- else:
- warning("La ligne "+str(num_ligne)+" n'est pas reconnue et sera ignorée : <"+ligne+">",liste_err)
+ return valform
- #le nb max du formulaire
- valform["nb_data"] = indice_formulaire +2
- return valform
+
\ No newline at end of file
}
}
+
+// Affichage de la textarea "export"
+function affiche_export()
+{
+ document.getElementById("export").style.display = "block" ;
+
+}
+
+function affiche_cache(id,elemcourant)
+{
+ // affiche et/ou cache l'élément id, tout en changeant le this
+ // en afficher/masquer
+ elem = document.getElementById(id)
+ if(elem.style.display == "block")
+ {
+ elem.style.display = "none";
+ elemcourant.innerHTML = "Afficher" ;
+ }
+ else
+ {
+ elem.style.display = "block" ;
+ elemcourant.innerHTML = "Masquer" ;
+
+ }
+
+}
requete.onreadystatechange = function()
{
if (this.readyState == 4 && this.status == 200) {
+ // On nettoie
+ nettoie_erreurs()
+
// on récupère les différents champs de la réponse
var result = this.response.result ;
var image = this.response.image ;
var liste_warnings = this.response.messages
var texte = this.response.export_txt;
+ var nomenfant = this.response.nomenfant ;
+
// on affiche l'export des données
document.getElementById('export').innerHTML = texte;
document.getElementById('sectionexport').style.display = "block";
+
+
+ var boutondl = document.getElementById("export_dl") ;
+ boutondl.setAttribute('onclick',"download_file('donnees_"+nomenfant+".json', 'application/json;charset=utf-8','"+encodeURIComponent(texte) +"')")
+
if(result == "success")
- {
+ {
// On affiche l'image
document.getElementById('courbe').src = 'data:image/png;base64,'+(image);
document.getElementById('sectioncourbe').style.display = "block";
-
+
+ //document.getElementById("courbe_dl").setAttribute('href', 'data:image/png;base64,' + image);
+ boutondl = document.getElementById("courbe_dl") ;
+ boutondl.setAttribute('onclick',"download_file('courbe_"+nomenfant+".png', 'image/png;base64','"+image +"')")
// Si y'a eu des warnings, faut les afficher
if(liste_warnings.length != 0)
}
else{ // si la génération de l'image a merdé
- // afficher la liste des warnings
+ // afficher la liste des erreurs
var elem_div = document.getElementById('courbe_erreurs') ;
elem_div.style.display = "block" ;
var ul = elem_div.children[1] ;
requete.open("POST","courbe/b64",true)
requete.send(formData)
}
+
+function nettoie_erreurs()
+{
+ // fonction qui nettoie les erreurs affichées sur la page
+ // vider les warnings
+ var elem_div = document.getElementById('courbe_warnings') ;
+ elem_div.style.display = "none" ;
+ var ul = elem_div.children[1] ;
+ ul.innerHTML = "";
+
+ // vider les erreurs
+ elem_div = document.getElementById('courbe_erreurs') ;
+ elem_div.style.display = "none" ;
+ ul = elem_div.children[1] ;
+ ul.innerHTML = "" ;
+
+}
+
+
+function download_file(filename,mimetype,data) {
+ var element = document.createElement('a');
+ element.setAttribute('href', 'data:'+mimetype+',' + data);
+ element.setAttribute('download', filename);
+
+ element.style.display = 'none';
+ document.body.appendChild(element);
+
+ element.click();
+
+ document.body.removeChild(element);
+
+}
#export {
width: 25em;
height: 20em;
+ display:none;
}
#courbe_warnings {
#courbe_erreurs {
display: none;
}
+
+#import_donnees, #pref_graphique{
+ display: none;
+}
+
+.bouton {
+ text-decoration: underline;
+}
+
+.bouton:hover {
+ cursor:pointer;
+}
<nav><a href="/">Accueil et saisie des données</a> |
<a href="/apropos">À propos</a> |
- <a href="/faq">Foire Aux Questions</a> |
+ <a href="/faq">FAQ</a> |
<a href="/changelog">Changelog</a>
</nav>
<h3>{{ ligne[0] }}</h3>
<div class="date">Le {{ ligne[1] }}</div>
-<p>{{ ligne[2]|safe }}</p>
+<div class="contenu_changelog">
+{{ ligne[2]|safe }}
</div>
{% endfor %}
{% extends "base.html" %}
{% block contenu %}
-<h2>Foire Aux Questions</h2>
+<h2>Foire Aux Questions (FAQ)</h2>
<div id="sommaire">
<ul>{% for cat in lcateg %}
<hr>
-<h2>Données du graphique</h2>
+<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">
+
+
+
<label for="fichier_donnees">Importer le fichier de données</label>
<input type="file" name="fichier_donnees">
<input type="submit" name="valider_fichier" value="Charger les données">
</form>
-
+</div>
+<hr>
<form id="donnees_enfant">
-
+<div>
+<input type="reset" value="Effacer les données du formulaire">
+</div>
<h3>Informations sur l'enfant</h3>
<ul>
-<li><label>Nom de l'enfant :</label> <input type="text" name="nom" value="{{ valform.nom }}"></li>
-<li><label>Sexe :</label> <input type="radio" name="sexe" value="F" {%if valform.sexe == "F" %}checked{% endif %}> féminin
+<li><label>Nom de l'enfant :</label> <input type="text" name="nom" value="{{ valform.nom }}"></li>
+<li><label>Sexe :</label> <input type="radio" name="sexe" value="F" {%if valform.sexe == "F" %}checked{% endif %}> féminin
| <input type="radio" name="sexe" value="M" {%if valform.sexe == "M"%} checked {% endif %}> masculin | <input type="radio" name="sexe" value="N" {%if valform.sexe == "N"%} checked {% endif %}> neutre (expérimental)</li>
-<li><label>Date de naissance : </label> <input type="date" name="naissance" value="{{ valform.naissance }}"></li>
+<li><label>Date de naissance : </label> <input type="date" name="naissance" value="{{ valform.naissance }}"></li>
</ul>
-<h3>Préférences du graphique</h3>
-<ul>
- <li><label>Type de courbe :</label> <input type="radio" name="typecourbe" value="P"
- {% if valform.typecourbe == "P" or valform.typecourbe is not defined %} checked {% endif %}> Percentiles | <input type="radio" name="typecourbe" value="Z"
- {% if valform.typecourbe == "Z" %} checked {% endif %}> Moyenne et écarts-type</li>
- <li><label>Grille : </label><input type="checkbox" name="grille" {%if valform.grille == "oui" or valform.grille is not defined %} checked {% endif %}></li>
- <li><label>Unité : </label>
- <select name="unite">
- <option value="" {% if valform.typecourbe == "" or valform is not defined %} selected {%endif %}>Par défaut</option>
- {% for unite in ['jours','semaines','mois','années'] %}
- <option value="{{ unite }}"{% if valform.unite == unite %} selected {% endif %} >{{ unite }}</option>
- {% endfor %}
- </select></li>
- <li><label>valeur maximum du graphique (facultatif, syntaxe similaire à l'âge, voir plus bas) </label><input type="text" name="maxi" value="{{ valform.maxi }}"></li>
- <li>Dimensions du graphique : <label>largeur : </label><input type="text" name="largeur" value="{{ valform.largeur }}">
- <label>hauteur : </label><input type="text" name="hauteur" value="{{ valform.hauteur }}"></li>
- <li><label>Légende : </label><input type="checkbox" name="legende" {% if valform.legende == 'oui' %} checked{% endif %}>
-Position : <select name="positionlegende">
-{%for (pos,posnom) in [('upper left','Haut gauche'),('upper right','Haut Droite'),('lower left','Bas gauche'),('lower right','Bas droite')] %}
-<option value="{{ pos }}">{{ posnom }}</option>
-{% endfor %}
-</select>
- </li>
-</ul>
<h3>Données de poids</h3>
-<p>Syntaxe pour l'âge : utiliser j, s, m, a comme des "unités" (jours, semaines, mois, années). Vous pouvez mixer les unités, par exemple "3a2m5j" pour 3 ans, 2 mois et 5 jours. Les espaces sont ignorées, et il faut saisir des nombres entiers.</p>
+<p>Syntaxe pour l'âge : utiliser j, s, m, a comme des "unités" (jours, semaines, mois, années). Vous pouvez mixer les unités, par exemple "3a2m5j" pour 3 ans, 2 mois et 5 jours. Les espaces sont ignorées, et il faut saisir des nombres entiers.</p>
<p>Saisir le poids en kilogrammes (par exemple "2.62" ou "2,62" pour 2 kilogrammes et 620 grammes).</p>
<p>Il faut saisir la date ou l'âge. Si les deux sont saisis, seul l'âge comptera.</p>
{% endfor %}
</table>
+<p class="bouton" onclick="ajoutelignes()">Cliquer ici pour ajouter des lignes</p>
+
+
+
+<h3>Préférences du graphique</h3>
+<div class="bouton" onclick="affiche_cache('pref_graphique',this)">Afficher</div>
+
+<ul id="pref_graphique">
+ <li><label>Type de courbe :</label> <input type="radio" name="typecourbe" value="P"
+ {% if valform.typecourbe == "P" or valform.typecourbe is not defined %} checked {% endif %}> Percentiles | <input type="radio" name="typecourbe" value="Z"
+ {% if valform.typecourbe == "Z" %} checked {% endif %}> Moyenne et écarts-type</li>
+ <li><label>Grille : </label><input type="checkbox" name="grille" {%if valform.grille == "oui" or valform.grille is not defined %} checked {% endif %}></li>
+ <li><label>Unité : </label>
+ <select name="unite">
+ <option value="" {% if valform.typecourbe == "" or valform is not defined %} selected {%endif %}>Par défaut</option>
+ {% for unite in ['jours','semaines','mois','années'] %}
+ <option value="{{ unite }}"{% if valform.unite == unite %} selected {% endif %} >{{ unite }}</option>
+ {% endfor %}
+ </select></li>
+ <li><label>valeur maximum du graphique (facultatif, syntaxe similaire à l'âge, voir plus bas) </label><input type="text" name="maxi" value="{{ valform.maxi }}"></li>
+ <li>Dimensions du graphique : <label>largeur : </label><input type="text" name="largeur" value="{{ valform.largeur }}">
+ <label>hauteur : </label><input type="text" name="hauteur" value="{{ valform.hauteur }}"></li>
+ <li><label>Légende : </label><input type="checkbox" name="legende" {% if valform.legende == 'oui' %} checked{% endif %}>
+Position : <select name="positionlegende">
+{%for (pos,posnom) in [('upper left','Haut gauche'),('upper right','Haut Droite'),('lower left','Bas gauche'),('lower right','Bas droite')] %}
+<option value="{{ pos }}">{{ posnom }}</option>
+{% endfor %}
+</select>
+ </li>
+</ul>
+
+
</form>
-<button onclick="ajoutelignes()">Ajouter des lignes</button>
<hr>
<button onclick="appelle_image()">Je veux la courbe !</button>
-
-<div id="sectioncourbe">
<hr>
-<h2>Courbe</h2>
-<img id="courbe">
+<div id="sectioncourbe">
+
+ <h2>Courbe</h2>
+ <img id="courbe">
+ <div>
+ <button id="courbe_dl">Télécharger la courbe</button>
+ </div>
</div>
<div id="courbe_warnings">
-<p><strong>Alerte :</strong> la courbe a eu quelques soucis à se générer. Voici la liste des erreurs.</p>
-<ul></ul>
+ <p><strong>Alerte :</strong> la courbe a eu quelques soucis à se générer. Voici la liste des erreurs.</p>
+ <ul></ul>
</div>
<div id="courbe_erreurs">
-<p><strong>Alerte :</strong> La courbe n'a pas pu être générée. Vérifiez les données saisies, ou contactez l'administratrice. Erreurs : </p>
-<ul></ul>
+ <p><strong>Alerte :</strong> La courbe n'a pas pu être générée. Vérifiez les données saisies, ou contactez l'administratrice. Erreurs : </p>
+ <ul></ul>
</div>
<div id="sectionexport">
-<h2>Export des données</h2>
-<p>Vous trouverez ci-dessous les données exportées au format texte. Il peut être utile de les copier/coller
-quelque part et de les sauvegarder, comme ça la prochaine fois vous n'aurez pas à ressaisir tout à la main.</p>
+ <h2>Export des données</h2>
+ <p>Vous pouvez télécharger les données afin de ne pas avoir à les re-saisir la prochaine fois.</p>
-<textarea readonly id="export">
-</textarea>
+ <div><button id="export_dl">Télécharger les données</button></div>
+ <p>Si vous n'arrivez pas à télécharger les données, <a href='#export' onclick="affiche_export()">cliquez ici</a> pour les voir en texte clair : il vous suffira de les copier/coller dans un fichier texte.</p>
+ <textarea readonly id="export">
+ </textarea>
</div>