""" Implémentations concrètes des providers pour l'injection de dépendances. Ce module fournit les adaptateurs concrets qui implémentent les interfaces définies dans assessment_services.py, résolvant ainsi les imports circulaires. """ from typing import Dict, Any, List from sqlalchemy.orm import joinedload from sqlalchemy import func from models import db, Grade, GradingElement, Exercise class ConfigManagerProvider: """ Implémentation concrète du ConfigProvider utilisant app_config. Résout les imports circulaires en encapsulant l'accès à la configuration. """ def __init__(self): # Import paresseux pour éviter les dépendances circulaires self._config_manager = None @property def config_manager(self): """Accès paresseux au config_manager.""" if self._config_manager is None: from app_config import config_manager self._config_manager = config_manager return self._config_manager def is_special_value(self, value: str) -> bool: """Vérifie si une valeur est spéciale.""" return self.config_manager.is_special_value(value) def get_special_values(self) -> Dict[str, Dict[str, Any]]: """Retourne la configuration des valeurs spéciales.""" return self.config_manager.get_special_values() class SQLAlchemyDatabaseProvider: """ Implémentation concrète du DatabaseProvider utilisant SQLAlchemy. Fournit des requêtes optimisées pour éviter les problèmes N+1. """ def get_grades_for_assessment(self, assessment_id: int) -> List[Dict[str, Any]]: """ Récupère toutes les notes d'une évaluation en une seule requête optimisée. Résout le problème N+1 identifié dans calculate_student_scores. """ query = ( db.session.query( Grade.student_id, Grade.grading_element_id, Grade.value, GradingElement.grading_type, GradingElement.max_points ) .join(GradingElement) .join(Exercise) .filter(Exercise.assessment_id == assessment_id) .filter(Grade.value.isnot(None)) .filter(Grade.value != '') ) return [ { 'student_id': row.student_id, 'grading_element_id': row.grading_element_id, 'value': row.value, 'grading_type': row.grading_type, 'max_points': row.max_points } for row in query.all() ] def get_grading_elements_with_students(self, assessment_id: int) -> List[Dict[str, Any]]: """ Récupère les éléments de notation avec le nombre de notes complétées. Résout le problème N+1 identifié dans grading_progress. """ # Sous-requête pour compter les grades complétés par élément grades_subquery = ( db.session.query( Grade.grading_element_id, func.count(Grade.id).label('completed_count') ) .filter(Grade.value.isnot(None)) .filter(Grade.value != '') .group_by(Grade.grading_element_id) .subquery() ) # Requête principale avec jointure query = ( db.session.query( GradingElement.id, GradingElement.label, func.coalesce(grades_subquery.c.completed_count, 0).label('completed_grades_count') ) .join(Exercise) .outerjoin(grades_subquery, GradingElement.id == grades_subquery.c.grading_element_id) .filter(Exercise.assessment_id == assessment_id) ) return [ { 'element_id': row.id, 'element_label': row.label, 'completed_grades_count': row.completed_grades_count } for row in query.all() ] # =================== FACTORY pour la création des services =================== class AssessmentServicesFactory: """ Factory pour créer l'ensemble des services avec injection de dépendances. Centralise la création et la configuration des services. """ @classmethod def create_facade(cls) -> 'AssessmentServicesFacade': """ Crée une facade complète avec toutes les dépendances injectées. Point d'entrée principal pour obtenir les services. """ from services.assessment_services import AssessmentServicesFacade config_provider = ConfigManagerProvider() db_provider = SQLAlchemyDatabaseProvider() return AssessmentServicesFacade( config_provider=config_provider, db_provider=db_provider ) @classmethod def create_with_custom_providers(cls, config_provider=None, db_provider=None) -> 'AssessmentServicesFacade': """ Crée une facade avec des providers personnalisés. Utile pour les tests avec des mocks. """ from services.assessment_services import AssessmentServicesFacade config_provider = config_provider or ConfigManagerProvider() db_provider = db_provider or SQLAlchemyDatabaseProvider() return AssessmentServicesFacade( config_provider=config_provider, db_provider=db_provider ) @classmethod def create_class_services_facade(cls) -> 'ClassServicesFacade': """ Crée une facade pour les services de classe avec toutes les dépendances injectées. Point d'entrée pour obtenir les services ClassGroup. """ from services.assessment_services import ClassServicesFacade db_provider = SQLAlchemyDatabaseProvider() return ClassServicesFacade(db_provider=db_provider)