feat: add heatmap to resultat
This commit is contained in:
@@ -187,6 +187,7 @@ def new():
|
||||
@bp.route('/<int:id>/results')
|
||||
@handle_db_errors
|
||||
def results(id):
|
||||
from models import Competence, Domain
|
||||
assessment_repo = AssessmentRepository()
|
||||
assessment = assessment_repo.get_with_full_details_or_404(id)
|
||||
|
||||
@@ -204,12 +205,228 @@ def results(id):
|
||||
# Préparer les données pour l'histogramme
|
||||
scores = [data['total_score'] for data in students_scores.values()]
|
||||
|
||||
# === NOUVEAUX : Préparer les données pour les heatmaps ===
|
||||
|
||||
# Récupérer toutes les compétences et domaines avec leurs couleurs depuis la BD
|
||||
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()}
|
||||
|
||||
# Collecter toutes les compétences et domaines présents dans cette évaluation
|
||||
competences_in_eval = set()
|
||||
domains_in_eval = set()
|
||||
|
||||
for exercise in assessment.exercises:
|
||||
for element in exercise.grading_elements:
|
||||
if element.skill:
|
||||
competences_in_eval.add(element.skill)
|
||||
if element.domain_id:
|
||||
domains_in_eval.add(element.domain_id)
|
||||
|
||||
# Préparer les données heatmap compétences
|
||||
students_list = [f"{s['student'].last_name} {s['student'].first_name}" for s in sorted_students]
|
||||
competences_list = sorted(competences_in_eval)
|
||||
|
||||
# Calculer les scores par élève/compétence
|
||||
competences_scores_matrix = []
|
||||
for student_data in sorted_students:
|
||||
student_scores_by_competence = {}
|
||||
student_totals_by_competence = {}
|
||||
|
||||
for exercise_id, exercise_data in student_data['exercises'].items():
|
||||
# Récupérer l'exercice pour accéder aux grading_elements
|
||||
exercise = next(ex for ex in assessment.exercises if ex.id == exercise_id)
|
||||
|
||||
for element in exercise.grading_elements:
|
||||
if element.skill and element.skill in competences_in_eval:
|
||||
# Trouver la note correspondante
|
||||
grade = None
|
||||
for g in element.grades:
|
||||
if g.student_id == student_data['student'].id:
|
||||
grade = g
|
||||
break
|
||||
|
||||
if grade and grade.value:
|
||||
from models import GradingCalculator
|
||||
score = GradingCalculator.calculate_score(grade.value, element.grading_type, element.max_points)
|
||||
if score is not None: # Exclure les dispensés
|
||||
if element.skill not in student_scores_by_competence:
|
||||
student_scores_by_competence[element.skill] = 0
|
||||
student_totals_by_competence[element.skill] = 0
|
||||
student_scores_by_competence[element.skill] += score
|
||||
student_totals_by_competence[element.skill] += element.max_points
|
||||
|
||||
# Calculer les pourcentages par compétence pour cet élève
|
||||
student_row = []
|
||||
for comp in competences_list:
|
||||
if comp in student_scores_by_competence and student_totals_by_competence[comp] > 0:
|
||||
percentage = (student_scores_by_competence[comp] / student_totals_by_competence[comp]) * 100
|
||||
student_row.append(round(percentage, 1))
|
||||
else:
|
||||
student_row.append(None) # Pas de données pour cette compétence
|
||||
|
||||
competences_scores_matrix.append(student_row)
|
||||
|
||||
# Préparer les données heatmap domaines
|
||||
domains_list = []
|
||||
domains_colors = {}
|
||||
# Trier les domain_id (entiers) puis récupérer les noms
|
||||
sorted_domain_ids = sorted(domains_in_eval)
|
||||
for domain_id in sorted_domain_ids:
|
||||
if domain_id in all_domains:
|
||||
domain_name = all_domains[domain_id]['name']
|
||||
domains_list.append(domain_name)
|
||||
domains_colors[domain_name] = all_domains[domain_id]['color']
|
||||
|
||||
# Calculer les scores par élève/domaine
|
||||
domains_scores_matrix = []
|
||||
for student_data in sorted_students:
|
||||
student_scores_by_domain = {}
|
||||
student_totals_by_domain = {}
|
||||
|
||||
for exercise_id, exercise_data in student_data['exercises'].items():
|
||||
# Récupérer l'exercice pour accéder aux grading_elements
|
||||
exercise = next(ex for ex in assessment.exercises if ex.id == exercise_id)
|
||||
|
||||
for element in exercise.grading_elements:
|
||||
if element.domain_id and element.domain_id in domains_in_eval:
|
||||
domain_name = all_domains[element.domain_id]['name']
|
||||
|
||||
# Trouver la note correspondante
|
||||
grade = None
|
||||
for g in element.grades:
|
||||
if g.student_id == student_data['student'].id:
|
||||
grade = g
|
||||
break
|
||||
|
||||
if grade and grade.value:
|
||||
from models import GradingCalculator
|
||||
score = GradingCalculator.calculate_score(grade.value, element.grading_type, element.max_points)
|
||||
if score is not None: # Exclure les dispensés
|
||||
if domain_name not in student_scores_by_domain:
|
||||
student_scores_by_domain[domain_name] = 0
|
||||
student_totals_by_domain[domain_name] = 0
|
||||
student_scores_by_domain[domain_name] += score
|
||||
student_totals_by_domain[domain_name] += element.max_points
|
||||
|
||||
# Calculer les pourcentages par domaine pour cet élève
|
||||
student_row = []
|
||||
for domain in domains_list:
|
||||
if domain in student_scores_by_domain and student_totals_by_domain[domain] > 0:
|
||||
percentage = (student_scores_by_domain[domain] / student_totals_by_domain[domain]) * 100
|
||||
student_row.append(round(percentage, 1))
|
||||
else:
|
||||
student_row.append(None) # Pas de données pour ce domaine
|
||||
|
||||
domains_scores_matrix.append(student_row)
|
||||
|
||||
# Préparer les couleurs des compétences pour celles présentes dans l'évaluation
|
||||
competences_colors = {comp: all_competences.get(comp, '#6b7280') for comp in competences_list}
|
||||
|
||||
heatmap_competences = {
|
||||
'students': students_list,
|
||||
'competences': competences_list,
|
||||
'scores': competences_scores_matrix,
|
||||
'colors': competences_colors
|
||||
} if competences_list else None
|
||||
|
||||
heatmap_domains = {
|
||||
'students': students_list,
|
||||
'domains': domains_list,
|
||||
'scores': domains_scores_matrix,
|
||||
'colors': domains_colors
|
||||
} if domains_list else None
|
||||
|
||||
# === NOUVEAU : Préparer les données heatmap éléments de notation ===
|
||||
|
||||
# Collecter tous les éléments de notation de l'évaluation
|
||||
grading_elements_list = []
|
||||
grading_elements_info = {}
|
||||
|
||||
for exercise in sorted(assessment.exercises, key=lambda x: x.order):
|
||||
for element in exercise.grading_elements:
|
||||
element_key = f"{exercise.title} - {element.label}"
|
||||
grading_elements_list.append(element_key)
|
||||
grading_elements_info[element_key] = {
|
||||
'element': element,
|
||||
'exercise': exercise
|
||||
}
|
||||
|
||||
# Collecter les données détaillées par élève/élément avec valeurs originales
|
||||
grading_elements_detailed_matrix = []
|
||||
for student_data in sorted_students:
|
||||
student_row = []
|
||||
|
||||
for element_key in grading_elements_list:
|
||||
element_info = grading_elements_info[element_key]
|
||||
element = element_info['element']
|
||||
|
||||
# Trouver la note correspondante
|
||||
grade = None
|
||||
for g in element.grades:
|
||||
if g.student_id == student_data['student'].id:
|
||||
grade = g
|
||||
break
|
||||
|
||||
if grade and grade.value:
|
||||
# Stocker les données complètes pour le rendu avec couleurs spécifiques
|
||||
student_row.append({
|
||||
'value': grade.value,
|
||||
'grading_type': element.grading_type,
|
||||
'max_points': element.max_points
|
||||
})
|
||||
else:
|
||||
student_row.append(None) # Pas de note
|
||||
|
||||
grading_elements_detailed_matrix.append(student_row)
|
||||
|
||||
# Préparer les couleurs des éléments basées sur les exercices pour une meilleure distinction
|
||||
grading_elements_colors = {}
|
||||
exercise_colors = [
|
||||
'#3b82f6', # Bleu
|
||||
'#10b981', # Vert
|
||||
'#f59e0b', # Orange
|
||||
'#8b5cf6', # Violet
|
||||
'#ef4444', # Rouge
|
||||
'#06b6d4', # Cyan
|
||||
'#84cc16', # Vert clair
|
||||
'#f97316', # Orange foncé
|
||||
]
|
||||
|
||||
exercises_seen = {}
|
||||
color_index = 0
|
||||
|
||||
for element_key in grading_elements_list:
|
||||
element_info = grading_elements_info[element_key]
|
||||
exercise = element_info['exercise']
|
||||
|
||||
# Assigner une couleur unique par exercice
|
||||
if exercise.id not in exercises_seen:
|
||||
exercises_seen[exercise.id] = exercise_colors[color_index % len(exercise_colors)]
|
||||
color_index += 1
|
||||
|
||||
grading_elements_colors[element_key] = exercises_seen[exercise.id]
|
||||
|
||||
# Récupérer la configuration des couleurs depuis la base de données
|
||||
from app_config import config_manager
|
||||
scale_colors = config_manager.get_competence_scale_values()
|
||||
|
||||
heatmap_grading_elements = {
|
||||
'students': students_list,
|
||||
'elements': grading_elements_list,
|
||||
'detailed_scores': grading_elements_detailed_matrix,
|
||||
'colors': grading_elements_colors,
|
||||
'scale_colors': scale_colors
|
||||
} if grading_elements_list else None
|
||||
|
||||
return render_template('assessment_results.html',
|
||||
assessment=assessment,
|
||||
students_scores=sorted_students,
|
||||
statistics=statistics,
|
||||
total_max_points=total_max_points,
|
||||
scores_json=scores)
|
||||
scores_json=scores,
|
||||
heatmap_competences=heatmap_competences,
|
||||
heatmap_domains=heatmap_domains,
|
||||
heatmap_grading_elements=heatmap_grading_elements)
|
||||
|
||||
@bp.route('/<int:id>/delete', methods=['POST'])
|
||||
@handle_db_errors
|
||||
|
||||
Reference in New Issue
Block a user