Feat: add class page

This commit is contained in:
2025-08-09 11:32:36 +02:00
parent 17995f913e
commit 0e87a457af
18 changed files with 6974 additions and 14 deletions

View File

@@ -1,4 +1,4 @@
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, current_app
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, current_app, abort
from models import db, ClassGroup, Student, Assessment
from forms import ClassGroupForm
from utils import handle_db_errors, ValidationError
@@ -148,16 +148,105 @@ def delete(id):
return redirect(url_for('classes'))
@bp.route('/<int:id>/details')
@bp.route('/<int:id>', methods=['GET'])
@handle_db_errors
def details(id):
"""Page de détail d'une classe avec ses étudiants et évaluations."""
"""Redirection transparente vers le dashboard de classe."""
return redirect(url_for('classes.dashboard', id=id))
@bp.route('/<int:id>/dashboard')
@handle_db_errors
def dashboard(id):
"""Page de présentation de classe avec statistiques."""
# Récupération paramètre trimestre
trimester = request.args.get('trimestre', type=int)
if trimester and trimester not in [1, 2, 3]:
trimester = None
# Repository optimisé
class_repo = ClassRepository()
class_group = class_repo.find_with_statistics(id, trimester)
if not class_group:
abort(404)
current_app.logger.debug(f'Dashboard classe {id} affiché pour trimestre {trimester}')
return render_template('class_dashboard.html',
class_group=class_group,
selected_trimester=trimester)
@bp.route('/<int:id>/stats')
@handle_db_errors
def get_stats_api(id):
"""API JSON pour statistiques dynamiques (AJAX)."""
# Récupération paramètre trimestre
trimester = request.args.get('trimestre', type=int)
if trimester and trimester not in [1, 2, 3]:
trimester = None
# Repository optimisé
class_repo = ClassRepository()
class_group = class_repo.find_with_statistics(id, trimester)
if not class_group:
abort(404)
try:
# Construction de la réponse JSON avec les nouvelles méthodes du modèle
quantity_stats = class_group.get_trimester_statistics(trimester)
domain_analysis = class_group.get_domain_analysis(trimester)
competence_analysis = class_group.get_competence_analysis(trimester)
class_results = class_group.get_class_results(trimester)
# Compter le nombre d'évaluations selon le trimestre
if hasattr(class_group, '_filtered_assessments'):
assessments_count = len(class_group._filtered_assessments)
current_app.logger.debug(f'Assessments count from _filtered_assessments: {assessments_count}')
else:
# Fallback si _filtered_assessments n'existe pas
if trimester:
assessments_count = len([a for a in class_group.assessments if a.trimester == trimester])
else:
assessments_count = len(class_group.assessments)
current_app.logger.debug(f'Assessments count from fallback: {assessments_count}')
current_app.logger.debug(f'Final assessments_count value: {assessments_count}, type: {type(assessments_count)}')
stats = {
"quantity": {
"total": quantity_stats["total"],
"completed": quantity_stats["completed"],
"in_progress": quantity_stats["in_progress"],
"not_started": quantity_stats["not_started"]
},
"domains": domain_analysis["domains"], # Extraire directement le tableau
"competences": competence_analysis["competences"], # Extraire directement le tableau
"results": {
"average": class_results["overall_statistics"]["mean"],
"min": class_results["overall_statistics"]["min"],
"max": class_results["overall_statistics"]["max"],
"median": class_results["overall_statistics"]["median"],
"std_dev": class_results["overall_statistics"]["std_dev"],
"assessments_count": assessments_count
}
}
current_app.logger.debug(f'Statistiques API générées pour classe {id}, trimestre {trimester}')
return jsonify(stats)
except Exception as e:
current_app.logger.error(f'Erreur génération statistiques API classe {id}: {e}')
return jsonify({"error": "Erreur lors de la génération des statistiques"}), 500
@bp.route('/<int:id>/details')
@handle_db_errors
def details_legacy(id):
"""Page de détail d'une classe avec ses étudiants et évaluations (legacy)."""
class_repo = ClassRepository()
class_group = class_repo.find_with_full_details(id)
if not class_group:
# Gestion manuelle du 404 car find_with_full_details retourne None
from flask import abort
abort(404)
# Trier les étudiants par nom (optimisé en Python car déjà chargés)