--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Jul 26 17:31:48 2021
+
+@author: sekhmet
+"""
+
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import flask
+from data_textes import liste_textes
+import gere_grille as g
+import gere_erreurs as e
+from config import DEFAUT, CONFIG
+
+
+def initialise_mode_beta():
+ global beta
+ hote = flask.request.host
+ if hote[:4] == "beta":
+ print("** Mode bêta !**")
+ return True
+ else:
+ return False
+
+
+app = flask.Flask(__name__)
+
+
+@app.route('/', methods=["GET", "POST"])
+def index():
+ liste_err = e.initialise_erreurs()
+ idg = flask.request.args.get("grille", "") # Id de grille passée en param (ou pas)
+ if idg != "": # Si on a mis un url de grille
+ conf = g.decode_grille(idg, liste_err)
+ if conf == {}: # Erreur à la génération
+ bingo = g.genere_grille(DEFAUT, liste_textes)
+ conf = DEFAUT.copy()
+ e.erreur("L'url de la grille n'est pas valide...", liste_err)
+ else:
+ bingo=conf["grille"]
+ else:
+ # On récupère les données post (et ça sera défaut si y'a rien)
+ if flask.request.method == "POST":
+ conf = g.gere_donnees_custom(flask.request.form, liste_err)
+ else:
+ conf= DEFAUT.copy()
+ bingo = g.genere_grille(conf, liste_textes) # aléatoire
+
+ chainecode = g.encode_grille(conf, bingo, liste_err)
+
+ return flask.render_template("index.html", bingo=bingo, chainecode=chainecode, conf=conf, e=liste_err[0]+liste_err[1]+liste_err[2])
+
+
+@app.route('/custom')
+def custom():
+ liste_err = e.initialise_erreurs()
+ return flask.render_template("custom.html", DEFAUT=DEFAUT,CONFIG=CONFIG, e=liste_err[0]+liste_err[1]+liste_err[2])
+
+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
--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Configuration générale
+"""
+
+# Configuration par défaut
+DEFAUT = {}
+
+DEFAUT["titre"] = "Le bingo de l'allaitement"
+
+DEFAUT["nblignes"] = 4
+DEFAUT["nbcolonnes"] = 3
+DEFAUT["nbcasesvides"] = 0
+
+
+# Configuration
+
+CONFIG = {}
+CONFIG["lmax_titre"] = 50
+CONFIG["maxlignes"] = 20
+CONFIG["minlignes"] = 1
+CONFIG["maxcolonnes"] = 20
+CONFIG["mincolonnes"] = 1
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Jul 26 22:50:08 2021
+
+@author: sekhmet
+"""
+
+liste_textes = [
+ "Tu vas pas l'allaiter jusqu'à [âge quelconque] !",
+ "Tu es sûre que tu as assez de lait ?",
+ "Quand est-ce que tu vas lui donner du lait normal ?",
+ "Il/elle fait ses nuits ?",
+ "Il/elle dort encore avec vous ?",
+ "Laisse le/la pleurer il/elle finira bien par dormir (et/ou ça lui fera les poumons).",
+ "Ton lait n'est pas/plus assez nourrissant.",
+ "Il/elle doit apprendre à se détacher de maman.",
+ "Tu as pris combien de kilos ?",
+ "Donne lui un biberon, il/elle fera ses nuits.",
+ "Tu arrêteras quand il/elle aura des dents, puisqu'il/elle va te mordre",
+
+ ]
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Tue Jul 27 11:16:54 2021
+
+@author: sekhmet
+"""
+
+## Gestion des erreurs en flask
+import sys
+
+niveau_debug = ("debug" in sys.argv)
+
+def initialise_erreurs():
+ """ retourne trois listes vides, erreurs fatales (0), warnings(1), debug(2):"""
+ return ([],[],[])
+
+
+def erreur(message,listeerreurs):
+ """ en cas d'erreur où on ne peut pas continuer
+ message est une chaîne"""
+ print("** Erreur fatale : "+message)
+ listeerreurs[0].append("** Erreur : "+message)
+
+def warning(message,listeerreurs):
+ """ En cas d'avertissement mais on peut quand même continuer """
+ print("** Warning : "+message)
+ message = "Alerte : "+message
+ if message not in listeerreurs[1]:
+ listeerreurs[1].append(message)
+
+def debug(message,listeerreurs):
+ global niveau_debug
+ if niveau_debug:
+ print("##Debug : "+message)
+ listeerreurs[2].append("# Debug : "+message)
+
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Jul 26 22:58:28 2021
+
+@author: sekhmet
+"""
+import random
+import zlib, base64, json
+import gere_erreurs as e
+from config import DEFAUT, CONFIG
+
+def genere_grille(config, textes):
+ """ génère une grille avec des éléments aléatoires du tableau textes. Si y'a pas assez, on complète aléatoirement avec des 0.
+ config contient tout ce qu'on veut pour la grille"""
+
+ nblignes, nbcol = config["nblignes"], config["nbcolonnes"]
+
+ # Créer la grille
+ grille = [ [""]*nbcol for i in range(nblignes)]
+
+ nbdata = nbcol*nblignes - config["nbcasesvides"] # Nombre d'entrées
+
+ # Créer le sous-tableau
+ if len(textes)>= nbdata: # Si y'a assez de textes
+ soustab = random.sample(textes, nbdata) + ["0"]*config["nbcasesvides"]
+ else:
+ # On met tous les textes + complète avec des "0"
+ soustab = textes + ["0"]*(nbcol*nblignes - len(textes))
+ random.shuffle(soustab)
+ #print(soustab)
+
+ # On met tout ça dans la grille
+ for i in range(nblignes):
+ for j in range(nbcol):
+ grille[i][j] = soustab[i*nbcol+j]
+
+ return grille
+
+def encode_grille(config, grille, liste_err):
+ """ grille est un tableau double qu'on veut sauvegarder.
+ config est la configuration (on va garder le titre avec)
+ Renvoie une chaîne de caractères qui encode cette grille
+ de manière condensée. Renvoie une chaîne cheloue"""
+ # Chaîne de caractère
+ data = {"titre": config["titre"], "grille": grille}
+ chaine = json.dumps(data)
+ code = zlib.compress(chaine.encode())
+ chaineencodee = str(base64.urlsafe_b64encode(code))
+ e.debug(chaineencodee, liste_err)
+ return chaineencodee[2:-1] # Enlever le b' devant et le ' à la fin
+
+def decode_grille(chaineencodee, liste_err):
+ """ l'inverse de la fonction précédente : renvoie le dictionnaire
+ avec les params et la grille reconstituée
+ """
+ e.debug(chaineencodee, liste_err)
+ b2 = base64.urlsafe_b64decode(chaineencodee)
+ try:
+ decodee = zlib.decompress(b2)
+ except:
+ e.erreur("Impossible de décoder la chaîne : "+chaineencodee, liste_err)
+ return {}
+ # Remettre ça en python
+ data = json.loads(decodee)
+ return data
+
+###########################
+
+def gere_donnees_custom(data, liste_err):
+ """ data est le dictionnaire de requête. Gère les données reçues
+ et en fait un dictionnaire propre, en mettant les défauts là où
+ c'est pas bon"""
+
+ conf =DEFAUT.copy()
+
+ ## Le titre
+ t = data.get("titre", "")
+ if len(t) > 0:
+ conf["titre"] = t[0:CONFIG["lmax_titre"]]
+
+ # Les dimensions
+ conf["nblignes"] = minimaxi(data.get("nblignes",""), CONFIG["minlignes"], CONFIG["maxlignes"], DEFAUT["nblignes"], liste_err)
+ conf["nbcolonnes"] = minimaxi(data.get("nbcolonnes",""), CONFIG["mincolonnes"], CONFIG["maxcolonnes"], DEFAUT["nbcolonnes"], liste_err)
+
+ # Les cases vides
+ conf["nbcasesvides"] = minimaxi(data.get("nbcasesvides",""), 0, CONFIG["maxlignes"]*CONFIG["maxcolonnes"], DEFAUT["nbcasesvides"], liste_err)
+
+ return conf
+###
+def minimaxi(donnee, mini, maxi, defaut, liste_err):
+ """ donnee est une chaine qui est censée être un nombre entier.
+ On vérifie que la donnée est valide, qu'elle est bien dans l'inter
+ valle mini, maxi et si ça foire on met le défaut"""
+ try:
+ x = int(donnee)
+ except:
+ e.warning("La donnée "+donnee+ "est invalide !", liste_err)
+ x = defaut
+ x = max(mini, min(x, maxi))
+ return x
\ No newline at end of file
--- /dev/null
+/* Calcul des scores */
+var points_par_case = 1 ;
+var points_par_ligne = 10 ;
+
+
+
+// Couleurs des cases
+var couleur_base = "rgb(238, 238, 238)" ;
+var couleur_valide = 'rgb(255, 136, 136)' ;
+
+
+// Valide la case et la colore en rouge
+function validecase(elem) {
+ elem.style.backgroundColor = couleur_valide ;
+
+ detectelignes() ;
+}
+
+// efface la grille
+function effacegrille() {
+ var table=document.getElementById("grille") ;
+ var listetd = table.getElementsByTagName("td") ;
+ for (var i=0; i< listetd.length; i++){
+ listetd[i].style.backgroundColor = couleur_base ;
+ }
+ metscore(0) ;
+}
+
+// mettre à jour le score si on veut
+function metscore(score) {
+ document.getElementById("score").innerHTML = score ;
+}
+
+// Compte les lignes, colonnes et diagonales de la grille et met à jour le score
+function detectelignes(elem) {
+ var table=document.getElementById("grille") ;
+ var listelignes = table.getElementsByTagName("tr") ;
+ var nblignes = listelignes.length ;
+ var nbcolonnes = listelignes[0].children.length ;
+ var i, j ;
+
+ // lignes
+ var nb_lignes_completes = 0 ;
+ var remplie ;
+ for(i=0; i<nblignes; i++) {
+ remplie = true ;
+ for(j=0; j<nbcolonnes; j++) {
+ //alert(j) ;
+ //alert(listelignes[i].children[j].style.backgroundColor) ;
+ if(listelignes[i].children[j].style.backgroundColor != couleur_valide) {
+ // c'est pas une ligne, dommage
+ remplie = false ;
+ //alert("la ligne "+i+"n'est pas remplie, vu à la colonne "+j) ;
+ break
+ }
+ }
+ if(remplie) {
+ nb_lignes_completes+=1 ;
+ }
+ }
+
+ // Colonnes
+ var nb_colonnes_completes = 0 ;
+ for(j=0; j<nbcolonnes; j++) {
+ remplie = true ;
+ for (i=0; i<nblignes; i++) {
+ if(listelignes[i].children[j].style.backgroundColor != couleur_valide) {
+ // c'est pas une colonne, dommage
+ remplie = false ;
+ break
+ }
+ }
+ if (remplie) {
+ nb_colonnes_completes+=1 ;
+ }
+ }
+
+ // Diagonales.
+ var nb_diagonales_1 = 0 ;
+ var nb_diagonales_2 = 0 ;
+ if(nblignes <= nbcolonnes) {
+ // Les diagonales se comptent horizontalement : "\\\" et "///"
+ for(i=0; i<nbcolonnes - nblignes +1; i++) {
+ // diagonales "\"
+ remplie=true ;
+ for(j=0; j<nblignes; j++) {
+ if(listelignes[j].children[i+j].style.backgroundColor != couleur_valide) {
+ remplie = false
+ break
+ }
+ }
+ if (remplie) {
+ nb_diagonales_1+=1
+ }
+
+ // diagonales "/"
+ remplie=true ;
+ for(j=0; j<nblignes; j++) {
+ if(listelignes[j].children[nbcolonnes-i-j-1].style.backgroundColor != couleur_valide) {
+ remplie = false
+ break
+ }
+ }
+ if (remplie) {
+ nb_diagonales_2+=1
+ }
+ }
+
+ } else { // les diagonales vont se compter "verticalement"
+ for(i=0; i<nblignes - nbcolonnes +1; i++) {
+ // diagonales "\"
+ remplie=true ;
+ for(j=0; j<nbcolonnes; j++) {
+ if(listelignes[i+j].children[j].style.backgroundColor != couleur_valide) {
+ remplie = false
+ break
+ }
+ }
+ if (remplie) {
+ nb_diagonales_1+=1
+ }
+
+ // diagonales "/"
+ remplie=true ;
+ for(j=0; j<nbcolonnes; j++) {
+ if(listelignes[nblignes -i-j-1].children[j].style.backgroundColor != couleur_valide) {
+ remplie = false
+ break
+ }
+ }
+ if (remplie) {
+ nb_diagonales_2+=1
+ }
+ }
+
+
+ }
+ // Compter les cases utilisées
+ var nbcases = 0 ;
+ for(i=0; i<nblignes; i++) {
+ for(j=0; j<nbcolonnes; j++) {
+ if (listelignes[i].children[j].style.backgroundColor == couleur_valide) {
+ nbcases+=1
+ }
+ }
+
+ }
+
+
+ // Mettre le score à jour
+ score = (nb_lignes_completes + nb_colonnes_completes + nb_diagonales_1 + nb_diagonales_2) *points_par_ligne + nbcases ;
+ metscore(score) ;
+}
--- /dev/null
+/* Styles divers */
+.cliquable {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+.petit {
+ font-size: 0.8em;
+}
+
+
+/* Grille du bingo */
+#grille {
+ background-color: #F8F8F8 ;
+ padding: 1px ;
+ margin: 30px ;
+ border: 1px solid black ;
+}
+
+#grille td {
+ background-color:#EEEEEE;
+ border: 1px solid black;
+ margin: 0px;
+ padding: 3px ;
+ text-align: center ;
+}
+
+#grille td.vide {
+ background-color:#999999 ;
+}
+
+/* Formulaire page custom */
+.texte {
+ width:20em
+}
+
+.data {
+ width:3em
--- /dev/null
+<!DOCTYPE html>
+
+<html lang="fr">
+ <head>
+ <meta charset="UTF-8" >
+ <title>Le bingo de l'allaitement</title>
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" type="text/css">
+ <!--<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">-->
+
+ <script src="static/outilspage.js"></script>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ </head>
+
+ <body>
+
+ {% block contenu %}{% endblock %}
+
+ <hr>
+<div id="basdepage">
+ <nav>
+ <a href="/">Grille aléatoire</a> |
+ <a href="/custom">Créer une grille personnalisée</a> |
+<!-- <a href="/apropos">À propos</a> |-->
+
+ </nav>
+</div>
+
+
+ </body>
+</html>
--- /dev/null
+{% extends "base.html" %}
+{% block contenu %}
+<h1>Créer une grille personnalisée</h1>
+
+{% if e | length >0 %}
+<div id="erreurs">
+ <h2>Log d'erreurs</h2>
+ <ul>
+ {% for err in e %}
+ <li>{{ err }}</li>
+ {% endfor %}
+ </ul>
+</div>
+{% endif %}
+
+<div id="contenu">
+
+<form action="/" method="POST">
+
+<ul>
+ <li>Titre de la page : <input class="texte" type="text" name="titre" value="{{ DEFAUT.titre }}"></li>
+ <li>Nombre de lignes : <input class="data" type="number" min="{{ CONFIG.minlignes }}" max="{{ CONFIG.maxlignes}}" name="nblignes" value="{{ DEFAUT.nblignes }}"></li>
+ <li>Nombre de colonnes : <input class="data" type="number" min="{{ CONFIG.mincolonnes }}" max="{{ CONFIG.maxcolonnes }}" name="nbcolonnes" value="{{ DEFAUT.nbcolonnes }}"></li>
+ <li>Nombre de cases vides : <input class="data" type="number" min="0" name="nbcasesvides" value="{{ DEFAUT.nbcasesvides }}"> <span class="petit">Remarque : s'il n'y a pas assez de données, la grille sera automatiquement complétée par des cass vides.</span></li>
+
+
+</ul>
+<input type="submit" name="zou" value="Je veux une grille !">
+
+</form>
+
+</div>
+
+
+{% endblock %}
--- /dev/null
+{% extends "base.html" %}
+{% block contenu %}
+<h2>Foire Aux Questions (FAQ)</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 %}
--- /dev/null
+{% extends "base.html" %}
+{% block contenu %}
+<h1>{{ conf.titre }}</h1>
+
+{% if e | length >0 %}
+<div id="erreurs">
+ <h2>Log d'erreurs</h2>
+ <ul>
+ {% for err in e %}
+ <li>{{ err }}</li>
+ {% endfor %}
+ </ul>
+</div>
+{% endif %}
+
+<div id="contenu">
+
+<p>Cochez la case dès que vous entendez une réplique idiote !</p>
+<table id="grille">
+{% for ligne in bingo %}
+<tr>{% for elt in ligne %}
+ {% if elt == "0" %}
+ <td class="vide"></td>
+ {% else %}
+ <td onclick='validecase(this)'>{{ elt }}</td>
+ {% endif %}
+ {% endfor %}
+</tr>
+{% endfor %}
+</table>
+
+<p>Score : <span id="score">0</span></p>
+
+<p onclick="effacegrille()" class="cliquable">Effacer la grille</p>
+
+<hr>
+<a href="/?grille={{ chainecode }}">Lien permanent vers cette grille</a>
+
+</div>
+
+
+{% endblock %}