From 58fe509734a3b5dc6e0b5c6aa3fd713fd4dc821e Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sun, 12 Jan 2025 13:18:34 +0100 Subject: [PATCH] refactor: Partie "School" --- Back-End/Auth/views.py | 3 + Back-End/N3wtSchool/bdd.py | 19 +++++ Back-End/School/apps.py | 2 +- Back-End/School/models.py | 40 +++++----- Back-End/School/serializers.py | 133 ++++++++++++--------------------- Back-End/School/urls.py | 10 +-- Back-End/School/views.py | 114 +++++++++++----------------- 7 files changed, 139 insertions(+), 182 deletions(-) diff --git a/Back-End/Auth/views.py b/Back-End/Auth/views.py index 9f09124..cd5c385 100644 --- a/Back-End/Auth/views.py +++ b/Back-End/Auth/views.py @@ -90,6 +90,9 @@ class ProfileView(APIView): return JsonResponse(profil_serializer.errors, safe=False) + def delete(self, request, _id): + return bdd.delete_object(Profile, _id) + def infoSession(request): profilCache = cache.get('session_cache') if profilCache: diff --git a/Back-End/N3wtSchool/bdd.py b/Back-End/N3wtSchool/bdd.py index 8d0c0a1..1fe62ef 100644 --- a/Back-End/N3wtSchool/bdd.py +++ b/Back-End/N3wtSchool/bdd.py @@ -1,5 +1,7 @@ import logging from django.db.models import Q +from django.http import JsonResponse +from django.core.exceptions import ObjectDoesNotExist from Subscriptions.models import RegistrationForm, Profile, Student logger = logging.getLogger('N3wtSchool') @@ -86,3 +88,20 @@ def searchObjects(_objectName, _searchTerm=None, _excludeStates=None): except _objectName.DoesNotExist: logging.error(f"Aucun résultat n'a été trouvé - {_objectName.__name__} (recherche: {_searchTerm})") return None + +def delete_object(model_class, object_id, related_field=None): + try: + obj = model_class.objects.get(id=object_id) + if related_field and hasattr(obj, related_field): + related_obj = getattr(obj, related_field) + if related_obj: + related_obj.delete() + obj_name = str(obj) # Utiliser la méthode __str__ + obj.delete() + return JsonResponse({'message': f'La suppression de l\'objet {obj_name} a été effectuée avec succès'}, safe=False) + except ObjectDoesNotExist: + return JsonResponse({'error': f'L\'objet {model_class.__name__} n\'existe pas avec cet ID'}, status=404, safe=False) + except Exception as e: + return JsonResponse({'error': f'Une erreur est survenue : {str(e)}'}, status=500, safe=False) + + diff --git a/Back-End/School/apps.py b/Back-End/School/apps.py index b19a86f..8e7c210 100644 --- a/Back-End/School/apps.py +++ b/Back-End/School/apps.py @@ -4,7 +4,7 @@ from django.db.models.signals import post_migrate def create_speciality(sender, **kwargs): from .models import Speciality if not Speciality.objects.filter(name='GROUPE').exists(): - Speciality.objects.create(name='GROUPE', colorCode='#FF0000') + Speciality.objects.create(name='GROUPE', color_code='#FF0000') class SchoolConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' diff --git a/Back-End/School/models.py b/Back-End/School/models.py index e2d27d7..31d2a80 100644 --- a/Back-End/School/models.py +++ b/Back-End/School/models.py @@ -18,22 +18,22 @@ LEVEL_CHOICES = [ class Speciality(models.Model): name = models.CharField(max_length=100) - updatedDate = models.DateTimeField(auto_now=True) - colorCode = models.CharField(max_length=7, default='#FFFFFF') + updated_date = models.DateTimeField(auto_now=True) + color_code = models.CharField(max_length=7, default='#FFFFFF') def __str__(self): return self.name class Teacher(models.Model): - lastName = models.CharField(max_length=100) - firstName = models.CharField(max_length=100) + last_name = models.CharField(max_length=100) + first_name = models.CharField(max_length=100) email = models.EmailField(unique=True) - specialities = models.ManyToManyField(Speciality, related_name='teachers') - associatedProfile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True, blank=True) - updatedDate = models.DateTimeField(auto_now=True) + specialities = models.ManyToManyField(Speciality, blank=True) + associated_profile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True, blank=True) + updated_date = models.DateTimeField(auto_now=True) def __str__(self): - return f"{self.lastName} {self.firstName}" + return f"{self.last_name} {self.first_name}" class SchoolClass(models.Model): PLANNING_TYPE_CHOICES = [ @@ -42,25 +42,25 @@ class SchoolClass(models.Model): (3, 'Trimestriel') ] - atmosphereName = models.CharField(max_length=255, null=True, blank=True) - ageGroup = models.JSONField() - numberOfStudents = models.PositiveIntegerField() - teachingLanguage = models.CharField(max_length=255) - schoolYear = models.CharField(max_length=9) - updatedDate = models.DateTimeField(auto_now_add=True) - teachers = models.ManyToManyField(Teacher, related_name='schoolClasses') + atmosphere_name = models.CharField(max_length=255, null=True, blank=True) + age_range = models.JSONField(blank=True) + number_of_students = models.PositiveIntegerField(blank=True) + teaching_language = models.CharField(max_length=255, blank=True) + school_year = models.CharField(max_length=9, blank=True) + updated_date = models.DateTimeField(auto_now_add=True) + teachers = models.ManyToManyField(Teacher, blank=True) levels = ArrayField(models.IntegerField(choices=LEVEL_CHOICES), default=list) type = models.IntegerField(choices=PLANNING_TYPE_CHOICES, default=1) - schedule = models.JSONField(default=list) - openingDays = ArrayField(models.IntegerField(), default=list) + time_range = models.JSONField(default=list) + opening_days = ArrayField(models.IntegerField(), default=list) def __str__(self): - return self.atmosphereName + return self.atmosphere_name class Planning(models.Model): level = models.IntegerField(choices=LEVEL_CHOICES, null=True, blank=True) - classModel = models.ForeignKey(SchoolClass, null=True, blank=True, related_name='plannings', on_delete=models.CASCADE) + school_class = models.ForeignKey(SchoolClass, null=True, blank=True, related_name='plannings', on_delete=models.CASCADE) schedule = JSONField(default=dict) def __str__(self): - return f'Planning for {self.level} of {self.classModel.atmosphereName}' + return f'Planning for {self.level} of {self.school_class.atmosphere_name}' diff --git a/Back-End/School/serializers.py b/Back-End/School/serializers.py index f58d5a9..320efd5 100644 --- a/Back-End/School/serializers.py +++ b/Back-End/School/serializers.py @@ -9,14 +9,14 @@ from django.utils import timezone import pytz class SpecialitySerializer(serializers.ModelSerializer): - creationDateFormatted = serializers.SerializerMethodField() + updated_date_formatted = serializers.SerializerMethodField() class Meta: model = Speciality fields = '__all__' - def get_creationDateFormatted(self, obj): - utc_time = timezone.localtime(obj.updatedDate) # Convert to local time + def get_updated_date_formatted(self, obj): + utc_time = timezone.localtime(obj.updated_date) # Convert to local time local_tz = pytz.timezone(settings.TZ_APPLI) local_time = utc_time.astimezone(local_tz) @@ -27,52 +27,44 @@ class TeacherDetailSerializer(serializers.ModelSerializer): class Meta: model = Teacher - fields = ['id', 'lastName', 'firstName', 'email', 'specialities'] + fields = ['id', 'last_name', 'first_name', 'email', 'specialities'] class TeacherSerializer(serializers.ModelSerializer): - specialities = SpecialitySerializer(many=True, read_only=True) - specialties_ids = serializers.PrimaryKeyRelatedField(queryset=Speciality.objects.all(), many=True, source='specialities') - associatedProfile_id = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), source='associatedProfile', write_only=False, read_only=False) - mainClasses = serializers.PrimaryKeyRelatedField(many=True, read_only=True, source='schoolClasses') - associatedProfile = ProfileSerializer(read_only=True) - rightLabel = serializers.SerializerMethodField() - rightValue = serializers.SerializerMethodField() - creationDateFormatted = serializers.SerializerMethodField() + specialities = serializers.PrimaryKeyRelatedField(queryset=Speciality.objects.all(), many=True, required=False) + associated_profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=True) + updated_date_formatted = serializers.SerializerMethodField() class Meta: model = Teacher - fields = ['id', 'lastName', 'firstName', 'email', 'specialities', 'specialties_ids', 'mainClasses', 'associatedProfile', 'associatedProfile_id', 'rightLabel', 'rightValue', 'updatedDate', 'creationDateFormatted'] + fields = '__all__' def create(self, validated_data): - specialties_data = validated_data.pop('specialities', None) - associatedProfile = validated_data.pop('associatedProfile', None) + specialities_data = validated_data.pop('specialities', None) + associated_profile = validated_data.pop('associated_profile', None) teacher = Teacher.objects.create(**validated_data) - teacher.specialities.set(specialties_data) - if associatedProfile: - teacher.associatedProfile = associatedProfile + if specialities_data: + teacher.specialities.set(specialities_data) + if associated_profile: + teacher.associated_profile = associated_profile teacher.save() return teacher def update(self, instance, validated_data): - specialties_data = validated_data.pop('specialities', []) - instance.lastName = validated_data.get('lastName', instance.lastName) - instance.firstName = validated_data.get('firstName', instance.firstName) + specialities_data = validated_data.pop('specialities', []) + instance.last_name = validated_data.get('last_name', instance.last_name) + instance.first_name = validated_data.get('first_name', instance.first_name) instance.email = validated_data.get('email', instance.email) - instance.associatedProfile = validated_data.get('associatedProfile', instance.associatedProfile) + instance.associated_profile = validated_data.get('associated_profile', instance.associated_profile) instance.save() - instance.specialities.set(specialties_data) + if specialities_data: + instance.specialities.set(specialities_data) return instance - def get_rightLabel(self, obj): - return obj.associatedProfile.get_right_display() if obj.associatedProfile else None - - def get_rightValue(self, obj): - return obj.associatedProfile.right if obj.associatedProfile else None - - def get_creationDateFormatted(self, obj): - utc_time = timezone.localtime(obj.updatedDate) # Convert to local time + def get_updated_date_formatted(self, obj): + utc_time = timezone.localtime(obj.updated_date) # Convert to local time local_tz = pytz.timezone(settings.TZ_APPLI) local_time = utc_time.astimezone(local_tz) + return local_time.strftime("%d-%m-%Y %H:%M") class PlanningSerializer(serializers.ModelSerializer): @@ -86,65 +78,55 @@ class PlanningSerializer(serializers.ModelSerializer): return internal_value class SchoolClassSerializer(serializers.ModelSerializer): - creationDateFormatted = serializers.SerializerMethodField() - teachers = TeacherSerializer(many=True, read_only=True) - teachers_ids = serializers.PrimaryKeyRelatedField(queryset=Teacher.objects.all(), many=True, source='teachers') - students = serializers.SerializerMethodField() - levels = serializers.ListField(child=serializers.ChoiceField(choices=LEVEL_CHOICES)) - plannings_read = serializers.SerializerMethodField() - plannings = PlanningSerializer(many=True, write_only=True) + updated_date_formatted = serializers.SerializerMethodField() + teachers = serializers.PrimaryKeyRelatedField(queryset=Teacher.objects.all(), many=True, required=False) class Meta: model = SchoolClass - fields = [ - 'id', 'atmosphereName', 'ageGroup', 'numberOfStudents', 'teachingLanguage', - 'teachers', 'teachers_ids', 'schoolYear', 'updatedDate', - 'creationDateFormatted', 'students', 'levels', 'type', 'schedule', - 'openingDays', 'plannings', 'plannings_read' - ] + fields = '__all__' def create(self, validated_data): teachers_data = validated_data.pop('teachers', []) levels_data = validated_data.pop('levels', []) plannings_data = validated_data.pop('plannings', []) - classModel = SchoolClass.objects.create( - atmosphereName=validated_data.get('atmosphereName', ''), - ageGroup=validated_data.get('ageGroup', []), - numberOfStudents=validated_data.get('numberOfStudents', 0), - teachingLanguage=validated_data.get('teachingLanguage', ''), - schoolYear=validated_data.get('schoolYear', ''), + school_class = SchoolClass.objects.create( + atmosphere_name=validated_data.get('atmosphere_name', ''), + age_range=validated_data.get('age_range', []), + number_of_students=validated_data.get('number_of_students', 0), + teaching_language=validated_data.get('teaching_language', ''), + school_year=validated_data.get('school_year', ''), levels=levels_data, type=validated_data.get('type', 1), # Added here - schedule=validated_data.get('schedule', ['08:30', '17:30']), # Added here - openingDays=validated_data.get('openingDays', [1, 2, 4, 5]) # Added here + time_range=validated_data.get('time_range', ['08:30', '17:30']), # Added here + opening_days=validated_data.get('opening_days', [1, 2, 4, 5]) # Added here ) - classModel.teachers.set(teachers_data) + school_class.teachers.set(teachers_data) for planning_data in plannings_data: Planning.objects.create( - classModel=classModel, + school_class=school_class, level=planning_data['level'], schedule=planning_data.get('schedule', {}) ) - return classModel + return school_class def update(self, instance, validated_data): teachers_data = validated_data.pop('teachers', []) levels_data = validated_data.pop('levels', []) plannings_data = validated_data.pop('plannings', []) - instance.atmosphereName = validated_data.get('atmosphereName', instance.atmosphereName) - instance.ageGroup = validated_data.get('ageGroup', instance.ageGroup) - instance.numberOfStudents = validated_data.get('numberOfStudents', instance.numberOfStudents) - instance.teachingLanguage = validated_data.get('teachingLanguage', instance.teachingLanguage) - instance.schoolYear = validated_data.get('schoolYear', instance.schoolYear) + instance.atmosphere_name = validated_data.get('atmosphere_name', instance.atmosphere_name) + instance.age_range = validated_data.get('age_range', instance.age_range) + instance.number_of_students = validated_data.get('number_of_students', instance.number_of_students) + instance.teaching_language = validated_data.get('teaching_language', instance.teaching_language) + instance.school_year = validated_data.get('school_year', instance.school_year) instance.levels = levels_data instance.type = validated_data.get('type', instance.type) # Added here - instance.schedule = validated_data.get('schedule', instance.schedule) # Added here - instance.openingDays = validated_data.get('openingDays', instance.openingDays) # Added here + instance.time_range = validated_data.get('time_range', instance.time_range) # Added here + instance.opening_days = validated_data.get('opening_days', instance.opening_days) # Added here instance.save() instance.teachers.set(teachers_data) @@ -161,34 +143,15 @@ class SchoolClassSerializer(serializers.ModelSerializer): else: # Create new planning if level not existing Planning.objects.create( - classModel=instance, + school_class=instance, level=level, schedule=planning_data.get('schedule', {}) ) return instance - def get_creationDateFormatted(self, obj): - utc_time = timezone.localtime(obj.updatedDate) + def get_updated_date_formatted(self, obj): + utc_time = timezone.localtime(obj.updated_date) local_tz = pytz.timezone(settings.TZ_APPLI) local_time = utc_time.astimezone(local_tz) - return local_time.strftime("%d-%m-%Y %H:%M") - - def get_students(self, obj): - studentsList = obj.students.all() - filtered_students = [] - for student in studentsList: - registrationForm = bdd.getObject(RegistrationForm, "student__id", student.id) - if registrationForm.status == registrationForm.RegistrationFormStatus.RF_VALIDATED: - filtered_students.append(student) - return StudentSerializer(filtered_students, many=True, read_only=True).data - - def get_plannings_read(self, obj): - plannings = obj.plannings.all() - levels_dict = {level: {'level': level, 'planning': None} for level in obj.levels} - - for planning in plannings: - if planning.level in levels_dict: - levels_dict[planning.level]['planning'] = PlanningSerializer(planning).data - - return list(levels_dict.values()) + return local_time.strftime("%d-%m-%Y %H:%M") \ No newline at end of file diff --git a/Back-End/School/urls.py b/Back-End/School/urls.py index 8dd1050..77897c3 100644 --- a/Back-End/School/urls.py +++ b/Back-End/School/urls.py @@ -1,16 +1,16 @@ from django.urls import path, re_path -from School.views import TeachersView, EnseignantView, SpecialitiesView, SpecialityView, ClassesView, ClasseView, PlanningsView, PlanningView +from School.views import TeachersView, TeacherView, SpecialitiesView, SpecialityView, ClassesView, ClasseView, PlanningsView, PlanningView urlpatterns = [ - re_path(r'^teachers$', TeachersView.as_view(), name="teachers"), - re_path(r'^teacher$', EnseignantView.as_view(), name="teacher"), - re_path(r'^teacher/([0-9]+)$', EnseignantView.as_view(), name="teacher"), - re_path(r'^specialities$', SpecialitiesView.as_view(), name="specialities"), re_path(r'^speciality$', SpecialityView.as_view(), name="speciality"), re_path(r'^speciality/([0-9]+)$', SpecialityView.as_view(), name="speciality"), + re_path(r'^teachers$', TeachersView.as_view(), name="teachers"), + re_path(r'^teacher$', TeacherView.as_view(), name="teacher"), + re_path(r'^teacher/([0-9]+)$', TeacherView.as_view(), name="teacher"), + re_path(r'^schoolClasses$', ClassesView.as_view(), name="schoolClasses"), re_path(r'^schoolClass$', ClasseView.as_view(), name="schoolClass"), re_path(r'^schoolClass/([0-9]+)$', ClasseView.as_view(), name="schoolClass"), diff --git a/Back-End/School/views.py b/Back-End/School/views.py index 112dd2e..7ba4984 100644 --- a/Back-End/School/views.py +++ b/Back-End/School/views.py @@ -4,59 +4,11 @@ from django.utils.decorators import method_decorator from rest_framework.parsers import JSONParser from rest_framework.views import APIView from django.core.cache import cache +from django.core.exceptions import ObjectDoesNotExist from .models import Teacher, Speciality, SchoolClass, Planning from .serializers import TeacherSerializer, SpecialitySerializer, SchoolClassSerializer, PlanningSerializer from N3wtSchool import bdd -class TeachersView(APIView): - def get(self, request): - teachersList=bdd.getAllObjects(Teacher) - teachers_serializer=TeacherSerializer(teachersList, many=True) - - return JsonResponse(teachers_serializer.data, safe=False) - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class EnseignantView(APIView): - def get (self, request, _id): - teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id) - teacher_serializer=TeacherSerializer(teacher) - - return JsonResponse(teacher_serializer.data, safe=False) - - def post(self, request): - teacher_data=JSONParser().parse(request) - teacher_serializer = TeacherSerializer(data=teacher_data) - - if teacher_serializer.is_valid(): - teacher_serializer.save() - - return JsonResponse(teacher_serializer.data, safe=False) - - return JsonResponse(teacher_serializer.errors, safe=False) - - def put(self, request, _id): - teacher_data=JSONParser().parse(request) - teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id) - teacher_serializer = TeacherSerializer(teacher, data=teacher_data) - if teacher_serializer.is_valid(): - teacher_serializer.save() - return JsonResponse(teacher_serializer.data, safe=False) - - return JsonResponse(teacher_serializer.errors, safe=False) - - def delete(self, request, _id): - teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id) - if teacher is not None: - if teacher.profilAssocie: - print('Suppression du profil associé') - teacher.profilAssocie.delete() - teacher.delete() - return JsonResponse({'message': 'La suppression de l\'teacher a été effectuée avec succès'}, safe=False) - else: - return JsonResponse({'erreur': 'L\'teacher n\'a pas été trouvé'}, safe=False) - - @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') class SpecialitiesView(APIView): @@ -116,11 +68,47 @@ class SpecialityView(APIView): return JsonResponse(speciality_serializer.errors, safe=False) def delete(self, request, _id): - speciality = bdd.getObject(_objectName=Speciality, _columnName='id', _value=_id) - if speciality != None: - speciality.delete() - - return JsonResponse("La suppression de la spécialité a été effectuée avec succès", safe=False) + return bdd.delete_object(Speciality, _id) + +class TeachersView(APIView): + def get(self, request): + teachersList=bdd.getAllObjects(Teacher) + teachers_serializer=TeacherSerializer(teachersList, many=True) + + return JsonResponse(teachers_serializer.data, safe=False) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class TeacherView(APIView): + def get (self, request, _id): + teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id) + teacher_serializer=TeacherSerializer(teacher) + + return JsonResponse(teacher_serializer.data, safe=False) + + def post(self, request): + teacher_data=JSONParser().parse(request) + teacher_serializer = TeacherSerializer(data=teacher_data) + + if teacher_serializer.is_valid(): + teacher_serializer.save() + + return JsonResponse(teacher_serializer.data, safe=False) + + return JsonResponse(teacher_serializer.errors, safe=False) + + def put(self, request, _id): + teacher_data=JSONParser().parse(request) + teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id) + teacher_serializer = TeacherSerializer(teacher, data=teacher_data) + if teacher_serializer.is_valid(): + teacher_serializer.save() + return JsonResponse(teacher_serializer.data, safe=False) + + return JsonResponse(teacher_serializer.errors, safe=False) + + def delete(self, request, _id): + return bdd.delete_object(Teacher, _id, related_field='associated_profile') @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') @@ -180,23 +168,7 @@ class ClasseView(APIView): return JsonResponse(classe_serializer.errors, safe=False) def delete(self, request, _id): - schoolClass = bdd.getObject(_objectName=SchoolClass, _columnName='id', _value=_id) - if schoolClass is not None: - # Supprimer les plannings associés à la schoolClass - for planning in schoolClass.plannings.all(): - print(f'Planning à supprimer : {planning}') - planning.delete() - - # Retirer la schoolClass des élèves associés - for eleve in schoolClass.eleves.all(): - print(f'Student à retirer de la schoolClass : {eleve}') - eleve.classeAssociee = None - eleve.save() - - # Supprimer la schoolClass - schoolClass.delete() - - return JsonResponse("La suppression de la schoolClass a été effectuée avec succès", safe=False) + return bdd.delete_object(SchoolClass, _id) @method_decorator(csrf_protect, name='dispatch')