fix: js errors
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user