clean: clean python code

This commit is contained in:
2025-08-07 14:16:15 +02:00
parent db96807422
commit 8cee665f2b
18 changed files with 316 additions and 936 deletions

View File

@@ -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()

View File

@@ -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:

View File

@@ -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