App Flask complète pour https://dynamic.alpinux.org : - 10 quiz Linux, 5 niveaux (Découverte → Expert), 50+ questions - Public : Découverte, Débutant, Intermédiaire (6 quiz) - Membres AlpID : Avancé, Expert (4 quiz — Git, Admin, Sécurité, Bash) - Navigation question par question avec avance automatique après choix - Score calculé côté serveur, enregistré en SQLite si connecté - Page profil : meilleurs scores par quiz + historique des tentatives Authentification : - OIDC via authlib + AlpID (Keycloak), SSO partagé avec Gitea/Nextcloud - Décorateur @login_required, redirection post-login sur l'URL d'origine - /auth/login, /auth/callback, /auth/logout Structure : - dynamic/app.py, db.py, quiz.py, auth_utils.py - dynamic/routes/ (public.py, auth.py, protected.py) - dynamic/templates/ (base, index, quiz/*, profil/) - dynamic/static/ (style.css thème Alpinux, quiz.js vanilla) - dynamic/data/quizzes.json (source de vérité des questions) - dynamic/.env.example Infrastructure : - scripts/dynamic.alpinux.org.vhost.conf (Apache reverse proxy) - scripts/dynamic.alpinux.org.service (systemd Gunicorn) - docs/technique/deploiement-dynamic.md (procédure complète) - mkdocs.yml : page de déploiement ajoutée à la nav Technique - .gitignore : exclut venv/ et .env Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
31 lines
747 B
Python
31 lines
747 B
Python
import json
|
|
from pathlib import Path
|
|
|
|
_QUIZZES = None
|
|
|
|
LEVEL_ORDER = {1: "Découverte", 2: "Débutant", 3: "Intermédiaire", 4: "Avancé", 5: "Expert"}
|
|
LEVEL_COLOR = {1: "green", 2: "teal", 3: "orange", 4: "red", 5: "purple"}
|
|
|
|
|
|
def load_quizzes():
|
|
global _QUIZZES
|
|
if _QUIZZES is None:
|
|
path = Path(__file__).parent / "data" / "quizzes.json"
|
|
_QUIZZES = json.loads(path.read_text(encoding="utf-8"))
|
|
return _QUIZZES
|
|
|
|
|
|
def get_all():
|
|
return load_quizzes()
|
|
|
|
|
|
def get_by_id(quiz_id):
|
|
return next((q for q in load_quizzes() if q["id"] == quiz_id), None)
|
|
|
|
|
|
def get_public():
|
|
return [q for q in load_quizzes() if not q["members_only"]]
|
|
|
|
|
|
def get_members():
|
|
return [q for q in load_quizzes() if q["members_only"]]
|