215 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, current_app
 | |
| from models import db, ClassGroup
 | |
| from forms import AssessmentForm
 | |
| from services import AssessmentService
 | |
| from repositories import AssessmentRepository
 | |
| from utils import handle_db_errors, ValidationError
 | |
| 
 | |
| bp = Blueprint('assessments', __name__, url_prefix='/assessments')
 | |
| 
 | |
| @bp.route('/')
 | |
| @handle_db_errors
 | |
| def list():
 | |
|     assessment_repo = AssessmentRepository()
 | |
|     
 | |
|     # Récupérer les paramètres de filtrage
 | |
|     trimester_filter = request.args.get('trimester', '')
 | |
|     class_filter = request.args.get('class', '')
 | |
|     correction_filter = request.args.get('correction', '')
 | |
|     sort_by = request.args.get('sort', 'date_desc')
 | |
|     
 | |
|     # Utiliser le repository pour les filtres
 | |
|     assessments = assessment_repo.find_by_filters(
 | |
|         trimester=int(trimester_filter) if trimester_filter else None,
 | |
|         class_id=int(class_filter) if class_filter else None,
 | |
|         correction_status=correction_filter if correction_filter else None,
 | |
|         sort_by=sort_by
 | |
|     )
 | |
|     
 | |
|     # Récupérer toutes les classes pour le filtre
 | |
|     classes = ClassGroup.query.order_by(ClassGroup.name.asc()).all()
 | |
|     
 | |
|     return render_template('assessments.html', 
 | |
|                          assessments=assessments, 
 | |
|                          classes=classes,
 | |
|                          current_trimester=trimester_filter,
 | |
|                          current_class=class_filter,
 | |
|                          current_correction=correction_filter,
 | |
|                          current_sort=sort_by)
 | |
| 
 | |
| # Route obsolète supprimée - utiliser new_unified à la place
 | |
| 
 | |
| @bp.route('/<int:id>')
 | |
| @handle_db_errors
 | |
| def detail(id):
 | |
|     assessment_repo = AssessmentRepository()
 | |
|     assessment = assessment_repo.get_with_full_details_or_404(id)
 | |
|     return render_template('assessment_detail.html', assessment=assessment)
 | |
| 
 | |
| def _handle_unified_assessment_request(form, assessment=None, is_edit=False):
 | |
|     """Fonction helper pour traiter les requêtes JSON d'évaluation unifiée"""
 | |
|     # Ne traiter que les requêtes POST
 | |
|     if request.method != 'POST':
 | |
|         return None
 | |
|         
 | |
|     if request.is_json:
 | |
|         try:
 | |
|             data = request.get_json()
 | |
|             if not data:
 | |
|                 return jsonify({'success': False, 'error': 'Aucune donnée fournie'}), 400
 | |
|             
 | |
|             # Peupler le formulaire pour validation CSRF
 | |
|             form.csrf_token.data = data.get('csrf_token')
 | |
|             
 | |
|             # Traitement via le service
 | |
|             if is_edit and assessment:
 | |
|                 processed_assessment = AssessmentService.process_assessment_with_exercises(
 | |
|                     data, is_edit=True, existing_assessment=assessment
 | |
|                 )
 | |
|             else:
 | |
|                 processed_assessment = AssessmentService.process_assessment_with_exercises(data)
 | |
|             
 | |
|             db.session.commit()
 | |
|             flash('Évaluation ' + ('modifiée' if is_edit else 'créée') + ' avec succès !', 'success')
 | |
|             return jsonify({'success': True, 'assessment_id': processed_assessment.id})
 | |
|             
 | |
|         except ValidationError as e:
 | |
|             current_app.logger.warning(f'Erreur de validation: {e}')
 | |
|             return jsonify({'success': False, 'error': str(e)}), 400
 | |
|         except ValueError as e:
 | |
|             current_app.logger.warning(f'Erreur de données: {e}')
 | |
|             return jsonify({'success': False, 'error': str(e)}), 400
 | |
|             
 | |
|     else:  # request.method == 'POST' and not request.is_json
 | |
|         # Traitement classique du formulaire (fallback)
 | |
|         if form.validate_on_submit():
 | |
|             if is_edit and assessment:
 | |
|                 AssessmentService.update_assessment_basic_info(assessment, {
 | |
|                     'title': form.title.data,
 | |
|                     'description': form.description.data,
 | |
|                     'date': form.date.data,
 | |
|                     'trimester': form.trimester.data,
 | |
|                     'class_group_id': form.class_group_id.data,
 | |
|                     'coefficient': form.coefficient.data
 | |
|                 })
 | |
|             else:
 | |
|                 assessment = AssessmentService.create_assessment({
 | |
|                     'title': form.title.data,
 | |
|                     'description': form.description.data,
 | |
|                     'date': form.date.data,
 | |
|                     'trimester': form.trimester.data,
 | |
|                     'class_group_id': form.class_group_id.data,
 | |
|                     'coefficient': form.coefficient.data
 | |
|                 })
 | |
|             db.session.commit()
 | |
|             flash('Évaluation ' + ('modifiée' if is_edit else 'créée') + ' avec succès !', 'success')
 | |
|             return redirect(url_for('assessments.detail', id=assessment.id))
 | |
|     
 | |
|     return None
 | |
| 
 | |
| @bp.route('/<int:id>/edit', methods=['GET', 'POST'])
 | |
| @handle_db_errors
 | |
| def edit(id):
 | |
|     assessment_repo = AssessmentRepository()
 | |
|     assessment = assessment_repo.get_with_full_details_or_404(id)
 | |
|     form = AssessmentForm(obj=assessment)
 | |
|     
 | |
|     result = _handle_unified_assessment_request(form, assessment, is_edit=True)
 | |
|     if result:
 | |
|         return result
 | |
|     
 | |
|     # Préparer les exercices pour la sérialisation JSON
 | |
|     exercises_data = []
 | |
|     for exercise in assessment.exercises:
 | |
|         exercise_data = {
 | |
|             'id': exercise.id,
 | |
|             'title': exercise.title,
 | |
|             'description': exercise.description or '',
 | |
|             'order': exercise.order,
 | |
|             'grading_elements': []
 | |
|         }
 | |
|         for element in exercise.grading_elements:
 | |
|             element_data = {
 | |
|                 'id': element.id,
 | |
|                 'label': element.label,
 | |
|                 'description': element.description or '',
 | |
|                 'skill': element.skill or '',
 | |
|                 'max_points': float(element.max_points),
 | |
|                 'grading_type': element.grading_type,
 | |
|                 'domain_id': element.domain_id
 | |
|             }
 | |
|             exercise_data['grading_elements'].append(element_data)
 | |
|         exercises_data.append(exercise_data)
 | |
|     
 | |
|     # Récupérer les compétences et domaines configurées
 | |
|     from app_config import config_manager
 | |
|     competences = config_manager.get_competences_list()
 | |
|     domains = config_manager.get_domains_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,
 | |
|                          competences=competences,
 | |
|                          domains=domains)
 | |
| 
 | |
| @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
 | |
|     
 | |
|     # Récupérer les compétences et domaines configurées
 | |
|     competences = config_manager.get_competences_list()
 | |
|     domains = config_manager.get_domains_list()
 | |
|     
 | |
|     return render_template('assessment_form_unified.html', 
 | |
|                          form=form, 
 | |
|                          title='Nouvelle évaluation complète',
 | |
|                          competences=competences,
 | |
|                          domains=domains)
 | |
| 
 | |
| @bp.route('/<int:id>/results')
 | |
| @handle_db_errors
 | |
| def results(id):
 | |
|     assessment_repo = AssessmentRepository()
 | |
|     assessment = assessment_repo.get_with_full_details_or_404(id)
 | |
|     
 | |
|     # Calculer les scores des élèves
 | |
|     students_scores, exercise_scores = assessment.calculate_student_scores()
 | |
|     
 | |
|     # Trier les élèves par ordre alphabétique
 | |
|     sorted_students = sorted(students_scores.values(), 
 | |
|                            key=lambda x: (x['student'].last_name.lower(), x['student'].first_name.lower()))
 | |
|     
 | |
|     # Calculer les statistiques
 | |
|     statistics = assessment.get_assessment_statistics()
 | |
|     total_max_points = assessment.get_total_max_points()
 | |
|     
 | |
|     # Préparer les données pour l'histogramme
 | |
|     scores = [data['total_score'] for data in students_scores.values()]
 | |
|     
 | |
|     return render_template('assessment_results.html', 
 | |
|                          assessment=assessment,
 | |
|                          students_scores=sorted_students,
 | |
|                          statistics=statistics,
 | |
|                          total_max_points=total_max_points,
 | |
|                          scores_json=scores)
 | |
| 
 | |
| @bp.route('/<int:id>/delete', methods=['POST'])
 | |
| @handle_db_errors
 | |
| def delete(id):
 | |
|     assessment_repo = AssessmentRepository()
 | |
|     assessment = assessment_repo.get_or_404(id)
 | |
|     title = assessment.title  # Conserver pour le log
 | |
|     db.session.delete(assessment)
 | |
|     db.session.commit()
 | |
|     current_app.logger.info(f'Évaluation supprimée: {title} (ID: {id})')
 | |
|     flash('Évaluation supprimée avec succès !', 'success')
 | |
|     return redirect(url_for('assessments.list')) |