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>
37 lines
1.1 KiB
Python
37 lines
1.1 KiB
Python
from flask import Blueprint, render_template
|
|
|
|
from auth_utils import current_user, login_required
|
|
from quiz import get_all
|
|
from db import get_user_results, get_best_score
|
|
|
|
protected_bp = Blueprint('protected', __name__)
|
|
|
|
|
|
@protected_bp.route('/profil/')
|
|
@login_required
|
|
def profil():
|
|
user = current_user()
|
|
results = get_user_results(user['sub'])
|
|
quizzes = get_all()
|
|
|
|
# Calcule le meilleur score par quiz
|
|
best_scores = {}
|
|
for q in quizzes:
|
|
row = get_best_score(user['sub'], q['id'])
|
|
if row:
|
|
best_scores[q['id']] = {'score': row['best'], 'total': row['total']}
|
|
|
|
# Statistiques globales
|
|
completed_ids = {r['quiz_id'] for r in results}
|
|
stats = {
|
|
'quizzes_done': len(completed_ids),
|
|
'quizzes_total': len(quizzes),
|
|
'attempts': len(results),
|
|
}
|
|
|
|
return render_template('profil/index.html',
|
|
user=user,
|
|
results=results,
|
|
quizzes={q['id']: q for q in quizzes},
|
|
best_scores=best_scores,
|
|
stats=stats)
|