mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-05 20:51:26 +00:00
feat: Ajout d'un système d'historisation et d'export de données en CSV [N3WTS-5]
This commit is contained in:
@ -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')
|
||||
|
||||
@ -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"),
|
||||
]
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -130,6 +130,10 @@ class Student(models.Model):
|
||||
# One-to-Many Relationship
|
||||
associated_class = models.ForeignKey('School.SchoolClass', on_delete=models.SET_NULL, null=True, blank=True, related_name='students')
|
||||
|
||||
# Audit fields
|
||||
created_at = models.DateTimeField(auto_now_add=True, null=True)
|
||||
updated_at = models.DateTimeField(auto_now=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.last_name + "_" + self.first_name
|
||||
|
||||
@ -252,6 +256,7 @@ class RegistrationForm(models.Model):
|
||||
# One-to-One Relationship
|
||||
student = models.OneToOneField(Student, on_delete=models.CASCADE, primary_key=True)
|
||||
status = models.IntegerField(choices=RegistrationFormStatus, default=RegistrationFormStatus.RF_IDLE)
|
||||
created_at = models.DateTimeField(auto_now_add=True, null=True)
|
||||
last_update = models.DateTimeField(auto_now=True)
|
||||
school_year = models.CharField(max_length=9, default="", blank=True)
|
||||
notes = models.CharField(max_length=200, blank=True)
|
||||
@ -578,6 +583,8 @@ class StudentCompetency(models.Model):
|
||||
default="",
|
||||
blank=True
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True, null=True)
|
||||
updated_at = models.DateTimeField(auto_now=True, null=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ('student', 'establishment_competency', 'period')
|
||||
|
||||
@ -54,6 +54,12 @@ class StudentListView(APIView):
|
||||
description="ID de l'établissement",
|
||||
type=openapi.TYPE_INTEGER,
|
||||
required=True
|
||||
),
|
||||
openapi.Parameter(
|
||||
'school_year', openapi.IN_QUERY,
|
||||
description="Année scolaire (ex: 2025-2026)",
|
||||
type=openapi.TYPE_STRING,
|
||||
required=False
|
||||
)
|
||||
]
|
||||
)
|
||||
@ -61,6 +67,7 @@ class StudentListView(APIView):
|
||||
def get(self, request):
|
||||
establishment_id = request.GET.get('establishment_id', None)
|
||||
status_filter = request.GET.get('status', None) # Nouveau filtre optionnel
|
||||
school_year_filter = request.GET.get('school_year', None) # Filtre année scolaire
|
||||
|
||||
if establishment_id is None:
|
||||
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
@ -70,6 +77,9 @@ class StudentListView(APIView):
|
||||
if status_filter:
|
||||
students_qs = students_qs.filter(registrationform__status=status_filter)
|
||||
|
||||
if school_year_filter:
|
||||
students_qs = students_qs.filter(registrationform__school_year=school_year_filter)
|
||||
|
||||
students_qs = students_qs.distinct()
|
||||
students_serializer = StudentByRFCreationSerializer(students_qs, many=True)
|
||||
return JsonResponse(students_serializer.data, safe=False)
|
||||
|
||||
Reference in New Issue
Block a user