refactor: restructure codebase into modular architecture
- Split monolithic app.py (400+ lines) into organized modules - Extract models, forms, and commands into separate files - Implement Flask blueprints for route organization - Maintain full functionality with cleaner architecture - Update all templates to use new blueprint URLs - Enhance README with technical documentation Structure: ├── app.py (50 lines) - Flask app factory ├── models.py (62 lines) - SQLAlchemy models ├── forms.py (43 lines) - WTForms definitions ├── commands.py (74 lines) - CLI commands └── routes/ - Blueprint modules for each feature 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1
routes/__init__.py
Normal file
1
routes/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Routes package
|
||||
55
routes/assessments.py
Normal file
55
routes/assessments.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from models import db, Assessment, ClassGroup
|
||||
from forms import AssessmentForm
|
||||
|
||||
bp = Blueprint('assessments', __name__, url_prefix='/assessments')
|
||||
|
||||
@bp.route('/')
|
||||
def list():
|
||||
assessments = Assessment.query.join(ClassGroup).order_by(Assessment.date.desc()).all()
|
||||
return render_template('assessments.html', assessments=assessments)
|
||||
|
||||
@bp.route('/new', methods=['GET', 'POST'])
|
||||
def new():
|
||||
form = AssessmentForm()
|
||||
if form.validate_on_submit():
|
||||
assessment = Assessment(
|
||||
title=form.title.data,
|
||||
description=form.description.data,
|
||||
date=form.date.data,
|
||||
class_group_id=form.class_group_id.data,
|
||||
coefficient=form.coefficient.data
|
||||
)
|
||||
db.session.add(assessment)
|
||||
db.session.commit()
|
||||
flash('Évaluation créée avec succès !', 'success')
|
||||
return redirect(url_for('assessments.detail', id=assessment.id))
|
||||
return render_template('assessment_form.html', form=form, title='Nouvelle évaluation')
|
||||
|
||||
@bp.route('/<int:id>')
|
||||
def detail(id):
|
||||
assessment = Assessment.query.get_or_404(id)
|
||||
return render_template('assessment_detail.html', assessment=assessment)
|
||||
|
||||
@bp.route('/<int:id>/edit', methods=['GET', 'POST'])
|
||||
def edit(id):
|
||||
assessment = Assessment.query.get_or_404(id)
|
||||
form = AssessmentForm(obj=assessment)
|
||||
if form.validate_on_submit():
|
||||
assessment.title = form.title.data
|
||||
assessment.description = form.description.data
|
||||
assessment.date = form.date.data
|
||||
assessment.class_group_id = form.class_group_id.data
|
||||
assessment.coefficient = form.coefficient.data
|
||||
db.session.commit()
|
||||
flash('Évaluation modifiée avec succès !', 'success')
|
||||
return redirect(url_for('assessments.detail', id=assessment.id))
|
||||
return render_template('assessment_form.html', form=form, title='Modifier l\'évaluation', assessment=assessment)
|
||||
|
||||
@bp.route('/<int:id>/delete', methods=['POST'])
|
||||
def delete(id):
|
||||
assessment = Assessment.query.get_or_404(id)
|
||||
db.session.delete(assessment)
|
||||
db.session.commit()
|
||||
flash('Évaluation supprimée avec succès !', 'success')
|
||||
return redirect(url_for('assessments.list'))
|
||||
113
routes/exercises.py
Normal file
113
routes/exercises.py
Normal file
@@ -0,0 +1,113 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from models import db, Assessment, Exercise, GradingElement
|
||||
from forms import ExerciseForm, GradingElementForm
|
||||
|
||||
bp = Blueprint('exercises', __name__)
|
||||
|
||||
# Exercise routes
|
||||
@bp.route('/assessments/<int:assessment_id>/exercises/new', methods=['GET', 'POST'])
|
||||
def new(assessment_id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
form = ExerciseForm()
|
||||
|
||||
# Set default order to next available
|
||||
if form.order.data == 1: # Only if it's the default value
|
||||
max_order = db.session.query(db.func.max(Exercise.order)).filter_by(assessment_id=assessment_id).scalar()
|
||||
form.order.data = (max_order or 0) + 1
|
||||
|
||||
if form.validate_on_submit():
|
||||
exercise = Exercise(
|
||||
assessment_id=assessment_id,
|
||||
title=form.title.data,
|
||||
description=form.description.data,
|
||||
order=form.order.data
|
||||
)
|
||||
db.session.add(exercise)
|
||||
db.session.commit()
|
||||
flash('Exercice créé avec succès !', 'success')
|
||||
return redirect(url_for('exercises.detail', assessment_id=assessment_id, id=exercise.id))
|
||||
|
||||
return render_template('exercise_form.html', form=form, assessment=assessment, title='Nouvel exercice')
|
||||
|
||||
@bp.route('/assessments/<int:assessment_id>/exercises/<int:id>')
|
||||
def detail(assessment_id, id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
exercise = Exercise.query.filter_by(id=id, assessment_id=assessment_id).first_or_404()
|
||||
return render_template('exercise_detail.html', assessment=assessment, exercise=exercise)
|
||||
|
||||
@bp.route('/assessments/<int:assessment_id>/exercises/<int:id>/edit', methods=['GET', 'POST'])
|
||||
def edit(assessment_id, id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
exercise = Exercise.query.filter_by(id=id, assessment_id=assessment_id).first_or_404()
|
||||
form = ExerciseForm(obj=exercise)
|
||||
|
||||
if form.validate_on_submit():
|
||||
exercise.title = form.title.data
|
||||
exercise.description = form.description.data
|
||||
exercise.order = form.order.data
|
||||
db.session.commit()
|
||||
flash('Exercice modifié avec succès !', 'success')
|
||||
return redirect(url_for('exercises.detail', assessment_id=assessment_id, id=exercise.id))
|
||||
|
||||
return render_template('exercise_form.html', form=form, assessment=assessment, exercise=exercise, title='Modifier l\'exercice')
|
||||
|
||||
@bp.route('/assessments/<int:assessment_id>/exercises/<int:id>/delete', methods=['POST'])
|
||||
def delete(assessment_id, id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
exercise = Exercise.query.filter_by(id=id, assessment_id=assessment_id).first_or_404()
|
||||
db.session.delete(exercise)
|
||||
db.session.commit()
|
||||
flash('Exercice supprimé avec succès !', 'success')
|
||||
return redirect(url_for('assessments.detail', id=assessment_id))
|
||||
|
||||
# GradingElement routes
|
||||
@bp.route('/assessments/<int:assessment_id>/exercises/<int:exercise_id>/elements/new', methods=['GET', 'POST'])
|
||||
def new_grading_element(assessment_id, exercise_id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
exercise = Exercise.query.filter_by(id=exercise_id, assessment_id=assessment_id).first_or_404()
|
||||
form = GradingElementForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
element = GradingElement(
|
||||
exercise_id=exercise_id,
|
||||
label=form.label.data,
|
||||
description=form.description.data,
|
||||
skill=form.skill.data,
|
||||
max_points=form.max_points.data,
|
||||
grading_type=form.grading_type.data
|
||||
)
|
||||
db.session.add(element)
|
||||
db.session.commit()
|
||||
flash('Élément de notation créé avec succès !', 'success')
|
||||
return redirect(url_for('exercises.detail', assessment_id=assessment_id, id=exercise_id))
|
||||
|
||||
return render_template('grading_element_form.html', form=form, assessment=assessment, exercise=exercise, title='Nouvel élément de notation')
|
||||
|
||||
@bp.route('/assessments/<int:assessment_id>/exercises/<int:exercise_id>/elements/<int:id>/edit', methods=['GET', 'POST'])
|
||||
def edit_grading_element(assessment_id, exercise_id, id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
exercise = Exercise.query.filter_by(id=exercise_id, assessment_id=assessment_id).first_or_404()
|
||||
element = GradingElement.query.filter_by(id=id, exercise_id=exercise_id).first_or_404()
|
||||
form = GradingElementForm(obj=element)
|
||||
|
||||
if form.validate_on_submit():
|
||||
element.label = form.label.data
|
||||
element.description = form.description.data
|
||||
element.skill = form.skill.data
|
||||
element.max_points = form.max_points.data
|
||||
element.grading_type = form.grading_type.data
|
||||
db.session.commit()
|
||||
flash('Élément de notation modifié avec succès !', 'success')
|
||||
return redirect(url_for('exercises.detail', assessment_id=assessment_id, id=exercise_id))
|
||||
|
||||
return render_template('grading_element_form.html', form=form, assessment=assessment, exercise=exercise, element=element, title='Modifier l\'élément de notation')
|
||||
|
||||
@bp.route('/assessments/<int:assessment_id>/exercises/<int:exercise_id>/elements/<int:id>/delete', methods=['POST'])
|
||||
def delete_grading_element(assessment_id, exercise_id, id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
exercise = Exercise.query.filter_by(id=exercise_id, assessment_id=assessment_id).first_or_404()
|
||||
element = GradingElement.query.filter_by(id=id, exercise_id=exercise_id).first_or_404()
|
||||
db.session.delete(element)
|
||||
db.session.commit()
|
||||
flash('Élément de notation supprimé avec succès !', 'success')
|
||||
return redirect(url_for('exercises.detail', assessment_id=assessment_id, id=exercise_id))
|
||||
78
routes/grading.py
Normal file
78
routes/grading.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from models import db, Assessment, Student, Grade, GradingElement, Exercise
|
||||
|
||||
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()
|
||||
|
||||
# Get all grading elements for this assessment
|
||||
grading_elements = []
|
||||
for exercise in assessment.exercises:
|
||||
for element in exercise.grading_elements:
|
||||
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
|
||||
|
||||
return render_template('assessment_grading.html',
|
||||
assessment=assessment,
|
||||
students=students,
|
||||
grading_elements=grading_elements,
|
||||
existing_grades=existing_grades)
|
||||
|
||||
@bp.route('/assessments/<int:assessment_id>/grading/save', methods=['POST'])
|
||||
def save_grades(assessment_id):
|
||||
assessment = Assessment.query.get_or_404(assessment_id)
|
||||
|
||||
for key, value in request.form.items():
|
||||
if key.startswith('grade_'):
|
||||
# Parse key: grade_<student_id>_<element_id>
|
||||
parts = key.split('_')
|
||||
if len(parts) == 3:
|
||||
student_id = int(parts[1])
|
||||
element_id = int(parts[2])
|
||||
|
||||
# Find or create grade
|
||||
grade = Grade.query.filter_by(
|
||||
student_id=student_id,
|
||||
grading_element_id=element_id
|
||||
).first()
|
||||
|
||||
if value.strip(): # If value is not empty
|
||||
if not grade:
|
||||
grade = Grade(
|
||||
student_id=student_id,
|
||||
grading_element_id=element_id,
|
||||
value=value
|
||||
)
|
||||
db.session.add(grade)
|
||||
else:
|
||||
grade.value = value
|
||||
elif grade: # If value is empty but grade exists, delete it
|
||||
db.session.delete(grade)
|
||||
|
||||
# Handle comments
|
||||
for key, value in request.form.items():
|
||||
if key.startswith('comment_'):
|
||||
parts = key.split('_')
|
||||
if len(parts) == 3:
|
||||
student_id = int(parts[1])
|
||||
element_id = int(parts[2])
|
||||
|
||||
grade = Grade.query.filter_by(
|
||||
student_id=student_id,
|
||||
grading_element_id=element_id
|
||||
).first()
|
||||
|
||||
if grade:
|
||||
grade.comment = value.strip() if value.strip() else None
|
||||
|
||||
db.session.commit()
|
||||
flash('Notes sauvegardées avec succès !', 'success')
|
||||
return redirect(url_for('grading.assessment_grading', assessment_id=assessment_id))
|
||||
Reference in New Issue
Block a user