clean: clean python code
This commit is contained in:
@@ -1,44 +1,28 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, current_app
|
||||
from models import db, Assessment, ClassGroup
|
||||
from models import db, ClassGroup
|
||||
from forms import AssessmentForm
|
||||
# Import du service avec rétrocompatibilité gérée dans services/__init__.py
|
||||
from services import AssessmentService
|
||||
from repositories import AssessmentRepository
|
||||
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
|
||||
assessment_repo = AssessmentRepository()
|
||||
|
||||
# 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()
|
||||
# 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,
|
||||
sort_by=sort_by
|
||||
)
|
||||
|
||||
# Récupérer toutes les classes pour le filtre
|
||||
classes = ClassGroup.query.order_by(ClassGroup.name.asc()).all()
|
||||
@@ -55,12 +39,8 @@ def list():
|
||||
@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)
|
||||
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):
|
||||
@@ -127,12 +107,8 @@ def _handle_unified_assessment_request(form, assessment=None, is_edit=False):
|
||||
@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)
|
||||
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)
|
||||
@@ -199,13 +175,8 @@ def new():
|
||||
@bp.route('/<int:id>/results')
|
||||
@handle_db_errors
|
||||
def results(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)
|
||||
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()
|
||||
@@ -231,7 +202,8 @@ def results(id):
|
||||
@bp.route('/<int:id>/delete', methods=['POST'])
|
||||
@handle_db_errors
|
||||
def delete(id):
|
||||
assessment = Assessment.query.get_or_404(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()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from flask import Blueprint, render_template, request, flash, redirect, url_for, jsonify
|
||||
from app_config import config_manager
|
||||
from models import db, AppConfig, CompetenceScaleValue, Competence
|
||||
from utils import handle_error
|
||||
from utils import handle_error, handle_db_errors
|
||||
import logging
|
||||
|
||||
bp = Blueprint('config', __name__, url_prefix='/config')
|
||||
@@ -74,40 +74,37 @@ def competences():
|
||||
return handle_error(e, "Erreur lors du chargement des compétences")
|
||||
|
||||
@bp.route('/competences/add', methods=['POST'])
|
||||
@handle_db_errors
|
||||
def add_competence():
|
||||
"""Ajouter une nouvelle compétence."""
|
||||
try:
|
||||
name = request.form.get('name')
|
||||
color = request.form.get('color', '#3b82f6')
|
||||
icon = request.form.get('icon', 'star')
|
||||
|
||||
if not name:
|
||||
flash('Le nom de la compétence est requis', 'error')
|
||||
return redirect(url_for('config.competences'))
|
||||
|
||||
# Validation de la couleur hexadécimale
|
||||
import re
|
||||
if not re.match(r'^#[0-9a-fA-F]{6}$', color):
|
||||
flash('Format de couleur invalide', 'error')
|
||||
return redirect(url_for('config.competences'))
|
||||
|
||||
# Vérifier si le nom existe déjà
|
||||
if Competence.query.filter_by(name=name).first():
|
||||
flash(f'Une compétence "{name}" existe déjà', 'error')
|
||||
return redirect(url_for('config.competences'))
|
||||
|
||||
if config_manager.add_competence(name, color, icon):
|
||||
flash(f'Compétence "{name}" ajoutée avec succès', 'success')
|
||||
else:
|
||||
flash('Erreur lors de la sauvegarde', 'error')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Erreur ajout compétence: {e}")
|
||||
flash('Erreur lors de l\'ajout de la compétence', 'error')
|
||||
name = request.form.get('name')
|
||||
color = request.form.get('color', '#3b82f6')
|
||||
icon = request.form.get('icon', 'star')
|
||||
|
||||
if not name:
|
||||
flash('Le nom de la compétence est requis', 'error')
|
||||
return redirect(url_for('config.competences'))
|
||||
|
||||
# Validation de la couleur hexadécimale
|
||||
import re
|
||||
if not re.match(r'^#[0-9a-fA-F]{6}$', color):
|
||||
flash('Format de couleur invalide', 'error')
|
||||
return redirect(url_for('config.competences'))
|
||||
|
||||
# Vérifier si le nom existe déjà
|
||||
if Competence.query.filter_by(name=name).first():
|
||||
flash(f'Une compétence "{name}" existe déjà', 'error')
|
||||
return redirect(url_for('config.competences'))
|
||||
|
||||
if config_manager.add_competence(name, color, icon):
|
||||
flash(f'Compétence "{name}" ajoutée avec succès', 'success')
|
||||
else:
|
||||
flash('Erreur lors de la sauvegarde', 'error')
|
||||
|
||||
return redirect(url_for('config.competences'))
|
||||
|
||||
@bp.route('/competences/update', methods=['POST'])
|
||||
@handle_db_errors
|
||||
def update_competence():
|
||||
"""Modifier une compétence existante."""
|
||||
try:
|
||||
@@ -154,6 +151,7 @@ def update_competence():
|
||||
return redirect(url_for('config.competences'))
|
||||
|
||||
@bp.route('/competences/delete/<int:index>', methods=['POST'])
|
||||
@handle_db_errors
|
||||
def delete_competence(index):
|
||||
"""Supprimer une compétence."""
|
||||
try:
|
||||
@@ -351,6 +349,7 @@ def general():
|
||||
return handle_error(e, "Erreur lors du chargement de la configuration générale")
|
||||
|
||||
@bp.route('/general/update', methods=['POST'])
|
||||
@handle_db_errors
|
||||
def update_general():
|
||||
"""Mettre à jour la configuration générale."""
|
||||
try:
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, current_app
|
||||
from models import db, Assessment, Student, Grade, GradingElement, Exercise
|
||||
from flask import Blueprint, render_template, request, jsonify
|
||||
from models import db, Grade, GradingElement
|
||||
from repositories import AssessmentRepository, StudentRepository, GradeRepository
|
||||
from app_config import config_manager
|
||||
|
||||
bp = Blueprint('grading', __name__)
|
||||
|
||||
@bp.route('/assessments/<int:assessment_id>/grading')
|
||||
def assessment_grading(assessment_id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
students = Student.query.filter_by(class_group_id=assessment.class_group_id).order_by(Student.last_name, Student.first_name).all()
|
||||
assessment_repo = AssessmentRepository()
|
||||
student_repo = StudentRepository()
|
||||
grade_repo = GradeRepository()
|
||||
|
||||
assessment = assessment_repo.get_or_404(assessment_id)
|
||||
students = student_repo.find_by_class_ordered(assessment.class_group_id)
|
||||
|
||||
# Get all grading elements for this assessment
|
||||
grading_elements = []
|
||||
@@ -16,10 +21,7 @@ def assessment_grading(assessment_id):
|
||||
grading_elements.append(element)
|
||||
|
||||
# Get existing grades
|
||||
existing_grades = {}
|
||||
for grade in Grade.query.join(GradingElement).join(Exercise).filter_by(assessment_id=assessment_id).all():
|
||||
key = f"{grade.student_id}_{grade.grading_element_id}"
|
||||
existing_grades[key] = grade
|
||||
existing_grades = grade_repo.find_existing_grades_for_assessment(assessment_id)
|
||||
|
||||
# Préparer les informations d'affichage pour les scores
|
||||
scale_values = config_manager.get_competence_scale_values()
|
||||
@@ -34,7 +36,11 @@ def assessment_grading(assessment_id):
|
||||
|
||||
@bp.route('/assessments/<int:assessment_id>/grading/save', methods=['POST'])
|
||||
def save_grades(assessment_id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
assessment_repo = AssessmentRepository()
|
||||
student_repo = StudentRepository()
|
||||
grade_repo = GradeRepository()
|
||||
|
||||
assessment = assessment_repo.get_or_404(assessment_id)
|
||||
errors = []
|
||||
saved_count = 0
|
||||
|
||||
@@ -61,7 +67,7 @@ def save_grades(assessment_id):
|
||||
|
||||
# Vérifier que l'étudiant et l'élément existent avec protection
|
||||
try:
|
||||
student = Student.query.get(student_id)
|
||||
student = student_repo.find_by_id(student_id)
|
||||
grading_element = GradingElement.query.get(element_id)
|
||||
except Exception as e:
|
||||
errors.append(f'Erreur DB pour {key}: {str(e)}')
|
||||
@@ -77,10 +83,7 @@ def save_grades(assessment_id):
|
||||
|
||||
# Find or create grade avec protection
|
||||
try:
|
||||
grade = Grade.query.filter_by(
|
||||
student_id=student_id,
|
||||
grading_element_id=element_id
|
||||
).first()
|
||||
grade = grade_repo.find_by_student_and_element(student_id, element_id)
|
||||
|
||||
if value.strip(): # If value is not empty
|
||||
# Passer max_points pour la validation des notes
|
||||
@@ -130,10 +133,7 @@ def save_grades(assessment_id):
|
||||
continue
|
||||
|
||||
try:
|
||||
grade = Grade.query.filter_by(
|
||||
student_id=student_id,
|
||||
grading_element_id=element_id
|
||||
).first()
|
||||
grade = grade_repo.find_by_student_and_element(student_id, element_id)
|
||||
|
||||
# Créer une note avec commentaire uniquement si nécessaire
|
||||
if value.strip():
|
||||
@@ -152,7 +152,8 @@ def save_grades(assessment_id):
|
||||
|
||||
except Exception as e:
|
||||
# Log l'erreur mais ne pas faire planter la sauvegarde
|
||||
print(f"Erreur commentaire pour {key}: {str(e)}")
|
||||
from flask import current_app
|
||||
current_app.logger.warning(f"Erreur commentaire pour {key}: {str(e)}", exc_info=True)
|
||||
continue
|
||||
|
||||
db.session.commit()
|
||||
@@ -186,10 +187,14 @@ def save_grades(assessment_id):
|
||||
|
||||
# Log détaillé de l'erreur
|
||||
error_details = traceback.format_exc()
|
||||
print(f"=== ERREUR SAUVEGARDE ASSESSMENT {assessment_id} ===")
|
||||
print(f"Exception: {type(e).__name__}: {str(e)}")
|
||||
print(f"Traceback:\n{error_details}")
|
||||
print("=" * 50)
|
||||
from flask import current_app
|
||||
current_app.logger.error(
|
||||
f"=== ERREUR SAUVEGARDE ASSESSMENT {assessment_id} ===\n"
|
||||
f"Exception: {type(e).__name__}: {str(e)}\n"
|
||||
f"Traceback:\n{error_details}\n"
|
||||
f"{'=' * 50}",
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
error_msg = f'Erreur lors de la sauvegarde: {str(e)}'
|
||||
|
||||
@@ -208,7 +213,11 @@ def save_grades(assessment_id):
|
||||
@bp.route('/assessments/<int:assessment_id>/grading/save-single', methods=['POST'])
|
||||
def save_single_grade(assessment_id):
|
||||
"""Sauvegarde incrémentale d'une seule note"""
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
assessment_repo = AssessmentRepository()
|
||||
student_repo = StudentRepository()
|
||||
grade_repo = GradeRepository()
|
||||
|
||||
assessment = assessment_repo.get_or_404(assessment_id)
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
@@ -218,7 +227,7 @@ def save_single_grade(assessment_id):
|
||||
comment = data.get('comment', '').strip()
|
||||
|
||||
# Vérifications
|
||||
student = Student.query.get(student_id)
|
||||
student = student_repo.find_by_id(student_id)
|
||||
grading_element = GradingElement.query.get(element_id)
|
||||
|
||||
if not student or not grading_element:
|
||||
@@ -228,10 +237,7 @@ def save_single_grade(assessment_id):
|
||||
}), 404
|
||||
|
||||
# Find or create grade
|
||||
grade = Grade.query.filter_by(
|
||||
student_id=student_id,
|
||||
grading_element_id=element_id
|
||||
).first()
|
||||
grade = grade_repo.find_by_student_and_element(student_id, element_id)
|
||||
|
||||
if value:
|
||||
# Validation
|
||||
|
||||
Reference in New Issue
Block a user