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