feat: Ajout d'un système d'historisation et d'export de données en CSV [N3WTS-5]

This commit is contained in:
N3WT DE COMPET
2026-04-04 13:51:43 +02:00
parent 2579af9b8b
commit f091fa0432
18 changed files with 796 additions and 134 deletions

View File

@ -21,6 +21,7 @@ class Speciality(models.Model):
name = models.CharField(max_length=100)
updated_date = models.DateTimeField(auto_now=True)
color_code = models.CharField(max_length=7, default='#FFFFFF')
school_year = models.CharField(max_length=9, blank=True)
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='specialities')
def __str__(self):
@ -31,6 +32,7 @@ class Teacher(models.Model):
first_name = models.CharField(max_length=100)
specialities = models.ManyToManyField(Speciality, blank=True)
profile_role = models.OneToOneField('Auth.ProfileRole', on_delete=models.CASCADE, related_name='teacher_profile', null=True, blank=True)
school_year = models.CharField(max_length=9, blank=True)
updated_date = models.DateTimeField(auto_now=True)
def __str__(self):
@ -48,6 +50,7 @@ class SchoolClass(models.Model):
number_of_students = models.PositiveIntegerField(null=True, blank=True)
teaching_language = models.CharField(max_length=255, blank=True)
school_year = models.CharField(max_length=9, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True)
updated_date = models.DateTimeField(auto_now=True)
teachers = models.ManyToManyField(Teacher, blank=True)
levels = models.ManyToManyField('Common.Level', blank=True, related_name='school_classes')

View File

@ -13,6 +13,7 @@ from .views import (
EstablishmentCompetencyListCreateView, EstablishmentCompetencyDetailView,
EvaluationListCreateView, EvaluationDetailView,
StudentEvaluationListView, StudentEvaluationBulkUpdateView, StudentEvaluationDetailView,
SchoolYearsListView,
)
urlpatterns = [
@ -54,4 +55,7 @@ urlpatterns = [
re_path(r'^studentEvaluations$', StudentEvaluationListView.as_view(), name="student_evaluation_list"),
re_path(r'^studentEvaluations/bulk$', StudentEvaluationBulkUpdateView.as_view(), name="student_evaluation_bulk"),
re_path(r'^studentEvaluations/(?P<id>[0-9]+)$', StudentEvaluationDetailView.as_view(), name="student_evaluation_detail"),
# History / School Years
re_path(r'^schoolYears$', SchoolYearsListView.as_view(), name="school_years_list"),
]

View File

@ -38,12 +38,34 @@ from N3wtSchool.bdd import delete_object, getAllObjects, getObject
from django.db.models import Q
from collections import defaultdict
from Subscriptions.models import Student, StudentCompetency, StudentEvaluation
from Subscriptions.util import getCurrentSchoolYear
from Subscriptions.util import getCurrentSchoolYear, getNextSchoolYear, getHistoricalYears
import logging
from N3wtSchool.mailManager import sendRegisterForm, sendRegisterTeacher
logger = logging.getLogger(__name__)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class SchoolYearsListView(APIView):
"""
Liste les années scolaires disponibles pour l'historique.
Retourne l'année en cours, la suivante, et les années historiques.
"""
permission_classes = [IsAuthenticated]
def get(self, request):
current_year = getCurrentSchoolYear()
next_year = getNextSchoolYear()
historical_years = getHistoricalYears(5)
return JsonResponse({
'current_year': current_year,
'next_year': next_year,
'historical_years': historical_years,
'all_years': [next_year, current_year] + historical_years
}, safe=False)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class SpecialityListCreateView(APIView):
@ -186,12 +208,33 @@ class SchoolClassListCreateView(APIView):
def get(self, request):
establishment_id = request.GET.get('establishment_id', None)
school_year = request.GET.get('school_year', None)
year_filter = request.GET.get('year_filter', None) # 'current_year', 'next_year', 'historical'
if establishment_id is None:
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
school_classes_list = getAllObjects(SchoolClass)
if school_classes_list:
school_classes_list = school_classes_list.filter(establishment=establishment_id).distinct()
school_classes_list = school_classes_list.filter(establishment=establishment_id)
# Filtrage par année scolaire
if school_year:
school_classes_list = school_classes_list.filter(school_year=school_year)
elif year_filter:
current_year = getCurrentSchoolYear()
next_year = getNextSchoolYear()
historical_years = getHistoricalYears(5)
if year_filter == 'current_year':
school_classes_list = school_classes_list.filter(school_year=current_year)
elif year_filter == 'next_year':
school_classes_list = school_classes_list.filter(school_year=next_year)
elif year_filter == 'historical':
school_classes_list = school_classes_list.filter(school_year__in=historical_years)
school_classes_list = school_classes_list.distinct()
classes_serializer = SchoolClassSerializer(school_classes_list, many=True)
return JsonResponse(classes_serializer.data, safe=False)