13 KiB
📊 Page des Résultats d'Évaluation
Vue d'Ensemble
La page des résultats d'évaluation (/assessments/<id>/results) est une interface d'analyse complète qui présente les performances des élèves sous forme de statistiques, visualisations et heatmaps interactives. Cette page constitue l'aboutissement du processus d'évaluation et offre aux enseignants une vision claire et détaillée des résultats.
🎯 Fonctionnalités Principales
1. Statistiques Descriptives
- Nombre d'élèves évalués
- Moyenne générale de la classe
- Médiane des scores
- Minimum et Maximum obtenus
- Écart-type de la distribution
- Total des points possible pour l'évaluation
2. Histogramme de Distribution
- Graphique en barres des notes avec bins de 1 point
- Tooltips personnalisés affichant la liste des élèves pour chaque barre
- Visualisation Chart.js responsive et interactive
- Animation fluide et design cohérent
3. Heatmaps d'Analyse
- Heatmap par compétences : Performance des élèves par compétence évaluée
- Heatmap par domaines : Performance des élèves par domaine mathématique
- Heatmap par éléments de notation : Vue détaillée question par question avec couleurs authentiques de la base de données
4. Tableau Détaillé des Résultats
- Liste alphabétique des élèves avec leurs scores
- Détail par exercice au format "score/total"
- Scores totaux précis avec décimales
- Navigation intuitive vers les détails
🏗️ Architecture Technique
Backend - Route /assessments/<id>/results
Fichier : routes/assessments.py (lignes 187-429)
@bp.route('/<int:id>/results')
@handle_db_errors
def results(id):
# 1. Récupération de l'évaluation avec tous ses détails
assessment_repo = AssessmentRepository()
assessment = assessment_repo.get_with_full_details_or_404(id)
# 2. Calculs des scores et statistiques
students_scores, exercise_scores = assessment.calculate_student_scores()
statistics = assessment.get_assessment_statistics()
# 3. Préparation des données heatmaps
# - Collecte des compétences et domaines avec couleurs depuis la BD
# - Calcul des scores agrégés par compétence/domaine
# - Préparation des données pour éléments de notation
# 4. Rendu du template avec toutes les données
return render_template('assessment_results.html', ...)
Logique de Préparation des Heatmaps
Compétences et Domaines :
# Récupération des couleurs depuis la base de données
all_competences = {comp.name: comp.color for comp in Competence.query.all()}
all_domains = {domain.id: {'name': domain.name, 'color': domain.color}
for domain in Domain.query.all()}
# Calcul des pourcentages par élève/compétence
for student_data in sorted_students:
for comp in competences_list:
if comp in student_scores_by_competence:
percentage = (student_scores_by_competence[comp] /
student_totals_by_competence[comp]) * 100
student_row.append(round(percentage, 1))
Éléments de Notation :
# Couleurs par exercice pour meilleure distinction visuelle
exercise_colors = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', ...]
# Données détaillées avec valeurs originales
for element_key in grading_elements_list:
student_row.append({
'value': grade.value,
'grading_type': element.grading_type,
'max_points': element.max_points
})
Frontend - Template assessment_results.html
Structure HTML
Statistiques Générales :
<div class="bg-white shadow rounded-lg">
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
<div class="text-center">
<div class="text-2xl font-bold text-blue-600">{{ statistics.count }}</div>
<div class="text-sm text-gray-500">Élèves</div>
</div>
<!-- Autres métriques... -->
</div>
</div>
Histogramme Interactif :
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-medium text-gray-900">Distribution des notes</h2>
</div>
<div class="px-6 py-4">
<canvas id="scoresChart" width="400" height="200"></canvas>
</div>
</div>
JavaScript Chart.js avec Tooltips Personnalisés
Données des Élèves :
const studentsData = [
{% for student_data in students_scores %}
{
name: "{{ student_data.student.last_name }} {{ student_data.student.first_name }}",
score: {{ student_data.total_score }}
}{% if not loop.last %},{% endif %}
{% endfor %}
];
Configuration Chart.js :
new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Nombre d\'élèves',
data: bins,
backgroundColor: 'rgba(59, 130, 246, 0.5)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 1
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label: function(tooltipItem) {
const binIndex = tooltipItem.dataIndex;
const count = bins[binIndex];
const students = studentsInBins[binIndex];
if (count === 1) {
return '1 élève : ' + students[0];
} else {
return `${count} élèves : ${students.join(', ')}`;
}
}
},
maxWidth: 400
}
}
}
});
Heatmaps Canvas Personnalisées
Fonction createHeatmap() pour Compétences/Domaines
function createHeatmap(canvasId, data, type) {
// 1. Configuration des dimensions adaptatives
const cellWidth = Math.max(minCellWidth, (canvas.width - padding * 2) / students.length);
const cellHeight = Math.max(minCellHeight, (canvas.height - padding * 2) / categories.length);
// 2. Fonction de couleur avec dégradé HSL continu
function getScoreColor(score) {
const factor = percentage / 100;
return interpolateColorHSL('#ef4444', '#22c55e', factor); // Rouge → Vert
}
// 3. Rendu des cellules avec labels rotatifs
// 4. Légende avec échelle continue 0% → 100%
}
Fonction createGradingElementsHeatmap() pour Éléments
function createGradingElementsHeatmap(canvasId, data) {
// 1. Récupération des couleurs authentiques depuis la BD
const scaleColors = data.scale_colors;
// 2. Fonction de couleur selon le type
function getGradeColor(gradeData) {
if (gradeData.grading_type === 'score') {
// Utiliser les couleurs de l'échelle configurée
return scaleColors[gradeData.value].color;
} else if (gradeData.grading_type === 'notes') {
// Dégradé HSL basé sur le pourcentage
const percentage = (parseFloat(gradeData.value) / gradeData.max_points) * 100;
return interpolateColorHSL('#ef4444', '#22c55e', percentage / 100);
}
}
// 3. Labels colorés par exercice pour distinction visuelle
// 4. Légende spécialisée avec échelle complète
}
Interpolation HSL pour Dégradés Continus
function interpolateColorHSL(color1, color2, factor) {
const rgb1 = hexToRgb(color1);
const rgb2 = hexToRgb(color2);
const hsl1 = rgbToHsl(rgb1.r, rgb1.g, rgb1.b);
const hsl2 = rgbToHsl(rgb2.r, rgb2.g, rgb2.b);
// Interpolation en HSL avec gestion des transitions de teinte
let deltaH = hsl2.h - hsl1.h;
if (deltaH > 180) hsl2.h -= 360;
else if (deltaH < -180) hsl2.h += 360;
const h = hsl1.h + (hsl2.h - hsl1.h) * factor;
const s = hsl1.s + (hsl2.s - hsl1.s) * factor;
const l = hsl1.l + (hsl2.l - hsl1.l) * factor;
return rgbToHex(...hslToRgb(normalizedH, s, l));
}
🎨 Interface Utilisateur
Design System Cohérent
- Couleurs : Palette Tailwind avec bleus, verts, oranges
- Typographie : Hiérarchie claire avec font-bold et font-medium
- Espacement : Grid system responsive avec gap-4/6
- Cartes :
bg-white shadow rounded-lgpour la consistance
Responsive Design
- Mobile : Scrolling horizontal pour les heatmaps
- Tablet : Grilles adaptatives 2-3 colonnes
- Desktop : Grilles 6 colonnes pour les statistiques
Interactions
- Tooltips Chart.js : Liste des élèves par tranche de notes
- Heatmaps hover : Affichage des scores détaillés
- Navigation : Lien retour vers l'évaluation
📊 Types de Données Affichées
1. Scores Calculés
- Notes numériques : Sommation directe des valeurs décimales
- Scores de compétences : Formule
score * max_points / 3 - Valeurs spéciales :
.= 0,d= dispensé,a= absent
2. Agrégations
- Par compétence : Moyenne pondérée des éléments associés
- Par domaine : Moyenne pondérée des éléments du domaine
- Par exercice : Somme des éléments de l'exercice
3. Métadonnées
- Couleurs authentiques : Récupérées depuis les modèles BD
- Libellés configurables : Depuis l'échelle de notation
- Statuts de correction : Basés sur la saisie des notes
🔧 Gestion des Cas Particuliers
Données Manquantes
if (score === null || score === undefined) {
return '#f3f4f6'; // Gris clair
}
Valeurs Spéciales
# Configuration depuis la base de données
scale_colors = config_manager.get_competence_scale_values()
if scaleColors[value]:
return scaleColors[value].color
Calculs de Pourcentage
if student_totals_by_competence[comp] > 0:
percentage = (student_scores_by_competence[comp] /
student_totals_by_competence[comp]) * 100
student_row.append(round(percentage, 1))
🚀 Performance et Optimisations
Backend
- Requêtes optimisées :
get_with_full_details_or_404()avec jointures - Calculs unifiés : Services découplés avec injection de dépendances
- Cache des couleurs : Récupération unique depuis la BD
Frontend
- Canvas natif : Rendu des heatmaps sans librairies lourdes
- Chart.js CDN : Chargement optimisé des graphiques
- Responsive dimensions : Calculs adaptatifs des tailles
JavaScript
- Fonctions globales : Réutilisation des utilitaires HSL
- Gestion d'erreurs : Fallbacks pour données manquantes
- Optimisation DOM : Mise à jour ciblée des éléments
🎯 Cas d'Usage Typiques
1. Analyse de Classe
L'enseignant consulte la page après correction complète pour :
- Identifier les difficultés : Compétences en rouge sur les heatmaps
- Repérer les réussites : Domaines bien maîtrisés en vert
- Analyser la distribution : Homogénéité/hétérogénéité de la classe
2. Feedback Individuel
Utilisation du tableau détaillé pour :
- Scores par exercice : Performance question par question
- Comparaison relative : Position dans la distribution
- Identification des lacunes : Éléments non maîtrisés
3. Reporting Pédagogique
Les statistiques servent à :
- Évaluer la difficulté : Moyenne générale vs objectifs
- Adapter la pédagogie : Révisions ciblées sur les compétences faibles
- Communiquer avec les familles : Résultats contextualisés
🔄 Évolutions Récentes (2025)
✅ Améliorations Implementées
- Tooltips personnalisés dans l'histogramme avec liste des élèves
- Heatmap des éléments de notation avec couleurs authentiques de la BD
- Distinction visuelle par exercice avec palette de couleurs dédiée
- Interpolation HSL pour dégradés continus sans sauts de couleur
- Gestion des erreurs JavaScript avec fonctions globales structurées
🎨 Améliorations UX
- Maxwidth tooltips : Affichage optimal sur 400px
- Police adaptative : 10px pour les éléments, 12px pour les heatmaps classiques
- Troncature intelligente : Libellés longs coupés à 22 caractères
- Légende complète : Échelle de notation avec explications
🔍 Tests et Validation
Tests d'Intégration
# Test de la route des résultats
uv run pytest tests/test_routes_assessments.py -k "result"
# Validation des calculs
uv run pytest tests/test_models.py -k "calculate_student_scores"
Tests Frontend
- Vérification Chart.js : Présence des tooltips avec noms d'élèves
- Validation heatmaps : Rendu correct des couleurs authentiques
- Test responsive : Affichage sur différentes tailles d'écran
🔗 Navigation et Intégration
Points d'Entrée
- Dashboard évaluation : Bouton "Résultats" sur
/assessments/<id> - Liste des évaluations : Lien direct depuis la progression
- Navigation breadcrumb : Retour vers l'évaluation parente
Données Partagées
- Statistiques : Réutilisées dans d'autres vues analytiques
- Scores calculés : Cohérents avec les autres pages de l'application
- Configuration : Respect de l'échelle de notation globale
La page des résultats d'évaluation représente l'aboutissement de l'écosystème Notytex, offrant une analyse complète et visuelle des performances avec des outils de diagnostic pédagogique avancés. 🎓📊