feat(class): improve class/id/student
All checks were successful
Build and Publish Docker Images / Build Backend Image (push) Successful in 3m1s
Build and Publish Docker Images / Build Frontend Image (push) Successful in 3m0s
Build and Publish Docker Images / Build Summary (push) Successful in 3s

This commit is contained in:
2025-12-03 06:32:16 +01:00
parent ab86bbb2e1
commit 08c8ee4931
6 changed files with 656 additions and 20 deletions

View File

@@ -41,7 +41,7 @@ from schemas.class_group import (
)
from domain.services.grading_calculator import GradingCalculator
from domain.services.class_statistics_service import ClassStatisticsService
from schemas.student import StudentWithClass, StudentList
from schemas.student import StudentWithClass, StudentList, StudentWithEnrollmentInfo, StudentEnrollmentList
from schemas.csv_import import (
CSVImportResponse,
ImportedStudentInfo,
@@ -145,7 +145,7 @@ async def get_class(
)
@router.get("/{class_id}/students", response_model=StudentList)
@router.get("/{class_id}/students", response_model=StudentEnrollmentList)
async def get_class_students(
class_id: int,
session: AsyncSessionDep,
@@ -153,7 +153,7 @@ async def get_class_students(
at_date: Optional[str] = Query(None, description="Filtrer les élèves inscrits à cette date (YYYY-MM-DD)"),
):
"""
Récupère la liste des étudiants d'une classe.
Récupère la liste des étudiants d'une classe avec leurs informations d'inscription.
Si at_date est fourni, retourne uniquement les élèves qui étaient inscrits à cette date.
"""
@@ -194,22 +194,29 @@ async def get_class_students(
students = []
for enrollment in enrollments:
student = enrollment.student
is_active = enrollment.departure_date is None
students.append(
StudentWithClass(
StudentWithEnrollmentInfo(
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}",
current_class_id=class_id if enrollment.departure_date is None else None,
current_class_name=cls.name if enrollment.departure_date is None else None
current_class_id=class_id if is_active else None,
current_class_name=cls.name if is_active else None,
enrollment_id=enrollment.id,
enrollment_date=enrollment.enrollment_date,
departure_date=enrollment.departure_date,
enrollment_reason=enrollment.enrollment_reason,
departure_reason=enrollment.departure_reason,
is_active=is_active
)
)
# Trier par nom de famille puis prénom
students.sort(key=lambda s: (s.last_name.lower(), s.first_name.lower()))
return StudentList(
return StudentEnrollmentList(
students=students,
total=len(students)
)

View File

@@ -281,6 +281,67 @@ async def update_student(
)
@router.patch("/{student_id}/email", response_model=StudentWithClass)
async def update_student_email(
student_id: int,
email: str,
session: AsyncSessionDep,
):
"""
Modifie rapidement l'email d'un étudiant.
Endpoint optimisé pour l'édition inline.
"""
# Récupérer l'étudiant
query = (
select(Student)
.options(
selectinload(Student.enrollments).selectinload(StudentEnrollment.class_group)
)
.where(Student.id == student_id)
)
result = await session.execute(query)
student = result.scalar_one_or_none()
if not student:
raise HTTPException(status_code=404, detail="Étudiant non trouvé")
# Vérifier l'unicité du nouvel email si fourni
if email and email != student.email:
existing_query = select(Student).where(
Student.email == email,
Student.id != student_id
)
existing_result = await session.execute(existing_query)
if existing_result.scalar_one_or_none():
raise HTTPException(
status_code=400,
detail=f"Un autre élève avec l'email '{email}' existe déjà"
)
# Mettre à jour l'email (permet de le vider avec chaîne vide)
student.email = email if email else None
await session.commit()
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
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}",
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
)
@router.delete("/{student_id}", status_code=204)
async def delete_student(
student_id: int,