feat: intégrer la configuration des compétences dans la gestion des assessments
- Remplacer le champ texte libre par une liste déroulante des compétences configurées - Charger dynamiquement les compétences dans les routes d'assessments (new/edit) - Moderniser le calcul des scores pour utiliser l'échelle de compétences configurable - Adapter la logique de scoring aux valeurs personnalisées (0-3 ou autres) - Respecter le paramètre 'included_in_total' de chaque valeur de l'échelle - Maintenir la compatibilité descendante avec l'ancienne formule 🎯 Améliore l'intégration entre la configuration système et l'interface utilisateur 📊 Rend les calculs de scores plus flexibles et cohérents avec la configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										44
									
								
								models.py
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								models.py
									
									
									
									
									
								
							| @@ -112,6 +112,10 @@ class Assessment(db.Model): | ||||
|         Retourne un dictionnaire avec les scores par élève et par exercice.""" | ||||
|         from collections import defaultdict | ||||
|         import statistics | ||||
|         from app_config import config_manager | ||||
|          | ||||
|         # Récupérer l'échelle des compétences configurée | ||||
|         competence_scale = config_manager.get_competence_scale_values() | ||||
|          | ||||
|         students_scores = {} | ||||
|         exercise_scores = defaultdict(lambda: defaultdict(float)) | ||||
| @@ -144,16 +148,44 @@ class Assessment(db.Model): | ||||
|                                     exercise_max_points += element.max_points | ||||
|                                 except ValueError: | ||||
|                                     pass | ||||
|                         else:  # compétences | ||||
|                             if grade.value == '.': | ||||
|                                 # '.' signifie non évalué = 0 point mais on compte le max | ||||
|                         else:  # compétences - utiliser la nouvelle échelle | ||||
|                             grade_value = grade.value.strip() | ||||
|                              | ||||
|                             # Gérer les valeurs numériques et string | ||||
|                             scale_key = int(grade_value) if grade_value.isdigit() else grade_value | ||||
|                              | ||||
|                             if scale_key in competence_scale: | ||||
|                                 scale_config = competence_scale[scale_key] | ||||
|                                  | ||||
|                                 if scale_config['included_in_total']: | ||||
|                                     # Calculer le score selon l'échelle configurée | ||||
|                                     if grade_value == '.': | ||||
|                                         # Non évalué = 0 point | ||||
|                                         exercise_score += 0 | ||||
|                                 exercise_max_points += (1/3) * 3 * element.max_points  # Score max de 3 | ||||
|                                     else: | ||||
|                                         # Calculer le score proportionnel | ||||
|                                         # Trouver la valeur maximale de l'échelle | ||||
|                                         max_scale_value = max([ | ||||
|                                             int(k) if str(k).isdigit() else 0  | ||||
|                                             for k in competence_scale.keys()  | ||||
|                                             if competence_scale[k]['included_in_total'] and k != '.' | ||||
|                                         ]) | ||||
|                                          | ||||
|                                         if max_scale_value > 0: | ||||
|                                             if grade_value.isdigit(): | ||||
|                                                 score_ratio = int(grade_value) / max_scale_value | ||||
|                                                 exercise_score += score_ratio * element.max_points | ||||
|                                      | ||||
|                                     # Compter les points maximum (sauf pour '.') | ||||
|                                     if grade_value != '.': | ||||
|                                         exercise_max_points += element.max_points | ||||
|                                 # Si not included_in_total, on ne compte ni score ni max | ||||
|                             else: | ||||
|                                 # Valeur non reconnue, utiliser l'ancienne logique par défaut | ||||
|                                 try: | ||||
|                                     score_value = float(grade.value) | ||||
|                                     score_value = float(grade_value) | ||||
|                                     exercise_score += (1/3) * score_value * element.max_points | ||||
|                                     exercise_max_points += (1/3) * 3 * element.max_points  # Score max de 3 | ||||
|                                     exercise_max_points += element.max_points | ||||
|                                 except ValueError: | ||||
|                                     pass | ||||
|                  | ||||
|   | ||||
| @@ -160,23 +160,35 @@ def edit(id): | ||||
|             exercise_data['grading_elements'].append(element_data) | ||||
|         exercises_data.append(exercise_data) | ||||
|      | ||||
|     # Récupérer les compétences configurées | ||||
|     from app_config import config_manager | ||||
|     competences = config_manager.get_competences_list() | ||||
|      | ||||
|     return render_template('assessment_form_unified.html',  | ||||
|                          form=form,  | ||||
|                          title='Modifier l\'évaluation complète',  | ||||
|                          assessment=assessment,  | ||||
|                          exercises_json=exercises_data, | ||||
|                          is_edit=True) | ||||
|                          is_edit=True, | ||||
|                          competences=competences) | ||||
|  | ||||
| @bp.route('/new', methods=['GET', 'POST']) | ||||
| @handle_db_errors | ||||
| def new(): | ||||
|     from app_config import config_manager | ||||
|     form = AssessmentForm() | ||||
|      | ||||
|     result = _handle_unified_assessment_request(form, is_edit=False) | ||||
|     if result: | ||||
|         return result | ||||
|      | ||||
|     return render_template('assessment_form_unified.html', form=form, title='Nouvelle évaluation complète') | ||||
|     # Récupérer les compétences configurées | ||||
|     competences = config_manager.get_competences_list() | ||||
|      | ||||
|     return render_template('assessment_form_unified.html',  | ||||
|                          form=form,  | ||||
|                          title='Nouvelle évaluation complète', | ||||
|                          competences=competences) | ||||
|  | ||||
| @bp.route('/<int:id>/results') | ||||
| @handle_db_errors | ||||
|   | ||||
| @@ -241,7 +241,14 @@ | ||||
|             </div> | ||||
|             <div> | ||||
|                 <label class="block text-xs font-medium text-gray-700 mb-1">Compétence</label> | ||||
|                 <input type="text" class="element-skill block w-full border border-gray-300 rounded-md px-2 py-1 text-sm focus:ring-purple-500 focus:border-purple-500"> | ||||
|                 <select class="element-skill block w-full border border-gray-300 rounded-md px-2 py-1 text-sm focus:ring-purple-500 focus:border-purple-500"> | ||||
|                     <option value="">Non spécifiée</option> | ||||
|                     {% for competence in competences %} | ||||
|                         <option value="{{ competence.name }}" data-color="{{ competence.color }}" data-icon="{{ competence.icon }}"> | ||||
|                             {{ competence.name }} | ||||
|                         </option> | ||||
|                     {% endfor %} | ||||
|                 </select> | ||||
|             </div> | ||||
|             <div> | ||||
|                 <label class="block text-xs font-medium text-gray-700 mb-1">Points max</label> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user