From e65e31014d2b89afe1e5f077e8d4109f07d40d0b Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sun, 18 May 2025 15:42:21 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20Utilisation=20d'une=20application?= =?UTF-8?q?=20"Common"=20pour=20tous=20les=20mod=C3=A8les=20de=20r=C3=A9f?= =?UTF-8?q?=C3=A9rence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Back-End/Common/apps.py | 3 + Back-End/Common/models.py | 64 ++++++- Back-End/Common/serializers.py | 21 +++ Back-End/{School => Common}/signals.py | 37 +++- Back-End/Common/urls.py | 18 ++ Back-End/Common/views.py | 166 +++++++++++++++++- Back-End/Establishment/views.py | 3 +- Back-End/N3wtSchool/settings.py | 1 + Back-End/N3wtSchool/urls.py | 1 + Back-End/School/apps.py | 5 +- .../management/commands/init_mock_datas.py | 13 +- Back-End/School/models.py | 57 +----- Back-End/School/serializers.py | 22 +-- Back-End/School/urls.py | 12 -- Back-End/School/views.py | 157 +---------------- Back-End/Subscriptions/models.py | 23 ++- Back-End/Subscriptions/serializers.py | 20 ++- .../templatetags/myTemplateTag.py | 5 +- Back-End/Subscriptions/urls.py | 7 +- Back-End/Subscriptions/views/__init__.py | 5 +- .../views/register_form_views.py | 35 +++- .../views/student_competencies_views.py | 91 ++++++++++ Back-End/clean_migration_dir.py | 1 + Back-End/start.py | 1 + 24 files changed, 491 insertions(+), 277 deletions(-) create mode 100644 Back-End/Common/serializers.py rename Back-End/{School => Common}/signals.py (67%) create mode 100644 Back-End/Common/urls.py create mode 100644 Back-End/Subscriptions/views/student_competencies_views.py diff --git a/Back-End/Common/apps.py b/Back-End/Common/apps.py index 1345f2c..19006f2 100644 --- a/Back-End/Common/apps.py +++ b/Back-End/Common/apps.py @@ -4,3 +4,6 @@ from django.apps import AppConfig class CommonConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'Common' + + def ready(self): + import Common.signals \ No newline at end of file diff --git a/Back-End/Common/models.py b/Back-End/Common/models.py index 055cae6..3fd3a1f 100644 --- a/Back-End/Common/models.py +++ b/Back-End/Common/models.py @@ -1,13 +1,59 @@ from django.db import models -class StudentCompetency(models.Model): - student = models.ForeignKey('Subscriptions.Student', on_delete=models.CASCADE, related_name='competency_scores') - competency = models.ForeignKey('School.Competency', on_delete=models.CASCADE, related_name='student_scores') - score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) - comment = models.TextField(blank=True, null=True) - - class Meta: - unique_together = ('student', 'competency') +class Domain(models.Model): + name = models.CharField(max_length=255) + cycle = models.IntegerField(choices=[(1, 'Cycle 1'), (2, 'Cycle 2'), (3, 'Cycle 3'), (4, 'Cycle 4')]) def __str__(self): - return f"{self.student} - {self.competency.name} - Score: {self.score}" + return f"{self.name} (Cycle {self.cycle})" + +class Category(models.Model): + name = models.CharField(max_length=255) + domain = models.ForeignKey(Domain, on_delete=models.CASCADE, related_name='categories') + + def __str__(self): + return self.name + +class Competency(models.Model): + name = models.TextField() + end_of_cycle = models.BooleanField(default=False, null=True, blank=True) + level = models.CharField(max_length=50, null=True, blank=True) + category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='competencies') + + establishments = models.ManyToManyField( + 'Establishment.Establishment', + through='School.EstablishmentCompetency', + related_name='competencies', + blank=True + ) + + def __str__(self): + return self.name + +class PaymentPlanType(models.Model): + code = models.CharField(max_length=50, unique=True) + label = models.CharField(max_length=255) + + def __str__(self): + return self.label + +class PaymentModeType(models.Model): + code = models.CharField(max_length=50, unique=True) + label = models.CharField(max_length=255) + + def __str__(self): + return self.label + +class Cycle(models.Model): + number = models.IntegerField(unique=True) + label = models.CharField(max_length=50) + + def __str__(self): + return f"Cycle {self.number} - {self.label}" + +class Level(models.Model): + name = models.CharField(max_length=50) + cycle = models.ForeignKey(Cycle, on_delete=models.CASCADE, related_name='levels') + + def __str__(self): + return self.name \ No newline at end of file diff --git a/Back-End/Common/serializers.py b/Back-End/Common/serializers.py new file mode 100644 index 0000000..5b79b63 --- /dev/null +++ b/Back-End/Common/serializers.py @@ -0,0 +1,21 @@ +from rest_framework import serializers +from Common.models import ( + Domain, + Category, + Competency +) + +class DomainSerializer(serializers.ModelSerializer): + class Meta: + model = Domain + fields = '__all__' + +class CategorySerializer(serializers.ModelSerializer): + class Meta: + model = Category + fields = '__all__' + +class CompetencySerializer(serializers.ModelSerializer): + class Meta: + model = Competency + fields = '__all__' \ No newline at end of file diff --git a/Back-End/School/signals.py b/Back-End/Common/signals.py similarity index 67% rename from Back-End/School/signals.py rename to Back-End/Common/signals.py index 8567ac2..72dbc58 100644 --- a/Back-End/School/signals.py +++ b/Back-End/Common/signals.py @@ -2,10 +2,10 @@ import json import os from django.db.models.signals import post_migrate from django.dispatch import receiver -from School.models import Domain, Category, Competency, PaymentModeType, PaymentPlanType +from Common.models import Domain, Category, Competency, PaymentModeType, PaymentPlanType, Cycle, Level @receiver(post_migrate) -def school_post_migrate(sender, **kwargs): +def common_post_migrate(sender, **kwargs): if sender.name != "School": return @@ -57,7 +57,6 @@ def school_post_migrate(sender, **kwargs): for mode in payment_mode_types: PaymentModeType.objects.get_or_create(code=mode["code"], defaults={"label": mode["label"]}) - # ... après la création des PaymentModeType ... payment_plan_types = [ {"code": "ONE_TIME", "label": "1 fois"}, {"code": "THREE_TIMES", "label": "3 fois"}, @@ -65,4 +64,34 @@ def school_post_migrate(sender, **kwargs): {"code": "TWELVE_TIMES", "label": "12 fois"}, ] for plan in payment_plan_types: - PaymentPlanType.objects.get_or_create(code=plan["code"], defaults={"label": plan["label"]}) \ No newline at end of file + PaymentPlanType.objects.get_or_create(code=plan["code"], defaults={"label": plan["label"]}) + + # Création des cycles + cycles_data = [ + {"number": 1, "label": "Cycle 1"}, + {"number": 2, "label": "Cycle 2"}, + {"number": 3, "label": "Cycle 3"}, + {"number": 4, "label": "Cycle 4"}, + ] + cycle_objs = {} + for cycle in cycles_data: + obj, _ = Cycle.objects.get_or_create(number=cycle["number"], defaults={"label": cycle["label"]}) + cycle_objs[cycle["number"]] = obj + + # Création des niveaux et association au cycle + levels_data = [ + {"name": "TPS", "cycle": 1}, + {"name": "PS", "cycle": 1}, + {"name": "MS", "cycle": 1}, + {"name": "GS", "cycle": 1}, + {"name": "CP", "cycle": 2}, + {"name": "CE1", "cycle": 2}, + {"name": "CE2", "cycle": 2}, + {"name": "CM1", "cycle": 3}, + {"name": "CM2", "cycle": 3}, + ] + for level in levels_data: + Level.objects.get_or_create( + name=level["name"], + defaults={"cycle": cycle_objs[level["cycle"]]} + ) \ No newline at end of file diff --git a/Back-End/Common/urls.py b/Back-End/Common/urls.py new file mode 100644 index 0000000..9c0fd0b --- /dev/null +++ b/Back-End/Common/urls.py @@ -0,0 +1,18 @@ +from django.urls import path, re_path + +from .views import ( + DomainListCreateView, DomainDetailView, + CategoryListCreateView, CategoryDetailView, + CompetencyListCreateView, CompetencyDetailView, +) + +urlpatterns = [ + re_path(r'^domains$', DomainListCreateView.as_view(), name="domain_list_create"), + re_path(r'^domains/(?P[0-9]+)$', DomainDetailView.as_view(), name="domain_detail"), + + re_path(r'^categories$', CategoryListCreateView.as_view(), name="category_list_create"), + re_path(r'^categories/(?P[0-9]+)$', CategoryDetailView.as_view(), name="category_detail"), + + re_path(r'^competencies$', CompetencyListCreateView.as_view(), name="competency_list_create"), + re_path(r'^competencies/(?P[0-9]+)$', CompetencyDetailView.as_view(), name="competency_detail"), +] \ No newline at end of file diff --git a/Back-End/Common/views.py b/Back-End/Common/views.py index 91ea44a..b650bc4 100644 --- a/Back-End/Common/views.py +++ b/Back-End/Common/views.py @@ -1,3 +1,165 @@ -from django.shortcuts import render +from django.http.response import JsonResponse +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect +from django.utils.decorators import method_decorator +from rest_framework.parsers import JSONParser +from rest_framework.views import APIView +from .models import ( + Domain, + Category, + Competency +) +from .serializers import ( + DomainSerializer, + CategorySerializer, + CompetencySerializer +) -# Create your views here. +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class DomainListCreateView(APIView): + def get(self, request): + domains = Domain.objects.all() + serializer = DomainSerializer(domains, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = DomainSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class DomainDetailView(APIView): + def get(self, request, id): + try: + domain = Domain.objects.get(id=id) + serializer = DomainSerializer(domain) + return JsonResponse(serializer.data, safe=False) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + domain = Domain.objects.get(id=id) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = DomainSerializer(domain, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + domain = Domain.objects.get(id=id) + domain.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + +# Répète la même logique pour Category, Competency, EstablishmentCompetency + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CategoryListCreateView(APIView): + def get(self, request): + categories = Category.objects.all() + serializer = CategorySerializer(categories, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = CategorySerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CategoryDetailView(APIView): + def get(self, request, id): + try: + category = Category.objects.get(id=id) + serializer = CategorySerializer(category) + return JsonResponse(serializer.data, safe=False) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + category = Category.objects.get(id=id) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = CategorySerializer(category, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + category = Category.objects.get(id=id) + category.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CompetencyListCreateView(APIView): + def get(self, request): + cycle = request.GET.get('cycle') + if cycle is None: + return JsonResponse({'error': 'cycle est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + competencies_list = getAllObjects(Competency) + competencies_list = competencies_list.filter( + category__domain__cycle=cycle + ).distinct() + serializer = CompetencySerializer(competencies_list, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = CompetencySerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CompetencyDetailView(APIView): + def get(self, request, id): + try: + competency = Competency.objects.get(id=id) + serializer = CompetencySerializer(competency) + return JsonResponse(serializer.data, safe=False) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + competency = Competency.objects.get(id=id) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = CompetencySerializer(competency, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + competency = Competency.objects.get(id=id) + competency.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) diff --git a/Back-End/Establishment/views.py b/Back-End/Establishment/views.py index 76b7407..697a0fd 100644 --- a/Back-End/Establishment/views.py +++ b/Back-End/Establishment/views.py @@ -7,7 +7,8 @@ from rest_framework import status from .models import Establishment from .serializers import EstablishmentSerializer from N3wtSchool.bdd import delete_object, getAllObjects -from School.models import Competency, EstablishmentCompetency +from School.models import EstablishmentCompetency +from Common.models import Competency from django.db.models import Q @method_decorator(csrf_protect, name='dispatch') diff --git a/Back-End/N3wtSchool/settings.py b/Back-End/N3wtSchool/settings.py index 072372e..bd12a4e 100644 --- a/Back-End/N3wtSchool/settings.py +++ b/Back-End/N3wtSchool/settings.py @@ -42,6 +42,7 @@ ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ + 'Common.apps.CommonConfig', 'Subscriptions.apps.GestioninscriptionsConfig', 'Auth.apps.GestionloginConfig', 'GestionMessagerie.apps.GestionMessagerieConfig', diff --git a/Back-End/N3wtSchool/urls.py b/Back-End/N3wtSchool/urls.py index 93c7293..3681bf8 100644 --- a/Back-End/N3wtSchool/urls.py +++ b/Back-End/N3wtSchool/urls.py @@ -39,6 +39,7 @@ schema_view = get_schema_view( urlpatterns = [ path('admin/', admin.site.urls), + path("Common/", include(("Common.urls", 'Common'), namespace='Common')), path("Subscriptions/", include(("Subscriptions.urls", 'Subscriptions'), namespace='Subscriptions')), path("Auth/", include(("Auth.urls", 'Auth'), namespace='Auth')), path("GestionMessagerie/", include(("GestionMessagerie.urls", 'GestionMessagerie'), namespace='GestionMessagerie')), diff --git a/Back-End/School/apps.py b/Back-End/School/apps.py index 560e5ba..1dafe58 100644 --- a/Back-End/School/apps.py +++ b/Back-End/School/apps.py @@ -2,7 +2,4 @@ from django.apps import AppConfig class SchoolConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' - name = 'School' - - def ready(self): - import School.signals \ No newline at end of file + name = 'School' \ No newline at end of file diff --git a/Back-End/School/management/commands/init_mock_datas.py b/Back-End/School/management/commands/init_mock_datas.py index 511c318..7ba2447 100644 --- a/Back-End/School/management/commands/init_mock_datas.py +++ b/Back-End/School/management/commands/init_mock_datas.py @@ -4,12 +4,15 @@ from Subscriptions.models import ( RegistrationFileGroup ) from Auth.models import Profile, ProfileRole +from Common.models import ( + PaymentModeType, + PaymentPlanType, + Level +) from School.models import ( FeeType, Speciality, Teacher, - PaymentModeType, - PaymentPlanType, DiscountType, Fee, Discount, @@ -280,6 +283,7 @@ class Command(BaseCommand): def init_school_classes(self): school_classes_data = self.load_data('school_classes.json') + levels = list(Level.objects.all()) for index, class_data in enumerate(school_classes_data, start=1): # Randomize establishment @@ -288,7 +292,7 @@ class Command(BaseCommand): class_data["establishment"] = establishment.id # Randomize levels - class_data["levels"] = random.sample(range(1, 10), random.randint(1, 5)) + class_data["levels"] = [level.id for level in random.sample(levels, random.randint(1, min(5, len(levels))))] # Randomize teachers establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment)) @@ -331,6 +335,7 @@ class Command(BaseCommand): fake = Faker('fr_FR') # Utiliser le locale français pour Faker file_group_count = RegistrationFileGroup.objects.count() + levels = list(Level.objects.all()) # Récupérer tous les profils existants avec un ProfileRole Parent profiles_with_parent_role = Profile.objects.filter(roles__role_type=ProfileRole.RoleType.PROFIL_PARENT).distinct() @@ -376,7 +381,7 @@ class Command(BaseCommand): "birth_postal_code": fake.postcode(), "nationality": fake.country(), "attending_physician": fake.name(), - "level": fake.random_int(min=1, max=6), + "level": random.choice(levels).id, "guardians": [guardian_data], "sibling": [] } diff --git a/Back-End/School/models.py b/Back-End/School/models.py index 6e8a0cf..98185c9 100644 --- a/Back-End/School/models.py +++ b/Back-End/School/models.py @@ -50,7 +50,7 @@ class SchoolClass(models.Model): school_year = models.CharField(max_length=9, blank=True) updated_date = models.DateTimeField(auto_now=True) teachers = models.ManyToManyField(Teacher, blank=True) - levels = ArrayField(models.IntegerField(choices=LEVEL_CHOICES), default=list) + levels = models.ManyToManyField('Common.Level', blank=True, related_name='school_classes') type = models.IntegerField(choices=PLANNING_TYPE_CHOICES, default=1) time_range = models.JSONField(default=list) opening_days = ArrayField(models.IntegerField(), default=list) @@ -75,21 +75,6 @@ class FeeType(models.IntegerChoices): REGISTRATION_FEE = 0, 'Registration Fee' TUITION_FEE = 1, 'Tuition Fee' -class PaymentPlanType(models.Model): - code = models.CharField(max_length=50, unique=True) - label = models.CharField(max_length=255) - - def __str__(self): - return self.label - -class PaymentModeType(models.Model): - code = models.CharField(max_length=50, unique=True) - label = models.CharField(max_length=255) - is_active = models.BooleanField(default=True) - - def __str__(self): - return self.label - class Discount(models.Model): name = models.CharField(max_length=255, null=True, blank=True) amount = models.DecimalField(max_digits=10, decimal_places=2, default=0) @@ -115,7 +100,7 @@ class Fee(models.Model): return self.name class PaymentPlan(models.Model): - plan_type = models.ForeignKey(PaymentPlanType, on_delete=models.PROTECT, related_name='payment_plans') + plan_type = models.ForeignKey('Common.PaymentPlanType', on_delete=models.PROTECT, related_name='payment_plans') due_dates = ArrayField(models.DateField(), null=True, blank=True) type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_plans') @@ -124,45 +109,13 @@ class PaymentPlan(models.Model): return f"{self.plan_type.label} - {self.get_type_display()}" class PaymentMode(models.Model): - mode = models.ForeignKey(PaymentModeType, on_delete=models.PROTECT, related_name='payment_modes') + mode = models.ForeignKey('Common.PaymentModeType', on_delete=models.PROTECT, related_name='payment_modes') type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_modes') def __str__(self): return f"{self.mode.label} - {self.get_type_display()}" -class Domain(models.Model): - name = models.CharField(max_length=255) - cycle = models.IntegerField(choices=[(1, 'Cycle 1'), (2, 'Cycle 2'), (3, 'Cycle 3'), (4, 'Cycle 4')]) - - def __str__(self): - return f"{self.name} (Cycle {self.cycle})" - - -class Category(models.Model): - name = models.CharField(max_length=255) - domain = models.ForeignKey(Domain, on_delete=models.CASCADE, related_name='categories') - - def __str__(self): - return self.name - - -class Competency(models.Model): - name = models.TextField() - end_of_cycle = models.BooleanField(default=False, null=True, blank=True) - level = models.CharField(max_length=50, null=True, blank=True) - category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='competencies') - - establishments = models.ManyToManyField( - 'Establishment.Establishment', - through='EstablishmentCompetency', - related_name='competencies', - blank=True - ) - - def __str__(self): - return self.name - class EstablishmentCompetency(models.Model): """ Relation entre un établissement et une compétence. @@ -170,12 +123,12 @@ class EstablishmentCompetency(models.Model): """ establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE) competency = models.ForeignKey( - Competency, on_delete=models.CASCADE, null=True, blank=True, + 'Common.Competency', on_delete=models.CASCADE, null=True, blank=True, help_text="Compétence de référence (optionnelle si custom)" ) custom_name = models.TextField(null=True, blank=True, help_text="Nom de la compétence custom") custom_category = models.ForeignKey( - Category, on_delete=models.CASCADE, null=True, blank=True, + 'Common.Category', on_delete=models.CASCADE, null=True, blank=True, help_text="Catégorie de la compétence custom" ) is_required = models.BooleanField(default=True) diff --git a/Back-End/School/serializers.py b/Back-End/School/serializers.py index 72cb883..b0758de 100644 --- a/Back-End/School/serializers.py +++ b/Back-End/School/serializers.py @@ -9,9 +9,6 @@ from .models import ( Fee, PaymentPlan, PaymentMode, - Domain, - Category, - Competency, EstablishmentCompetency ) from Auth.models import Profile, ProfileRole @@ -22,21 +19,6 @@ from N3wtSchool import settings from django.utils import timezone import pytz -class DomainSerializer(serializers.ModelSerializer): - class Meta: - model = Domain - fields = '__all__' - -class CategorySerializer(serializers.ModelSerializer): - class Meta: - model = Category - fields = '__all__' - -class CompetencySerializer(serializers.ModelSerializer): - class Meta: - model = Competency - fields = '__all__' - class EstablishmentCompetencySerializer(serializers.ModelSerializer): class Meta: model = EstablishmentCompetency @@ -203,7 +185,6 @@ class SchoolClassSerializer(serializers.ModelSerializer): 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), time_range=validated_data.get('time_range', ['08:30', '17:30']), opening_days=validated_data.get('opening_days', [1, 2, 4, 5]), @@ -211,6 +192,7 @@ class SchoolClassSerializer(serializers.ModelSerializer): ) school_class.teachers.set(teachers_data) + school_class.levels.set(levels_data) for planning_data in plannings_data: Planning.objects.create( @@ -231,7 +213,6 @@ class SchoolClassSerializer(serializers.ModelSerializer): 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) instance.time_range = validated_data.get('time_range', instance.time_range) instance.opening_days = validated_data.get('opening_days', instance.opening_days) @@ -239,6 +220,7 @@ class SchoolClassSerializer(serializers.ModelSerializer): instance.save() instance.teachers.set(teachers_data) + instance.levels.set(levels_data) existing_plannings = {planning.level: planning for planning in instance.plannings.all()} diff --git a/Back-End/School/urls.py b/Back-End/School/urls.py index a18880e..49af99a 100644 --- a/Back-End/School/urls.py +++ b/Back-End/School/urls.py @@ -9,9 +9,6 @@ from .views import ( DiscountListCreateView, DiscountDetailView, PaymentPlanListCreateView, PaymentPlanDetailView, PaymentModeListCreateView, PaymentModeDetailView, - DomainListCreateView, DomainDetailView, - CategoryListCreateView, CategoryDetailView, - CompetencyListCreateView, CompetencyDetailView, EstablishmentCompetencyListCreateView, EstablishmentCompetencyDetailView, ) @@ -40,15 +37,6 @@ urlpatterns = [ re_path(r'^paymentModes$', PaymentModeListCreateView.as_view(), name="payment_mode_list_create"), re_path(r'^paymentModes/(?P[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail"), - re_path(r'^domains$', DomainListCreateView.as_view(), name="domain_list_create"), - re_path(r'^domains/(?P[0-9]+)$', DomainDetailView.as_view(), name="domain_detail"), - - re_path(r'^categories$', CategoryListCreateView.as_view(), name="category_list_create"), - re_path(r'^categories/(?P[0-9]+)$', CategoryDetailView.as_view(), name="category_detail"), - - re_path(r'^competencies$', CompetencyListCreateView.as_view(), name="competency_list_create"), - re_path(r'^competencies/(?P[0-9]+)$', CompetencyDetailView.as_view(), name="competency_detail"), - re_path(r'^establishmentCompetencies$', EstablishmentCompetencyListCreateView.as_view(), name="establishment_competency_list_create"), re_path(r'^establishmentCompetencies/(?P[0-9]+)$', EstablishmentCompetencyDetailView.as_view(), name="establishment_competency_detail"), ] \ No newline at end of file diff --git a/Back-End/School/views.py b/Back-End/School/views.py index eecfb78..330db63 100644 --- a/Back-End/School/views.py +++ b/Back-End/School/views.py @@ -13,9 +13,6 @@ from .models import ( Fee, PaymentPlan, PaymentMode, - Domain, - Category, - Competency, EstablishmentCompetency ) from .serializers import ( @@ -27,11 +24,9 @@ from .serializers import ( FeeSerializer, PaymentPlanSerializer, PaymentModeSerializer, - DomainSerializer, - CategorySerializer, - CompetencySerializer, EstablishmentCompetencySerializer ) +from Common.models import Domain, Category, Competency from N3wtSchool.bdd import delete_object, getAllObjects, getObject from django.db.models import Q from collections import defaultdict @@ -422,156 +417,6 @@ class PaymentModeDetailView(APIView): def delete(self, request, id): return delete_object(PaymentMode, id) -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class DomainListCreateView(APIView): - def get(self, request): - domains = Domain.objects.all() - serializer = DomainSerializer(domains, many=True) - return JsonResponse(serializer.data, safe=False) - - def post(self, request): - data = JSONParser().parse(request) - serializer = DomainSerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) - return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class DomainDetailView(APIView): - def get(self, request, id): - try: - domain = Domain.objects.get(id=id) - serializer = DomainSerializer(domain) - return JsonResponse(serializer.data, safe=False) - except Domain.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - - def put(self, request, id): - try: - domain = Domain.objects.get(id=id) - except Domain.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - data = JSONParser().parse(request) - serializer = DomainSerializer(domain, data=data, partial=True) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, safe=False) - return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, id): - try: - domain = Domain.objects.get(id=id) - domain.delete() - return JsonResponse({'message': 'Deleted'}, safe=False) - except Domain.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - -# Répète la même logique pour Category, Competency, EstablishmentCompetency - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class CategoryListCreateView(APIView): - def get(self, request): - categories = Category.objects.all() - serializer = CategorySerializer(categories, many=True) - return JsonResponse(serializer.data, safe=False) - - def post(self, request): - data = JSONParser().parse(request) - serializer = CategorySerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) - return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class CategoryDetailView(APIView): - def get(self, request, id): - try: - category = Category.objects.get(id=id) - serializer = CategorySerializer(category) - return JsonResponse(serializer.data, safe=False) - except Category.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - - def put(self, request, id): - try: - category = Category.objects.get(id=id) - except Category.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - data = JSONParser().parse(request) - serializer = CategorySerializer(category, data=data, partial=True) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, safe=False) - return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, id): - try: - category = Category.objects.get(id=id) - category.delete() - return JsonResponse({'message': 'Deleted'}, safe=False) - except Category.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class CompetencyListCreateView(APIView): - def get(self, request): - cycle = request.GET.get('cycle') - if cycle is None: - return JsonResponse({'error': 'cycle est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) - - competencies_list = getAllObjects(Competency) - competencies_list = competencies_list.filter( - category__domain__cycle=cycle - ).distinct() - serializer = CompetencySerializer(competencies_list, many=True) - return JsonResponse(serializer.data, safe=False) - - def post(self, request): - data = JSONParser().parse(request) - serializer = CompetencySerializer(data=data) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) - return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class CompetencyDetailView(APIView): - def get(self, request, id): - try: - competency = Competency.objects.get(id=id) - serializer = CompetencySerializer(competency) - return JsonResponse(serializer.data, safe=False) - except Competency.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - - def put(self, request, id): - try: - competency = Competency.objects.get(id=id) - except Competency.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - data = JSONParser().parse(request) - serializer = CompetencySerializer(competency, data=data, partial=True) - if serializer.is_valid(): - serializer.save() - return JsonResponse(serializer.data, safe=False) - return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, id): - try: - competency = Competency.objects.get(id=id) - competency.delete() - return JsonResponse({'message': 'Deleted'}, safe=False) - except Competency.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) - @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') class EstablishmentCompetencyListCreateView(APIView): diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index a9b81bd..a00f6cd 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -4,7 +4,6 @@ from django.conf import settings from django.utils.translation import gettext_lazy as _ from datetime import datetime -from School.models import PaymentModeType, PaymentPlanType import os @@ -85,7 +84,7 @@ class Student(models.Model): last_name = models.CharField(max_length=200, default="") first_name = models.CharField(max_length=200, default="") gender = models.IntegerField(choices=StudentGender, default=StudentGender.NONE, blank=True) - level = models.IntegerField(choices=StudentLevel, default=StudentLevel.NONE, blank=True) + level = models.ForeignKey('Common.Level', on_delete=models.SET_NULL, null=True, blank=True, related_name='students') nationality = models.CharField(max_length=200, default="", blank=True) address = models.CharField(max_length=200, default="", blank=True) birth_date = models.DateField(null=True, blank=True) @@ -247,10 +246,10 @@ class RegistrationForm(models.Model): blank=True) establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='register_forms') - registration_payment = models.ForeignKey('School.PaymentModeType', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_modes_forms') - tuition_payment = models.ForeignKey('School.PaymentModeType', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_modes_forms') - registration_payment_plan = models.ForeignKey('School.PaymentPlanType', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_plans_forms') - tuition_payment_plan = models.ForeignKey('School.PaymentPlanType', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_plans_forms') + registration_payment = models.ForeignKey('School.PaymentMode', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_modes_forms') + tuition_payment = models.ForeignKey('School.PaymentMode', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_modes_forms') + registration_payment_plan = models.ForeignKey('School.PaymentPlan', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_plans_forms') + tuition_payment_plan = models.ForeignKey('School.PaymentPlan', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_plans_forms') def __str__(self): return "RF_" + self.student.last_name + "_" + self.student.first_name @@ -323,6 +322,18 @@ class RegistrationSchoolFileTemplate(models.Model): filenames.append(reg_file.file.path) return filenames +class StudentCompetency(models.Model): + student = models.ForeignKey('Subscriptions.Student', on_delete=models.CASCADE, related_name='competency_scores') + competency = models.ForeignKey('Common.Competency', on_delete=models.CASCADE, related_name='student_scores') + score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) + comment = models.TextField(blank=True, null=True) + + class Meta: + unique_together = ('student', 'competency') + + def __str__(self): + return f"{self.student} - {self.competency.name} - Score: {self.score}" + ####### Parent files templates (par dossier d'inscription) ####### class RegistrationParentFileTemplate(models.Model): master = models.ForeignKey(RegistrationParentFileMaster, on_delete=models.CASCADE, related_name='parent_file_templates', blank=True) diff --git a/Back-End/Subscriptions/serializers.py b/Back-End/Subscriptions/serializers.py index 1c36b7e..c3f6105 100644 --- a/Back-End/Subscriptions/serializers.py +++ b/Back-End/Subscriptions/serializers.py @@ -1,5 +1,17 @@ from rest_framework import serializers -from .models import RegistrationFileGroup, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationSchoolFileMaster, RegistrationSchoolFileTemplate, RegistrationParentFileMaster, RegistrationParentFileTemplate, AbsenceManagement +from .models import ( + RegistrationFileGroup, + RegistrationForm, + Student, + Guardian, + Sibling, + Language, + RegistrationSchoolFileMaster, + RegistrationSchoolFileTemplate, + RegistrationParentFileMaster, + RegistrationParentFileTemplate, + AbsenceManagement +) from School.models import SchoolClass, Fee, Discount, FeeType from School.serializers import FeeSerializer, DiscountSerializer from Auth.models import ProfileRole, Profile @@ -406,15 +418,19 @@ class GuardianByDICreationSerializer(serializers.ModelSerializer): class StudentByRFCreationSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) guardians = GuardianByDICreationSerializer(many=True, required=False) + associated_class_name = serializers.SerializerMethodField() class Meta: model = Student - fields = ['id', 'last_name', 'first_name', 'guardians', 'level'] + fields = ['id', 'last_name', 'first_name', 'guardians', 'level', 'associated_class_name'] def __init__(self, *args, **kwargs): super(StudentByRFCreationSerializer, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].required = False + + def get_associated_class_name(self, obj): + return obj.associated_class.atmosphere_name if obj.associated_class else None class NotificationSerializer(serializers.ModelSerializer): notification_type_label = serializers.ReadOnlyField() diff --git a/Back-End/Subscriptions/templatetags/myTemplateTag.py b/Back-End/Subscriptions/templatetags/myTemplateTag.py index 56a7d91..5dc0986 100644 --- a/Back-End/Subscriptions/templatetags/myTemplateTag.py +++ b/Back-End/Subscriptions/templatetags/myTemplateTag.py @@ -35,7 +35,10 @@ def getTuitionPaymentMethod(pk): @register.filter def getStudentLevel(pk): registerForm = RegistrationForm.objects.get(student=pk) - return Student.StudentLevel(int(registerForm.student.level)).label + level = registerForm.student.level + if level: + return level.name + return "" @register.filter def getStudentGender(pk): diff --git a/Back-End/Subscriptions/urls.py b/Back-End/Subscriptions/urls.py index 2f71bba..b71a3e7 100644 --- a/Back-End/Subscriptions/urls.py +++ b/Back-End/Subscriptions/urls.py @@ -17,7 +17,9 @@ from .views import ( RegistrationParentFileTemplateSimpleView, RegistrationParentFileTemplateView, AbsenceManagementListCreateView, - AbsenceManagementDetailView + AbsenceManagementDetailView, + StudentCompetencyListCreateView, + StudentCompetencySimpleView ) from .views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group @@ -63,4 +65,7 @@ urlpatterns = [ re_path(r'^absences$', AbsenceManagementListCreateView.as_view(), name="absence_list_create"), re_path(r'^absences/(?P[0-9]+)$', AbsenceManagementDetailView.as_view(), name="absence_detail"), + re_path(r'^studentCompetencies$', StudentCompetencyListCreateView.as_view(), name="student_competency_list_create"), + re_path(r'^studentCompetencies/(?P[0-9]+)$', StudentCompetencySimpleView.as_view(), name="student_competency_detail"), + ] \ No newline at end of file diff --git a/Back-End/Subscriptions/views/__init__.py b/Back-End/Subscriptions/views/__init__.py index ea8ed98..549b3e0 100644 --- a/Back-End/Subscriptions/views/__init__.py +++ b/Back-End/Subscriptions/views/__init__.py @@ -13,6 +13,7 @@ from .registration_file_group_views import RegistrationFileGroupView, Registrati from .student_views import StudentView, StudentListView, ChildrenListView from .guardian_views import GuardianView, DissociateGuardianView from .absences_views import AbsenceManagementDetailView, AbsenceManagementListCreateView +from .student_competencies_views import StudentCompetencyListCreateView, StudentCompetencySimpleView __all__ = [ 'RegisterFormView', @@ -39,5 +40,7 @@ __all__ = [ 'GuardianView', 'DissociateGuardianView', 'AbsenceManagementDetailView', - 'AbsenceManagementListCreateView' + 'AbsenceManagementListCreateView', + 'StudentCompetencyListCreateView', + 'StudentCompetencySimpleView' ] diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py index fef3e79..45e7d07 100644 --- a/Back-End/Subscriptions/views/register_form_views.py +++ b/Back-End/Subscriptions/views/register_form_views.py @@ -16,10 +16,22 @@ import Subscriptions.util as util from Subscriptions.serializers import RegistrationFormSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileTemplateSerializer from Subscriptions.pagination import CustomSubscriptionPagination -from Subscriptions.models import Student, Guardian, RegistrationForm, RegistrationSchoolFileTemplate, RegistrationFileGroup, RegistrationParentFileTemplate +from Subscriptions.models import ( + Student, + Guardian, + RegistrationForm, + RegistrationSchoolFileTemplate, + RegistrationFileGroup, + RegistrationParentFileTemplate, + StudentCompetency +) from Subscriptions.automate import updateStateMachine +from Common.models import Competency from N3wtSchool import settings, bdd +from django.db.models import Q + + import logging logger = logging.getLogger(__name__) @@ -236,7 +248,6 @@ class RegisterFormWithIdView(APIView): studentForm_data = request.data.get('data', '{}') try: data = json.loads(studentForm_data) - print(f'data : {data}') except json.JSONDecodeError: return JsonResponse({"error": "Invalid JSON format in 'data'"}, status=status.HTTP_400_BAD_REQUEST) @@ -366,6 +377,26 @@ class RegisterFormWithIdView(APIView): save=True ) + # Valorisation des StudentCompetency pour l'élève + try: + student = registerForm.student + cycle = None + if student.level: + cycle = student.level.cycle.number + if cycle: + competencies = Competency.objects.filter( + category__domain__cycle=cycle + ).filter( + Q(end_of_cycle=True) | Q(level=student.level.name) + ) + for comp in competencies: + StudentCompetency.objects.get_or_create( + student=student, + competency=comp + ) + except Exception as e: + logger.error(f"Erreur lors de la valorisation des StudentCompetency: {e}") + updateStateMachine(registerForm, 'EVENT_VALIDATE') # Retourner les données mises à jour diff --git a/Back-End/Subscriptions/views/student_competencies_views.py b/Back-End/Subscriptions/views/student_competencies_views.py new file mode 100644 index 0000000..9e9e150 --- /dev/null +++ b/Back-End/Subscriptions/views/student_competencies_views.py @@ -0,0 +1,91 @@ +from django.http.response import JsonResponse +from rest_framework.views import APIView +from rest_framework import status +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect +from django.utils.decorators import method_decorator +from Subscriptions.models import StudentCompetency, Student +from Common.models import Domain, Competency +from N3wtSchool.bdd import delete_object + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class StudentCompetencyListCreateView(APIView): + def get(self, request): + student_id = request.GET.get('student_id') + if not student_id: + return JsonResponse({'error': 'student_id requis'}, status=400) + try: + student = Student.objects.get(id=student_id) + except Student.DoesNotExist: + return JsonResponse({'error': 'Élève introuvable'}, status=404) + + student_competencies = StudentCompetency.objects.filter(student=student).select_related('competency', 'competency__category', 'competency__category__domain') + + # On ne garde que les IDs des compétences de l'élève + student_competency_ids = set(sc.competency_id for sc in student_competencies) + + result = [] + total_competencies = 0 + domaines = Domain.objects.all() + for domaine in domaines: + domaine_dict = { + "domaine_id": domaine.id, + "domaine_nom": domaine.name, + "categories": [] + } + categories = domaine.categories.all() + for categorie in categories: + categorie_dict = { + "categorie_id": categorie.id, + "categorie_nom": categorie.name, + "competences": [] + } + # On ne boucle que sur les compétences du student pour cette catégorie + for sc in student_competencies: + comp = sc.competency + if comp.category_id == categorie.id: + categorie_dict["competences"].append({ + "competence_id": comp.id, + "nom": comp.name, + "score": sc.score, + "comment": sc.comment or "", + }) + total_competencies += 1 + if categorie_dict["competences"]: + domaine_dict["categories"].append(categorie_dict) + if domaine_dict["categories"]: + result.append(domaine_dict) + + return JsonResponse({ + "count": total_competencies, + "data": result + }, safe=False, status=200) + + # def post(self, request): + # serializer = AbsenceManagementSerializer(data=request.data) + # if serializer.is_valid(): + # serializer.save() + # return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + # return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class StudentCompetencySimpleView(APIView): + def get(self, request, id): + return JsonResponse("ok", safe=False, status=status.HTTP_200_OK) + + # def put(self, request, id): + # try: + # absence = AbsenceManagement.objects.get(id=id) + # serializer = AbsenceManagementSerializer(absence, data=request.data) + # if serializer.is_valid(): + # serializer.save() + # return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK) + # return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + # except AbsenceManagement.DoesNotExist: + # return JsonResponse({"error": "Absence not found"}, safe=False, status=status.HTTP_404_NOT_FOUND) + + # def delete(self, request, id): + # return delete_object(AbsenceManagement, id) \ No newline at end of file diff --git a/Back-End/clean_migration_dir.py b/Back-End/clean_migration_dir.py index a781cfd..b04e17a 100644 --- a/Back-End/clean_migration_dir.py +++ b/Back-End/clean_migration_dir.py @@ -10,6 +10,7 @@ APPS = [ "GestionMessagerie", "Auth", "School", + "Common" ] BASE_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/Back-End/start.py b/Back-End/start.py index af2b0e5..a6b9def 100644 --- a/Back-End/start.py +++ b/Back-End/start.py @@ -15,6 +15,7 @@ test_mode = os.getenv('TEST_MODE', 'False') == 'True' commands = [ ["python", "manage.py", "collectstatic", "--noinput"], ["python", "manage.py", "flush", "--noinput"], + ["python", "manage.py", "makemigrations", "Common", "--noinput"], ["python", "manage.py", "makemigrations", "Establishment", "--noinput"], ["python", "manage.py", "makemigrations", "Settings", "--noinput"], ["python", "manage.py", "makemigrations", "Subscriptions", "--noinput"],