- 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>
78 lines
3.2 KiB
Python
78 lines
3.2 KiB
Python
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)) |