refactor: extract duplicated patterns into shared helpers

Backend: create api/helpers.py with eligible_enrollment_filter,
count_eligible_students, get_active_enrollment, ensure_unique_name,
upsert_app_configs, and build_heatmap. Add full_name properties to
Student model. Apply across all route files (-481/+184 lines).

Frontend: create stores/helpers.js with withLoading composable,
apply to assessments and classes Pinia stores.

96/96 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 14:05:10 +01:00
parent b1b7d12a9f
commit a0ab7224e1
10 changed files with 402 additions and 481 deletions

View File

@@ -10,6 +10,7 @@ from sqlalchemy import select, func
from sqlalchemy.orm import selectinload
from api.dependencies import AsyncSessionDep
from api.helpers import get_active_enrollment
from infrastructure.database.models import (
Student,
StudentEnrollment,
@@ -62,12 +63,8 @@ async def get_students(
students_list = []
for student in students:
# Trouver l'inscription active
current_enrollment = None
for enrollment in student.enrollments:
if enrollment.departure_date is None:
current_enrollment = enrollment
break
current_enrollment = get_active_enrollment(student)
# Filtrer par classe si demandé
if class_id and (not current_enrollment or current_enrollment.class_group_id != class_id):
continue
@@ -78,7 +75,7 @@ async def get_students(
last_name=student.last_name,
first_name=student.first_name,
email=student.email,
full_name=f"{student.first_name} {student.last_name}",
full_name=student.full_name,
current_class_id=current_enrollment.class_group_id if current_enrollment else None,
current_class_name=current_enrollment.class_group.name if current_enrollment else None
)
@@ -113,13 +110,10 @@ async def get_student(
raise HTTPException(status_code=404, detail="Étudiant non trouvé")
# Trouver l'inscription active
current_enrollment = None
current_enrollment = get_active_enrollment(student)
enrollments_list = []
for enrollment in student.enrollments:
if enrollment.departure_date is None:
current_enrollment = enrollment
enrollments_list.append(
EnrollmentRead(
id=enrollment.id,
@@ -144,7 +138,7 @@ async def get_student(
last_name=student.last_name,
first_name=student.first_name,
email=student.email,
full_name=f"{student.first_name} {student.last_name}",
full_name=student.full_name,
current_class_id=current_enrollment.class_group_id if current_enrollment else None,
current_class_name=current_enrollment.class_group.name if current_enrollment else None,
enrollments=enrollments_list
@@ -210,7 +204,7 @@ async def create_student(
last_name=new_student.last_name,
first_name=new_student.first_name,
email=new_student.email,
full_name=f"{new_student.first_name} {new_student.last_name}",
full_name=new_student.full_name,
current_class_id=current_class_id,
current_class_name=current_class_name
)
@@ -264,18 +258,14 @@ async def update_student(
await session.refresh(student)
# Trouver la classe actuelle
current_enrollment = None
for enrollment in student.enrollments:
if enrollment.departure_date is None:
current_enrollment = enrollment
break
current_enrollment = get_active_enrollment(student)
return StudentWithClass(
id=student.id,
last_name=student.last_name,
first_name=student.first_name,
email=student.email,
full_name=f"{student.first_name} {student.last_name}",
full_name=student.full_name,
current_class_id=current_enrollment.class_group_id if current_enrollment else None,
current_class_name=current_enrollment.class_group.name if current_enrollment else None
)
@@ -325,18 +315,14 @@ async def update_student_email(
await session.refresh(student)
# Trouver la classe actuelle
current_enrollment = None
for enrollment in student.enrollments:
if enrollment.departure_date is None:
current_enrollment = enrollment
break
current_enrollment = get_active_enrollment(student)
return StudentWithClass(
id=student.id,
last_name=student.last_name,
first_name=student.first_name,
email=student.email,
full_name=f"{student.first_name} {student.last_name}",
full_name=student.full_name,
current_class_id=current_enrollment.class_group_id if current_enrollment else None,
current_class_name=current_enrollment.class_group.name if current_enrollment else None
)
@@ -403,7 +389,7 @@ async def enroll_student(
if active_result.scalar_one_or_none():
raise HTTPException(
status_code=400,
detail=f"L'élève {student.first_name} {student.last_name} est déjà inscrit dans une classe"
detail=f"L'élève {student.full_name} est déjà inscrit dans une classe"
)
else:
# Nouvel élève
@@ -447,9 +433,9 @@ async def enroll_student(
return EnrollmentResponse(
enrollment_id=enrollment.id,
student_id=student.id,
student_name=f"{student.first_name} {student.last_name}",
student_name=student.full_name,
class_name=class_group.name,
message=f"Élève {student.first_name} {student.last_name} inscrit en {class_group.name}",
message=f"Élève {student.full_name} inscrit en {class_group.name}",
is_new_student=is_new_student
)
@@ -493,9 +479,9 @@ async def transfer_student(
if not old_enrollment:
raise HTTPException(
status_code=400,
detail=f"Aucune inscription active trouvée pour l'élève {student.first_name} {student.last_name}"
detail=f"Aucune inscription active trouvée pour l'élève {student.full_name}"
)
old_class_name = old_enrollment.class_group.name
# Terminer l'ancienne inscription
@@ -516,10 +502,10 @@ async def transfer_student(
return TransferResponse(
old_enrollment_id=old_enrollment.id,
new_enrollment_id=new_enrollment.id,
student_name=f"{student.first_name} {student.last_name}",
student_name=student.full_name,
old_class_name=old_class_name,
new_class_name=new_class.name,
message=f"Élève {student.first_name} {student.last_name} transféré de {old_class_name} vers {new_class.name}"
message=f"Élève {student.full_name} transféré de {old_class_name} vers {new_class.name}"
)
@@ -554,9 +540,9 @@ async def record_departure(
if not enrollment:
raise HTTPException(
status_code=400,
detail=f"Aucune inscription active trouvée pour l'élève {student.first_name} {student.last_name}"
detail=f"Aucune inscription active trouvée pour l'élève {student.full_name}"
)
class_name = enrollment.class_group.name
# Enregistrer le départ
@@ -567,7 +553,7 @@ async def record_departure(
return DepartureResponse(
enrollment_id=enrollment.id,
student_name=f"{student.first_name} {student.last_name}",
student_name=student.full_name,
class_name=class_name,
message=f"Départ de {student.first_name} {student.last_name} de {class_name} enregistré"
message=f"Départ de {student.full_name} de {class_name} enregistré"
)