Ajout de la FAQ, correction de bugs. Meilleure requête de courbe.
authorDenise sur Lya <sekhmet@lya>
Mon, 15 Jun 2020 22:32:10 +0000 (00:32 +0200)
committerDenise sur Lya <sekhmet@lya>
Mon, 15 Jun 2020 22:32:10 +0000 (00:32 +0200)
FAQ_data.txt [new file with mode: 0644]
app.py
faq.py [new file with mode: 0644]
gestion_donnees.py
static/requetes.js
static/style.css
templates/base.html
templates/faq.html [new file with mode: 0644]
templates/index.html

diff --git a/FAQ_data.txt b/FAQ_data.txt
new file mode 100644 (file)
index 0000000..cce0a2d
--- /dev/null
@@ -0,0 +1,18 @@
+"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 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","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."
+
+"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","Mon enfant doit-il être dans la moyenne ?","Il y a des petits bébés, des gros bébés, des bébés moyens. En général, un bébé en bonne santé ""suit"" un couloir de courbe, que ce soit la courbe des « bébés crevettes » ou des « bébés dodus ». Un changement de couloir (poids en baisse le plus souvent) peut éventuellement être signe d'un problème. Ou pas. Dans tous les cas, référez vous à des professionnels de santé et/ou de la petite enfance."
+
+"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à."
diff --git a/app.py b/app.py
index 7348472c247a130aec058d6a77d6c0c88f6518f4..4f330d9204c715f64c2fea39ce1b3705112be69e 100644 (file)
--- a/app.py
+++ b/app.py
@@ -8,6 +8,8 @@ from gestion_erreurs import *
 from gestion_donnees import *
 from gestion_unites import *
 from trace_courbe import *
+from faq import *
+
 import matplotlib.pyplot as plt
 
 import base64
@@ -40,17 +42,27 @@ def courbe_image(ext):
     liste_err = initialise_erreurs()
     data = flask.request.form
     
-    config = gere_configuration(data,liste_err)
-    
+    # Régler la configuration et les données    
+    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)  
+
+    # créer la figure
     fig = cree_figure(config,l_jours,l_poids,liste_err)
-    
     output = io.BytesIO()
     FigureCanvas(fig).print_png(output)
-    
     plt.close(fig)
+    
     if ext == "b64":
-        return flask.Response(base64.b64encode(output.getvalue()), mimetype='text/plain')
+        reponse = flask.jsonify({ "result":"success", 
+            "messages": liste_err[1], 
+            "image": base64.b64encode(output.getvalue()).decode("ascii"),
+            "export_txt": texte})
+        return reponse
+        #return flask.Response(base64.b64encode(output.getvalue()), mimetype='text/plain')
     elif ext == "png":
         return flask.Response(output.getvalue(), mimetype='image/png')
     else:
@@ -72,5 +84,14 @@ def export_donnees():
     
     return flask.Response(texte,mimetype="text/plain")
 
+@app.route("/faq")
+def faq():
+    
+    table_faq = lire_fichier_csv_simple(fichier_FAQ)
+    l_categ,table_qr = extraire_tables_par_cat(table_faq)
+
+    return flask.render_template("faq.html",lcateg=l_categ,tableqr=table_qr,err=[])
+
+
 if __name__ == "__main__":
     app.run(host='0.0.0.0',debug=True)
\ No newline at end of file
diff --git a/faq.py b/faq.py
new file mode 100644 (file)
index 0000000..4ee8bfa
--- /dev/null
+++ b/faq.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+### Les fonctions et données pour la FAQ
+
+import csv
+fichier_FAQ = "FAQ_data.txt"
+
+# Les questions seront dans une database sous la forme (categorie,question,reponse)
+
+def lire_fichier_csv_simple(fichier):
+    """ lire un fichier csv et le renvoyer en table"""
+    table=[]
+    with open(fichier, "r") as fichier_csv:
+        reader_notes = csv.reader(fichier_csv,delimiter=",",dialect="unix")
+        for ligne in reader_notes:
+            if ligne != []:
+                table.append(ligne)
+    return table
+
+
+
+
+
+# On trie par catégorie
+#table_faq.sort(key=(lambda x: x[0]))
+
+def extraire_categories(table):
+    """ extrait les catégories (1ere colonne du tableau de tableau)
+    renvoie sous forme de liste"""
+    liste_cat = []
+    for ligne in table:
+        if not (ligne[0] in liste_cat):
+            liste_cat.append(ligne[0])
+    return liste_cat
+
+def extraire_tables_par_cat(table):
+    """ construit deux tables : une de catégories, une de tables de (q,r)
+     categ[i] va correspondre à tableqr[i] en terme de catégorie"""
+    categ = extraire_categories(table)
+    tableqr = [ [] for i in range(len(categ)) ]
+    
+    for ligne in table:
+        try:
+            cat = ligne[0]
+            (q,r) = [ligne[1],ligne[2]]
+            ind = categ.index(cat)
+            tableqr[ind].append((q,r))
+        except:
+            print("Impossible de lire la ligne : "+str(ligne))
+            
+    return (categ,tableqr)
index 82491ca606b168a12606f4bc1423784e9c6f7e2f..9584386714750d2a9c77ad321473a7287414df72 100644 (file)
@@ -73,12 +73,12 @@ def convertit_poids_vers_python(chaine,liste_err):
     chaine2 = chaine2.replace(" ","")
 
     try:
-        poids = float(chaine)
+        poids = float(chaine2)
     except:
         warning("Poids impossible à lire : "+chaine,liste_err)
         poids = 0
-    if not( 0<poids<poids_maxi):
-        warning("Poids incohérent : "+poids)
+    if not( 0<=poids<poids_maxi):
+        warning("Poids incohérent : "+str(poids),liste_err)
         poids = 0
     return poids
 
@@ -321,4 +321,4 @@ def fichier_texte_vers_configdonnees(fichier,liste_err):
     #le nb max du formulaire
     valform["nb_data"] = indice_formulaire +2
     
-    return valform
\ No newline at end of file
+    return valform
index 41fd8500bf665e08a47c3fc4c8e6a1895f6505bf..9da579c55b30b594f92dd0ab990510ff15506a8c 100644 (file)
@@ -5,31 +5,39 @@ function appelle_image()
        var formData = new FormData( document.getElementById("donnees_enfant") );
        
        var requete = new XMLHttpRequest(); 
+       requete.responseType = "json";
        requete.onreadystatechange = function()
        {
                if (this.readyState == 4 && this.status == 200) {
-                       document.getElementById('courbe').src = 'data:image/png;base64,'+(this.response);
+                       // on récupère les différents champs de la réponse
+                       var image = this.response.image ;
+                       var liste_warnings = this.response.messages
+                       var texte = this.response.export_txt;
+                       // On affiche l'image
+                       document.getElementById('courbe').src = 'data:image/png;base64,'+(image);
                        document.getElementById('sectioncourbe').style.display = "block";
+                       
+                       // on affiche l'export des données
+                       document.getElementById('export').innerHTML = texte;
+                       document.getElementById('sectionexport').style.display = "block";
+
+                       // Si y'a eu des warnings, faut les afficher
+                       if(liste_warnings.length != 0)
+                       {
+                               // afficher la liste des warnings
+                               var elem_div = document.getElementById('courbe_warnings') ;
+                               elem_div.style.display = "block" ;
+                               var ul = elem_div.children[1] ;
+                               for(i=0; i<liste_warnings.length; i++) {
+                                       var li = document.createElement("li");
+                                       li.appendChild(document.createTextNode(liste_warnings[i]));
+                                       ul.appendChild(li);
+                               }
+                               
+                               
+                       }
                }
        }
        requete.open("POST","courbe/b64",true)
        requete.send(formData)
 }
-
-function exporte_donnees()
-{
-       var formData = new FormData( document.getElementById("donnees_enfant") );
-       
-       var requete = new XMLHttpRequest(); 
-       requete.onreadystatechange = function()
-       {
-               if (this.readyState == 4 && this.status == 200) {
-                       document.getElementById('export').innerHTML = this.responseText;
-                       document.getElementById('sectionexport').style.display = "block";
-               }
-       }
-       requete.open("POST","export_donnees",true)
-       requete.send(formData)  
-       
-       
-}
index 58ddc3b112160162edce5a08eb4e0c9a953114d3..3216d34c262fcc3fded70e05018d3cc3d6d4e8f3 100644 (file)
@@ -14,3 +14,7 @@ body {
        width: 25em;
        height: 20em;
 }
+
+#courbe_warnings {
+       display: none;
+}
index 571292fc11bdc04177cb7f006eae183bbb3969e0..20e1dd5752c285841537389060d26604a7abd673 100644 (file)
@@ -33,6 +33,7 @@
                
                <nav><a href="/">Accueil et saisie des données</a> |
                <a href="/apropos">À propos</a> |
+               <a href="faq">Foire Aux Questions</a>
                </nav> 
                
        </body>
diff --git a/templates/faq.html b/templates/faq.html
new file mode 100644 (file)
index 0000000..301f5d7
--- /dev/null
@@ -0,0 +1,30 @@
+{% extends "base.html" %}
+{% block contenu %}
+<h2>Foire Aux Questions</h2>
+
+<div id="sommaire">
+<ul>{% for cat in lcateg %}
+<li><a href="#{{ cat }}">{{ cat }}</a></li>
+{% endfor %}
+</ul>
+</div>
+
+<div id="questionsreponses">
+{% for i in range(lcateg|length) %}
+{% set qr = tableqr[i] %}
+
+<h3 id="{{ lcateg[i] }}">{{ lcateg[i] }}</h3>
+<ul>
+       {% for (q,r) in qr %}
+       <li><p><strong>Q : </strong>{{ q|safe }}</p>
+       <p><strong>R : </strong>{{ r|safe }}</p>
+       </li>
+       {% endfor %}
+</ul>
+
+
+{% endfor %}
+</div>
+
+
+{% endblock %}
index f9de4a53a56e952d5ab0806eb3d3b56accaa7cf5..3d81e3e0e314a3111092b7a038cb9f140f01dd26 100644 (file)
@@ -55,7 +55,7 @@ Position : <select name="positionlegende">
 
 <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>Saisir le poids en kilogrammes (le point et la virgule comptent comme le séparateur virgule).</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>
 
 <p>Si le tableau est laissé vide, cela génère une courbe "vide" de référence.</p>
@@ -77,7 +77,7 @@ Position : <select name="positionlegende">
 
 <hr>
 
-<button onclick="appelle_image(); exporte_donnees()">Je veux la courbe !</button>
+<button onclick="appelle_image()">Je veux la courbe !</button>
 
 
 <div id="sectioncourbe">
@@ -85,6 +85,10 @@ Position : <select name="positionlegende">
 <h2>Courbe</h2>
 <img id="courbe">
 </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>
+</div>
 
 <div id="sectionexport">
 <h2>Export des données</h2>