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()