Files
notytex/routes/assessments.py

190 lines
7.5 KiB
Python

from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, current_app
from models import db, Assessment, ClassGroup
from forms import AssessmentForm
from services import AssessmentService
from utils import handle_db_errors, ValidationError
from datetime import datetime
bp = Blueprint('assessments', __name__, url_prefix='/assessments')
@bp.route('/')
@handle_db_errors
def list():
from sqlalchemy.orm import joinedload
# Récupérer les paramètres de filtrage
trimester_filter = request.args.get('trimester', '')
class_filter = request.args.get('class', '')
sort_by = request.args.get('sort', 'date_desc')
# Construire la requête de base
query = Assessment.query.options(joinedload(Assessment.class_group))
# Appliquer les filtres
if trimester_filter:
query = query.filter(Assessment.trimester == int(trimester_filter))
if class_filter:
query = query.filter(Assessment.class_group_id == int(class_filter))
# Appliquer le tri
if sort_by == 'date_desc':
query = query.order_by(Assessment.date.desc())
elif sort_by == 'date_asc':
query = query.order_by(Assessment.date.asc())
elif sort_by == 'title':
query = query.order_by(Assessment.title.asc())
elif sort_by == 'class':
query = query.join(ClassGroup).order_by(ClassGroup.name.asc())
assessments = query.all()
# 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_sort=sort_by)
# Route obsolète supprimée - utiliser new_unified à la place
@bp.route('/<int:id>')
@handle_db_errors
def detail(id):
from sqlalchemy.orm import joinedload
from models import Exercise, GradingElement
assessment = Assessment.query.options(
joinedload(Assessment.class_group),
joinedload(Assessment.exercises).joinedload(Exercise.grading_elements)
).get_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):
from sqlalchemy.orm import joinedload
from models import Exercise, GradingElement
assessment = Assessment.query.options(
joinedload(Assessment.class_group),
joinedload(Assessment.exercises).joinedload(Exercise.grading_elements)
).get_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
}
exercise_data['grading_elements'].append(element_data)
exercises_data.append(exercise_data)
return render_template('assessment_form_unified.html',
form=form,
title='Modifier l\'évaluation complète',
assessment=assessment,
exercises_json=exercises_data,
is_edit=True)
@bp.route('/new', methods=['GET', 'POST'])
@handle_db_errors
def new():
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')
@bp.route('/<int:id>/delete', methods=['POST'])
@handle_db_errors
def delete(id):
assessment = Assessment.query.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'))