refact: use repositories patterns for classes

This commit is contained in:
2025-08-08 06:04:55 +02:00
parent 35bf575125
commit 5c25723e4f
13 changed files with 2325 additions and 54 deletions

View File

@@ -0,0 +1,210 @@
from typing import List, Optional, Dict, Tuple
from sqlalchemy.orm import joinedload
from sqlalchemy import and_
from models import ClassGroup, Student, Assessment
from .base_repository import BaseRepository
class ClassRepository(BaseRepository[ClassGroup]):
"""Repository pour les classes (ClassGroup)."""
def __init__(self):
super().__init__(ClassGroup)
def get_or_404(self, id: int) -> ClassGroup:
"""
Récupère une classe ou lève une erreur 404.
Args:
id: Identifiant de la classe
Returns:
ClassGroup: La classe trouvée
Raises:
404: Si la classe n'existe pas
"""
return ClassGroup.query.get_or_404(id)
def find_by_name(self, name: str) -> Optional[ClassGroup]:
"""
Trouve une classe par son nom.
Args:
name: Nom de la classe à rechercher
Returns:
Optional[ClassGroup]: La classe trouvée ou None
"""
return ClassGroup.query.filter_by(name=name).first()
def exists_by_name(self, name: str, exclude_id: Optional[int] = None) -> bool:
"""
Vérifie si une classe avec ce nom existe déjà.
Args:
name: Nom à vérifier
exclude_id: ID de classe à exclure de la recherche (pour la modification)
Returns:
bool: True si une classe avec ce nom existe
"""
query = ClassGroup.query.filter_by(name=name)
if exclude_id is not None:
query = query.filter(ClassGroup.id != exclude_id)
return query.first() is not None
def find_all_ordered(self, order_by: str = 'year_name') -> List[ClassGroup]:
"""
Trouve toutes les classes triées selon le critère spécifié.
Args:
order_by: Critère de tri ('year_name', 'name', 'year')
Returns:
List[ClassGroup]: Liste des classes triées
"""
query = ClassGroup.query
if order_by == 'year_name':
query = query.order_by(ClassGroup.year, ClassGroup.name)
elif order_by == 'name':
query = query.order_by(ClassGroup.name)
elif order_by == 'year':
query = query.order_by(ClassGroup.year)
else:
# Défaut : tri par nom
query = query.order_by(ClassGroup.name)
return query.all()
def count_all(self) -> int:
"""
Compte le nombre total de classes.
Returns:
int: Nombre de classes
"""
return ClassGroup.query.count()
def can_be_deleted(self, id: int) -> Tuple[bool, Dict[str, int]]:
"""
Vérifie si une classe peut être supprimée et retourne les dépendances.
Args:
id: Identifiant de la classe
Returns:
Tuple[bool, Dict[str, int]]: (peut_être_supprimée, statistiques_dépendances)
"""
students_count = Student.query.filter_by(class_group_id=id).count()
assessments_count = Assessment.query.filter_by(class_group_id=id).count()
dependencies = {
'students': students_count,
'assessments': assessments_count
}
can_delete = students_count == 0 and assessments_count == 0
return can_delete, dependencies
def find_with_students_ordered(self, id: int) -> Optional[ClassGroup]:
"""
Trouve une classe avec ses étudiants triés par nom.
Args:
id: Identifiant de la classe
Returns:
Optional[ClassGroup]: La classe avec étudiants triés ou None
"""
class_group = ClassGroup.query.get(id)
if not class_group:
return None
# Charger les étudiants triés
students = Student.query.filter_by(class_group_id=id).order_by(
Student.last_name, Student.first_name
).all()
# Assigner les étudiants triés à la classe
class_group._students_ordered = students
return class_group
def find_with_recent_assessments(self, id: int, limit: int = 5) -> Optional[ClassGroup]:
"""
Trouve une classe avec ses évaluations récentes.
Args:
id: Identifiant de la classe
limit: Nombre maximum d'évaluations à récupérer
Returns:
Optional[ClassGroup]: La classe avec évaluations récentes ou None
"""
class_group = ClassGroup.query.get(id)
if not class_group:
return None
# Charger les évaluations récentes
recent_assessments = Assessment.query.filter_by(class_group_id=id).order_by(
Assessment.date.desc()
).limit(limit).all()
# Assigner les évaluations récentes à la classe
class_group._recent_assessments = recent_assessments
return class_group
def find_with_full_details(self, id: int) -> Optional[ClassGroup]:
"""
Trouve une classe avec tous ses détails (étudiants et évaluations).
Args:
id: Identifiant de la classe
Returns:
Optional[ClassGroup]: La classe avec tous ses détails ou None
"""
return ClassGroup.query.options(
joinedload(ClassGroup.students),
joinedload(ClassGroup.assessments)
).filter_by(id=id).first()
def get_students_count(self, id: int) -> int:
"""
Compte le nombre d'étudiants dans une classe.
Args:
id: Identifiant de la classe
Returns:
int: Nombre d'étudiants
"""
return Student.query.filter_by(class_group_id=id).count()
def get_assessments_count(self, id: int) -> int:
"""
Compte le nombre d'évaluations dans une classe.
Args:
id: Identifiant de la classe
Returns:
int: Nombre d'évaluations
"""
return Assessment.query.filter_by(class_group_id=id).count()
def find_for_form_choices(self) -> List[ClassGroup]:
"""
Trouve toutes les classes pour les choix de formulaires.
Optimisé pour n'inclure que les champs nécessaires.
Returns:
List[ClassGroup]: Liste des classes triées par nom
"""
return ClassGroup.query.order_by(ClassGroup.name).all()