fix: js errors

This commit is contained in:
2025-08-12 06:41:39 +02:00
parent c132419213
commit 11bfc5c5cb
3 changed files with 1284 additions and 1 deletions

View File

@@ -21,6 +21,7 @@ class StudentTrimesterSummary:
grades_by_assessment: Dict[int, Dict] # assessment_id -> {'score': float, 'max': float, 'title': str}
appreciation: Optional[CouncilAppreciation]
performance_status: str # 'excellent', 'good', 'average', 'struggling'
competence_domain_breakdown: Optional[Dict] = None # Données des compétences et domaines
@property
def has_appreciation(self) -> bool:
@@ -97,13 +98,17 @@ class StudentEvaluationService:
student_id, student.class_group_id, trimester
)
# Calculer les données de compétences et domaines
competence_domain_breakdown = self.get_student_competence_domain_breakdown(student_id, trimester)
return StudentTrimesterSummary(
student=student,
overall_average=overall_average,
assessment_count=len([a for a in assessments if self._has_grades(student_id, a)]),
grades_by_assessment=grades_by_assessment,
appreciation=appreciation,
performance_status=performance_status
performance_status=performance_status,
competence_domain_breakdown=competence_domain_breakdown
)
def get_students_summaries(self, class_group_id: int, trimester: int) -> List[StudentTrimesterSummary]:
@@ -121,6 +126,229 @@ class StudentEvaluationService:
return summaries
def get_student_competence_domain_breakdown(self, student_id: int, trimester: int) -> Dict[str, List[Dict]]:
"""
Calcule la répartition des points par compétence et domaine pour un élève.
Returns:
Dict avec 'competences' et 'domains', chacun contenant les points accumulés par évaluation
"""
from models import Student, db, GradingCalculator
student = Student.query.get(student_id)
if not student:
return {'competences': [], 'domains': []}
# Récupérer toutes les évaluations du trimestre
assessments = self.assessment_repo.find_by_class_trimester_with_details(
student.class_group_id, trimester
)
# Structures pour accumuler les données
competences_data = {}
domains_data = {}
# Parcourir chaque évaluation
for assessment in assessments:
grades = self.grade_repo.get_student_grades_by_assessment(student_id, assessment.id)
for grade in grades:
element = grade.grading_element
# Calculer le score pour cet élément
if grade.value and GradingCalculator.is_counted_in_total(grade.value, element.grading_type):
score = GradingCalculator.calculate_score(
grade.value, element.grading_type, element.max_points
)
if score is not None:
# Traiter les compétences
if element.skill:
if element.skill not in competences_data:
competences_data[element.skill] = {
'name': element.skill,
'earned_points': 0.0,
'total_points': 0.0,
'assessments': {}
}
# Accumuler les points
competences_data[element.skill]['earned_points'] += score
competences_data[element.skill]['total_points'] += element.max_points
# Grouper par évaluation
if assessment.id not in competences_data[element.skill]['assessments']:
competences_data[element.skill]['assessments'][assessment.id] = {
'id': assessment.id,
'title': assessment.title,
'earned': 0.0,
'max': 0.0,
'date': assessment.date
}
competences_data[element.skill]['assessments'][assessment.id]['earned'] += score
competences_data[element.skill]['assessments'][assessment.id]['max'] += element.max_points
# Traiter les domaines
if element.domain_id:
domain = element.domain
domain_name = domain.name if domain else f"Domaine {element.domain_id}"
if domain_name not in domains_data:
domains_data[domain_name] = {
'id': element.domain_id,
'name': domain_name,
'color': domain.color if domain else '#6B7280',
'earned_points': 0.0,
'total_points': 0.0,
'assessments': {}
}
# Accumuler les points
domains_data[domain_name]['earned_points'] += score
domains_data[domain_name]['total_points'] += element.max_points
# Grouper par évaluation
if assessment.id not in domains_data[domain_name]['assessments']:
domains_data[domain_name]['assessments'][assessment.id] = {
'id': assessment.id,
'title': assessment.title,
'earned': 0.0,
'max': 0.0,
'date': assessment.date
}
domains_data[domain_name]['assessments'][assessment.id]['earned'] += score
domains_data[domain_name]['assessments'][assessment.id]['max'] += element.max_points
# Récupérer la configuration des compétences pour les couleurs
from app_config import config_manager
competences_config = {comp['name']: comp for comp in config_manager.get_competences_list()}
# Finaliser les données des compétences
competences = []
for comp_name, comp_data in competences_data.items():
config = competences_config.get(comp_name, {})
# Calculer le pourcentage
percentage = (comp_data['earned_points'] / comp_data['total_points'] * 100) if comp_data['total_points'] > 0 else 0
# Convertir les assessments en liste avec calculs d'accumulation
assessments_list = []
# Palette de couleurs moderne et contrastée
colors_palette = ['#3B82F6', '#EF4444', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899', '#06B6D4', '#84CC16']
# D'abord, trier par date pour avoir l'ordre chronologique
sorted_assessments = sorted(comp_data['assessments'].values(),
key=lambda x: x['date'] if x['date'] else datetime.min)
# Calculer l'accumulation progressive
cumulative_earned = 0
cumulative_max = 0
for i, assessment_data in enumerate(sorted_assessments):
# Ajouter les points de cette évaluation à l'accumulation
cumulative_earned += assessment_data['earned']
cumulative_max += assessment_data['max']
# Calculer les pourcentages
cumulative_percentage = (cumulative_earned / comp_data['total_points'] * 100) if comp_data['total_points'] > 0 else 0
assessment_performance = (assessment_data['earned'] / assessment_data['max'] * 100) if assessment_data['max'] > 0 else 0
# Calculer le pourcentage de contribution de cette évaluation par rapport au total final
contribution_percentage = (assessment_data['earned'] / comp_data['total_points'] * 100) if comp_data['total_points'] > 0 else 0
# Couleur cohérente basée sur l'ID de l'évaluation pour garantir la cohérence entre compétences/domaines
assessment_color = colors_palette[assessment_data['id'] % len(colors_palette)]
assessments_list.append({
'id': assessment_data['id'],
'title': assessment_data['title'],
'earned_this': round(assessment_data['earned'], 2), # Points de cette évaluation
'max_this': round(assessment_data['max'], 2), # Max de cette évaluation
'earned_cumulative': round(cumulative_earned, 2), # Total cumulé jusqu'à cette éval
'max_cumulative': round(cumulative_max, 2), # Max cumulé jusqu'à cette éval
'date': assessment_data['date'],
'percentage_cumulative': round(cumulative_percentage, 1), # % cumulé par rapport au total
'percentage_contribution': round(contribution_percentage, 1), # % de contribution individuelle
'performance': round(assessment_performance, 1), # % de réussite de cette évaluation seule
'color': assessment_color # Couleur cohérente basée sur l'ID
})
competences.append({
'name': comp_name,
'color': config.get('color', '#6B7280'),
'earned_points': round(comp_data['earned_points'], 2),
'total_points': round(comp_data['total_points'], 2),
'percentage': round(percentage, 1),
'assessments': assessments_list
})
# Finaliser les données des domaines
domains = []
for domain_data in domains_data.values():
# Calculer le pourcentage
percentage = (domain_data['earned_points'] / domain_data['total_points'] * 100) if domain_data['total_points'] > 0 else 0
# Convertir les assessments en liste avec calculs d'accumulation
assessments_list = []
# Utiliser la même palette que les compétences pour la cohérence
colors_palette = ['#3B82F6', '#EF4444', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899', '#06B6D4', '#84CC16']
# D'abord, trier par date pour avoir l'ordre chronologique
sorted_assessments = sorted(domain_data['assessments'].values(),
key=lambda x: x['date'] if x['date'] else datetime.min)
# Calculer l'accumulation progressive
cumulative_earned = 0
cumulative_max = 0
for i, assessment_data in enumerate(sorted_assessments):
# Ajouter les points de cette évaluation à l'accumulation
cumulative_earned += assessment_data['earned']
cumulative_max += assessment_data['max']
# Calculer les pourcentages
cumulative_percentage = (cumulative_earned / domain_data['total_points'] * 100) if domain_data['total_points'] > 0 else 0
assessment_performance = (assessment_data['earned'] / assessment_data['max'] * 100) if assessment_data['max'] > 0 else 0
# Calculer le pourcentage de contribution de cette évaluation par rapport au total final
contribution_percentage = (assessment_data['earned'] / domain_data['total_points'] * 100) if domain_data['total_points'] > 0 else 0
# Couleur cohérente basée sur l'ID de l'évaluation (même logique que les compétences)
assessment_color = colors_palette[assessment_data['id'] % len(colors_palette)]
assessments_list.append({
'id': assessment_data['id'],
'title': assessment_data['title'],
'earned_this': round(assessment_data['earned'], 2), # Points de cette évaluation
'max_this': round(assessment_data['max'], 2), # Max de cette évaluation
'earned_cumulative': round(cumulative_earned, 2), # Total cumulé jusqu'à cette éval
'max_cumulative': round(cumulative_max, 2), # Max cumulé jusqu'à cette éval
'date': assessment_data['date'],
'percentage_cumulative': round(cumulative_percentage, 1), # % cumulé par rapport au total
'percentage_contribution': round(contribution_percentage, 1), # % de contribution individuelle
'performance': round(assessment_performance, 1), # % de réussite de cette évaluation seule
'color': assessment_color # Couleur cohérente basée sur l'ID
})
domains.append({
'id': domain_data['id'],
'name': domain_data['name'],
'color': domain_data['color'],
'earned_points': round(domain_data['earned_points'], 2),
'total_points': round(domain_data['total_points'], 2),
'percentage': round(percentage, 1),
'assessments': assessments_list
})
# Trier par nom
competences.sort(key=lambda x: x['name'].lower())
domains.sort(key=lambda x: x['name'].lower())
return {
'competences': competences,
'domains': domains
}
def _calculate_assessment_score_for_student(self, assessment: Assessment, student_id: int) -> Optional[float]:
"""Calcule le score d'un élève pour une évaluation."""
grades = self.grade_repo.get_student_grades_by_assessment(student_id, assessment.id)