feat: add concil page
This commit is contained in:
170
repositories/appreciation_repository.py
Normal file
170
repositories/appreciation_repository.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Repository pour la gestion des appréciations du conseil de classe.
|
||||
"""
|
||||
from typing import List, Optional
|
||||
from sqlalchemy.orm import joinedload
|
||||
from models import CouncilAppreciation, db
|
||||
from repositories.base_repository import BaseRepository
|
||||
|
||||
|
||||
class AppreciationRepository(BaseRepository[CouncilAppreciation]):
|
||||
"""Repository pour les appréciations du conseil de classe."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(CouncilAppreciation)
|
||||
|
||||
def find_by_class_trimester(self, class_group_id: int, trimester: int) -> List[CouncilAppreciation]:
|
||||
"""Trouve toutes les appréciations d'une classe pour un trimestre."""
|
||||
return CouncilAppreciation.query.filter_by(
|
||||
class_group_id=class_group_id,
|
||||
trimester=trimester
|
||||
).options(
|
||||
joinedload(CouncilAppreciation.student),
|
||||
joinedload(CouncilAppreciation.class_group)
|
||||
).all()
|
||||
|
||||
def find_by_student_trimester(
|
||||
self,
|
||||
student_id: int,
|
||||
class_group_id: int,
|
||||
trimester: int
|
||||
) -> Optional[CouncilAppreciation]:
|
||||
"""Trouve l'appréciation d'un élève pour un trimestre."""
|
||||
return CouncilAppreciation.query.filter_by(
|
||||
student_id=student_id,
|
||||
class_group_id=class_group_id,
|
||||
trimester=trimester
|
||||
).options(
|
||||
joinedload(CouncilAppreciation.student),
|
||||
joinedload(CouncilAppreciation.class_group)
|
||||
).first()
|
||||
|
||||
def find_by_student_all_trimesters(
|
||||
self,
|
||||
student_id: int,
|
||||
class_group_id: int
|
||||
) -> List[CouncilAppreciation]:
|
||||
"""Trouve toutes les appréciations d'un élève pour tous les trimestres."""
|
||||
return CouncilAppreciation.query.filter_by(
|
||||
student_id=student_id,
|
||||
class_group_id=class_group_id
|
||||
).options(
|
||||
joinedload(CouncilAppreciation.student),
|
||||
joinedload(CouncilAppreciation.class_group)
|
||||
).order_by(CouncilAppreciation.trimester).all()
|
||||
|
||||
def count_with_content_by_class_trimester(
|
||||
self,
|
||||
class_group_id: int,
|
||||
trimester: int
|
||||
) -> int:
|
||||
"""Compte le nombre d'appréciations avec contenu pour une classe/trimestre."""
|
||||
return CouncilAppreciation.query.filter(
|
||||
CouncilAppreciation.class_group_id == class_group_id,
|
||||
CouncilAppreciation.trimester == trimester,
|
||||
db.or_(
|
||||
CouncilAppreciation.general_appreciation.isnot(None),
|
||||
CouncilAppreciation.strengths.isnot(None),
|
||||
CouncilAppreciation.areas_for_improvement.isnot(None)
|
||||
)
|
||||
).count()
|
||||
|
||||
def get_completion_stats(self, class_group_id: int, trimester: int) -> dict:
|
||||
"""Statistiques de completion des appréciations pour une classe/trimestre."""
|
||||
from models import Student
|
||||
|
||||
# Nombre total d'élèves dans la classe
|
||||
total_students = Student.query.filter_by(class_group_id=class_group_id).count()
|
||||
|
||||
# Nombre d'appréciations existantes
|
||||
total_appreciations = CouncilAppreciation.query.filter_by(
|
||||
class_group_id=class_group_id,
|
||||
trimester=trimester
|
||||
).count()
|
||||
|
||||
# Nombre d'appréciations avec contenu
|
||||
completed_appreciations = self.count_with_content_by_class_trimester(
|
||||
class_group_id, trimester
|
||||
)
|
||||
|
||||
# Nombre d'appréciations finalisées
|
||||
finalized_appreciations = CouncilAppreciation.query.filter_by(
|
||||
class_group_id=class_group_id,
|
||||
trimester=trimester,
|
||||
status='finalized'
|
||||
).count()
|
||||
|
||||
return {
|
||||
'total_students': total_students,
|
||||
'total_appreciations': total_appreciations,
|
||||
'completed_appreciations': completed_appreciations,
|
||||
'finalized_appreciations': finalized_appreciations,
|
||||
'completion_percentage': (completed_appreciations / total_students * 100) if total_students > 0 else 0,
|
||||
'finalization_percentage': (finalized_appreciations / total_students * 100) if total_students > 0 else 0
|
||||
}
|
||||
|
||||
def create_or_update(
|
||||
self,
|
||||
student_id: int,
|
||||
class_group_id: int,
|
||||
trimester: int,
|
||||
data: dict
|
||||
) -> CouncilAppreciation:
|
||||
"""Crée ou met à jour une appréciation."""
|
||||
existing = self.find_by_student_trimester(student_id, class_group_id, trimester)
|
||||
|
||||
if existing:
|
||||
# Mise à jour
|
||||
for key, value in data.items():
|
||||
if hasattr(existing, key):
|
||||
setattr(existing, key, value)
|
||||
self.commit()
|
||||
return existing
|
||||
else:
|
||||
# Création
|
||||
appreciation_data = {
|
||||
'student_id': student_id,
|
||||
'class_group_id': class_group_id,
|
||||
'trimester': trimester,
|
||||
**data
|
||||
}
|
||||
appreciation = CouncilAppreciation(**appreciation_data)
|
||||
self.save(appreciation)
|
||||
self.commit()
|
||||
return appreciation
|
||||
|
||||
def delete_by_student_trimester(
|
||||
self,
|
||||
student_id: int,
|
||||
class_group_id: int,
|
||||
trimester: int
|
||||
) -> bool:
|
||||
"""Supprime une appréciation spécifique."""
|
||||
appreciation = self.find_by_student_trimester(student_id, class_group_id, trimester)
|
||||
if appreciation:
|
||||
self.delete(appreciation)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_students_without_appreciation(
|
||||
self,
|
||||
class_group_id: int,
|
||||
trimester: int
|
||||
) -> List:
|
||||
"""Retourne la liste des élèves sans appréciation pour un trimestre."""
|
||||
from models import Student
|
||||
|
||||
# Sous-requête pour les élèves qui ont déjà une appréciation
|
||||
students_with_appreciation = db.session.query(CouncilAppreciation.student_id).filter_by(
|
||||
class_group_id=class_group_id,
|
||||
trimester=trimester
|
||||
).subquery()
|
||||
|
||||
# Élèves sans appréciation
|
||||
students_without = Student.query.filter_by(
|
||||
class_group_id=class_group_id
|
||||
).filter(
|
||||
~Student.id.in_(students_with_appreciation)
|
||||
).order_by(Student.last_name, Student.first_name).all()
|
||||
|
||||
return students_without
|
||||
@@ -124,4 +124,60 @@ class AssessmentRepository(BaseRepository[Assessment]):
|
||||
elif status == 'not_started' and progress_status == 'not_started':
|
||||
filtered_assessments.append(assessment)
|
||||
|
||||
return filtered_assessments
|
||||
return filtered_assessments
|
||||
|
||||
def find_completed_by_class_trimester(self, class_group_id: int, trimester: int) -> List[Assessment]:
|
||||
"""Trouve les évaluations terminées d'une classe pour un trimestre."""
|
||||
assessments = Assessment.query.filter_by(
|
||||
class_group_id=class_group_id,
|
||||
trimester=trimester
|
||||
).options(
|
||||
joinedload(Assessment.class_group),
|
||||
joinedload(Assessment.exercises).joinedload(Exercise.grading_elements)
|
||||
).all()
|
||||
|
||||
# Filtrer sur progression = 100%
|
||||
completed_assessments = []
|
||||
for assessment in assessments:
|
||||
progress = assessment.grading_progress
|
||||
if progress.get('status') == 'completed':
|
||||
completed_assessments.append(assessment)
|
||||
|
||||
return completed_assessments
|
||||
|
||||
def find_by_class_trimester_with_details(self, class_group_id: int, trimester: int) -> List[Assessment]:
|
||||
"""Trouve toutes les évaluations d'une classe pour un trimestre avec détails complets."""
|
||||
return Assessment.query.filter_by(
|
||||
class_group_id=class_group_id,
|
||||
trimester=trimester
|
||||
).options(
|
||||
joinedload(Assessment.class_group),
|
||||
joinedload(Assessment.exercises).joinedload(Exercise.grading_elements)
|
||||
).order_by(Assessment.date.desc()).all()
|
||||
|
||||
def get_trimester_statistics(self, class_group_id: int, trimester: int) -> dict:
|
||||
"""Statistiques des évaluations pour une classe/trimestre."""
|
||||
assessments = self.find_by_class_trimester_with_details(class_group_id, trimester)
|
||||
|
||||
completed = 0
|
||||
in_progress = 0
|
||||
not_started = 0
|
||||
|
||||
for assessment in assessments:
|
||||
progress = assessment.grading_progress
|
||||
status = progress.get('status', 'not_started')
|
||||
|
||||
if status == 'completed':
|
||||
completed += 1
|
||||
elif status == 'in_progress':
|
||||
in_progress += 1
|
||||
else:
|
||||
not_started += 1
|
||||
|
||||
return {
|
||||
'total': len(assessments),
|
||||
'completed': completed,
|
||||
'in_progress': in_progress,
|
||||
'not_started': not_started,
|
||||
'completion_percentage': (completed / len(assessments) * 100) if assessments else 0
|
||||
}
|
||||
@@ -97,4 +97,34 @@ class GradeRepository(BaseRepository[Grade]):
|
||||
self.delete(grade)
|
||||
count += 1
|
||||
|
||||
return count
|
||||
return count
|
||||
|
||||
def find_by_student_trimester_with_elements(self, student_id: int, trimester: int) -> List[Grade]:
|
||||
"""Trouve toutes les notes d'un élève pour un trimestre avec les éléments de notation."""
|
||||
return Grade.query.join(GradingElement).join(Exercise).join(Assessment).filter(
|
||||
Grade.student_id == student_id,
|
||||
Assessment.trimester == trimester
|
||||
).options(
|
||||
joinedload(Grade.grading_element).joinedload(GradingElement.exercise).joinedload(Exercise.assessment),
|
||||
joinedload(Grade.grading_element).joinedload(GradingElement.domain)
|
||||
).all()
|
||||
|
||||
def find_by_class_trimester(self, class_group_id: int, trimester: int) -> List[Grade]:
|
||||
"""Trouve toutes les notes d'une classe pour un trimestre."""
|
||||
return Grade.query.join(Student).join(GradingElement).join(Exercise).join(Assessment).filter(
|
||||
Student.class_group_id == class_group_id,
|
||||
Assessment.trimester == trimester
|
||||
).options(
|
||||
joinedload(Grade.student),
|
||||
joinedload(Grade.grading_element).joinedload(GradingElement.exercise).joinedload(Exercise.assessment),
|
||||
joinedload(Grade.grading_element).joinedload(GradingElement.domain)
|
||||
).all()
|
||||
|
||||
def get_student_grades_by_assessment(self, student_id: int, assessment_id: int) -> List[Grade]:
|
||||
"""Récupère toutes les notes d'un élève pour une évaluation spécifique."""
|
||||
return Grade.query.join(GradingElement).join(Exercise).filter(
|
||||
Grade.student_id == student_id,
|
||||
Exercise.assessment_id == assessment_id
|
||||
).options(
|
||||
joinedload(Grade.grading_element).joinedload(GradingElement.exercise)
|
||||
).all()
|
||||
Reference in New Issue
Block a user