diff --git a/Back-End/Auth/models.py b/Back-End/Auth/models.py index 9792319..bb2cb9c 100644 --- a/Back-End/Auth/models.py +++ b/Back-End/Auth/models.py @@ -20,6 +20,7 @@ class Profile(AbstractUser): datePeremption = models.CharField(max_length=200, default="", blank=True) droit = models.IntegerField(choices=Droits, default=Droits.PROFIL_UNDEFINED) estConnecte = models.BooleanField(default=False, blank=True) + establishment = models.ForeignKey('School.Establishment', on_delete=models.PROTECT, related_name='profile', null=True, blank=True) def __str__(self): return self.email + " - " + str(self.droit) diff --git a/Back-End/Auth/serializers.py b/Back-End/Auth/serializers.py index 8ae7737..67ce120 100644 --- a/Back-End/Auth/serializers.py +++ b/Back-End/Auth/serializers.py @@ -8,7 +8,7 @@ class ProfileSerializer(serializers.ModelSerializer): class Meta: model = Profile - fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active'] + fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active', 'establishment'] extra_kwargs = {'password': {'write_only': True}} def create(self, validated_data): @@ -16,7 +16,8 @@ class ProfileSerializer(serializers.ModelSerializer): username=validated_data['username'], email=validated_data['email'], is_active=validated_data['is_active'], - droit=validated_data['droit'] + droit=validated_data['droit'], + establishment=validated_data.get('establishment') ) user.set_password(validated_data['password']) user.save() diff --git a/Back-End/Auth/views.py b/Back-End/Auth/views.py index 20fe255..a8b4de8 100644 --- a/Back-End/Auth/views.py +++ b/Back-End/Auth/views.py @@ -204,6 +204,7 @@ class LoginView(APIView): 'user_id': user.id, 'email': user.email, 'droit': user.droit, + 'establishment': user.establishment.id, 'type': 'access', 'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'], 'iat': datetime.utcnow(), @@ -300,6 +301,7 @@ class RefreshJWTView(APIView): 'user_id': user.id, 'email': user.email, 'droit': user.droit, + 'establishment': user.establishment.id, 'type': 'access', 'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'], 'iat': datetime.utcnow(), diff --git a/Back-End/N3wtSchool/bdd.py b/Back-End/N3wtSchool/bdd.py index 6c4b58b..12eafff 100644 --- a/Back-End/N3wtSchool/bdd.py +++ b/Back-End/N3wtSchool/bdd.py @@ -2,7 +2,8 @@ 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 +from Subscriptions.models import RegistrationForm, Student +from Auth.models import Profile logger = logging.getLogger('N3wtSchool') diff --git a/Back-End/School/management/commands/init_mock_datas.py b/Back-End/School/management/commands/init_mock_datas.py index 5b12a09..8e05014 100644 --- a/Back-End/School/management/commands/init_mock_datas.py +++ b/Back-End/School/management/commands/init_mock_datas.py @@ -31,12 +31,17 @@ from django.core.exceptions import SuspiciousFileOperation import os from django.conf import settings from faker import Faker +import random +import json + +# Définir le chemin vers le dossier mock_datas +MOCK_DATAS_PATH = os.path.join(settings.BASE_DIR, 'School', 'management', 'mock_datas') class Command(BaseCommand): help = 'Initialise toutes les données mock' def handle(self, *args, **kwargs): - self.create_or_update_establishment() + self.create_or_update_establishments() self.create_or_update_fees() self.create_or_update_discounts() self.create_or_update_payment_modes() @@ -47,71 +52,32 @@ class Command(BaseCommand): self.create_or_update_registration_file_group() self.create_register_form() - def create_or_update_establishment(self): - establishment_data = { - "name": "N3WT", - "address": "Société n3wt-innov 69 Chez LANA", - "total_capacity": 69, - "establishment_type": [StructureType.MATERNELLE, StructureType.PRIMAIRE], - "licence_code": "" - } + def load_data(self, filename): + with open(os.path.join(MOCK_DATAS_PATH, filename), 'r') as file: + return json.load(file) - establishment, created = Establishment.objects.update_or_create( - name=establishment_data["name"], - defaults=establishment_data - ) + def create_or_update_establishments(self): + establishments_data = self.load_data('establishments.json') - if created: - self.stdout.write(self.style.SUCCESS('Establishment created successfully')) - else: - self.stdout.write(self.style.SUCCESS('Establishment updated successfully')) + self.establishments = [] + for establishment_data in establishments_data: + establishment, created = Establishment.objects.update_or_create( + name=establishment_data["name"], + defaults=establishment_data + ) + self.establishments.append(establishment) + if created: + self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} created successfully')) + else: + self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} updated successfully')) def create_or_update_fees(self): - establishment = Establishment.objects.get(name="N3WT") - fees_data = [ - { - "name": "Frais d'inscription", - "base_amount": "150.00", - "description": "Montant de base", - "is_active": True, - "type": FeeType.REGISTRATION_FEE, - "establishment": establishment - }, - { - "name": "Matériel", - "base_amount": "85.00", - "description": "Livres / jouets", - "is_active": True, - "type": FeeType.REGISTRATION_FEE, - "establishment": establishment - }, - { - "name": "Sorties périscolaires", - "base_amount": "120.00", - "description": "Sorties", - "is_active": True, - "type": FeeType.REGISTRATION_FEE, - "establishment": establishment - }, - { - "name": "Les colibris", - "base_amount": "4500.00", - "description": "TPS / PS / MS / GS", - "is_active": True, - "type": FeeType.TUITION_FEE, - "establishment": establishment - }, - { - "name": "Les butterflies", - "base_amount": "5000.00", - "description": "CP / CE1 / CE2 / CM1 / CM2", - "is_active": True, - "type": FeeType.TUITION_FEE, - "establishment": establishment - } - ] + fees_data = self.load_data('fees.json') for fee_data in fees_data: + establishment = random.choice(self.establishments) + fee_data["name"] = f"{fee_data['name']} - {establishment.name}" + fee_data["establishment"] = establishment Fee.objects.update_or_create( name=fee_data["name"], type=fee_data["type"], @@ -121,27 +87,12 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS('Fees initialized or updated successfully')) def create_or_update_discounts(self): - establishment = Establishment.objects.get(name="N3WT") - discounts_data = [ - { - "name": "Parrainage", - "amount": "10.00", - "description": "Réduction pour parrainage", - "discount_type": DiscountType.PERCENT, - "type": FeeType.TUITION_FEE, - "establishment": establishment - }, - { - "name": "Réinscription", - "amount": "100.00", - "description": "Réduction pour Réinscription", - "discount_type": DiscountType.PERCENT, - "type": FeeType.REGISTRATION_FEE, - "establishment": establishment - } - ] + discounts_data = self.load_data('discounts.json') for discount_data in discounts_data: + establishment = random.choice(self.establishments) + discount_data["name"] = f"{discount_data['name']} - {establishment.name}" + discount_data["establishment"] = establishment Discount.objects.update_or_create( name=discount_data["name"], type=discount_data["type"], @@ -152,104 +103,44 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS('Discounts initialized or updated successfully')) def create_or_update_payment_modes(self): - establishment = Establishment.objects.get(name="N3WT") - for fee_type in FeeType.choices: - fee_type_value = fee_type[0] + payment_modes_data = self.load_data('payment_modes.json') - for mode in PaymentModeType.choices: - mode_value = mode[0] - - PaymentMode.objects.update_or_create( - mode=mode_value, - type=fee_type_value, - defaults={ - 'is_active': False, - 'establishment': establishment - } - ) + for payment_mode_data in payment_modes_data: + establishment = random.choice(self.establishments) + payment_mode_data["establishment"] = establishment + PaymentMode.objects.update_or_create( + mode=payment_mode_data["mode"], + type=payment_mode_data["type"], + defaults=payment_mode_data + ) self.stdout.write(self.style.SUCCESS('Payment Modes initialized or updated successfully')) def create_or_update_payment_plans(self): + payment_plans_data = self.load_data('payment_plans.json') current_date = timezone.now().date() - establishment = Establishment.objects.get(name="N3WT") - for fee_type in FeeType.choices: - fee_type_value = fee_type[0] + for payment_plan_data in payment_plans_data: + establishment = random.choice(self.establishments) + payment_plan_data["establishment"] = establishment + payment_plan_data["due_dates"] = [current_date + relativedelta(months=1)] + if payment_plan_data["frequency"] == PaymentPlanType.THREE_TIMES: + payment_plan_data["due_dates"] = [current_date + relativedelta(months=1+4*i) for i in range(3)] + elif payment_plan_data["frequency"] == PaymentPlanType.TEN_TIMES: + payment_plan_data["due_dates"] = [current_date + relativedelta(months=1+i) for i in range(10)] + elif payment_plan_data["frequency"] == PaymentPlanType.TWELVE_TIMES: + payment_plan_data["due_dates"] = [current_date + relativedelta(months=1+i) for i in range(12)] - # 1 fois - échéance à 1 mois à partir de la date actuelle PaymentPlan.objects.update_or_create( - frequency=PaymentPlanType.ONE_TIME, - type=fee_type_value, - defaults={ - 'due_dates': [current_date + relativedelta(months=1)], - 'is_active': True, - 'establishment': establishment - } - ) - - # 3 fois - échéances espacées de 4 mois - PaymentPlan.objects.update_or_create( - frequency=PaymentPlanType.THREE_TIMES, - type=fee_type_value, - defaults={ - 'due_dates': [current_date + relativedelta(months=1+4*i) for i in range(3)], - 'is_active': False, - 'establishment': establishment - } - ) - - # 10 fois - échéances espacées d'un mois - PaymentPlan.objects.update_or_create( - frequency=PaymentPlanType.TEN_TIMES, - type=fee_type_value, - defaults={ - 'due_dates': [current_date + relativedelta(months=1+i) for i in range(10)], - 'is_active': False, - 'establishment': establishment - } - ) - - # 12 fois - échéances espacées d'un mois - PaymentPlan.objects.update_or_create( - frequency=PaymentPlanType.TWELVE_TIMES, - type=fee_type_value, - defaults={ - 'due_dates': [current_date + relativedelta(months=1+i) for i in range(12)], - 'is_active': False, - 'establishment': establishment - } + frequency=payment_plan_data["frequency"], + type=payment_plan_data["type"], + defaults=payment_plan_data ) self.stdout.write(self.style.SUCCESS('Payment Plans initialized or updated successfully')) def create_or_update_specialities(self): - specialities_data = [ - { - "name": "GROUPE", - "color_code": "#FF0000" - }, - { - "name": "MATHS", - "color_code": "#0a98f0" - }, - { - "name": "ANGLAIS", - "color_code": "#f708d7" - }, - { - "name": "FRANCAIS", - "color_code": "#04f108" - }, - { - "name": "HISTOIRE", - "color_code": "#ffb005" - }, - { - "name": "SPORT", - "color_code": "#bbb9b9" - } - ] + specialities_data = self.load_data('specialities.json') for speciality_data in specialities_data: Speciality.objects.update_or_create( @@ -259,42 +150,15 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS('Specialities initialized or updated successfully')) def create_or_update_teachers(self): - teachers_data = [ - { - "last_name": "DUMBLEDORE", - "first_name": "Albus", - "email": "albus.dumbledore@gmail.com", - "specialities": ["GROUPE"], - "droit": 1 - }, - { - "last_name": "ROGUE", - "first_name": "Severus", - "email": "severus.rogue@gmail.com", - "specialities": ["ANGLAIS"], - "droit": 0 - }, - { - "last_name": "MC GONAGALL", - "first_name": "Minerva", - "email": "minerva.mcgonagall@gmail.com", - "specialities": ["MATHS", "HISTOIRE"], - "droit": 0 - }, - { - "last_name": "CHOURAVE", - "first_name": "Pomona", - "email": "pomona.chourave@gmail.com", - "specialities": ["MATHS", "FRANCAIS", "SPORT"], - "droit": 0 - } - ] + teachers_data = self.load_data('teachers.json') for teacher_data in teachers_data: specialities = teacher_data.pop("specialities") email = teacher_data["email"] droit = teacher_data.pop("droit") + establishment = random.choice(self.establishments) + # Create or update the user profile user, created = Profile.objects.update_or_create( email=email, @@ -303,7 +167,8 @@ class Command(BaseCommand): "email": email, "is_active": True, "password": "Provisoire01!", - "droit": droit + "droit": droit, + "establishment": establishment } ) if created: @@ -321,51 +186,13 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS('Teachers initialized or updated successfully')) def create_or_update_school_classes(self): - establishment = Establishment.objects.get(name="N3WT") - school_classes_data = [ - { - "atmosphere_name": "Classe A", - "age_range": "3-6", - "number_of_students": 14, - "teaching_language": "", - "school_year": "2024-2025", - "levels": [2, 3, 4], - "type": 1, - "time_range": ["08:30", "17:30"], - "opening_days": [1, 2, 4, 5], - "teachers": [2], # ID of Severus Rogue - "establishment": establishment - }, - { - "atmosphere_name": "Classe B", - "age_range": "2-3", - "number_of_students": 5, - "teaching_language": "", - "school_year": "2024-2025", - "levels": [1], - "type": 1, - "time_range": ["08:30", "17:30"], - "opening_days": [1, 2, 4, 5], - "teachers": [3], # ID of Minerva McGonagall - "establishment": establishment - }, - { - "atmosphere_name": "Classe C", - "age_range": "6-12", - "number_of_students": 21, - "teaching_language": "", - "school_year": "2024-2025", - "levels": [5, 6, 7, 8, 9], - "type": 1, - "time_range": ["08:30", "17:30"], - "opening_days": [1, 2, 4, 5], - "teachers": [4], # ID of Pomona Chourave - "establishment": establishment - } - ] + school_classes_data = self.load_data('school_classes.json') - for class_data in school_classes_data: + for index, class_data in enumerate(school_classes_data, start=1): teachers_ids = class_data.pop("teachers") + establishment = random.choice(self.establishments) + class_data["atmosphere_name"] = f"Classe {index} - {establishment.name}" + class_data["establishment"] = establishment school_class, created = SchoolClass.objects.update_or_create( atmosphere_name=class_data["atmosphere_name"], school_year=class_data["school_year"], @@ -377,33 +204,29 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS('SchoolClasses initialized or updated successfully')) def create_or_update_registration_file_group(self): - group_data_1 = { - "name": "LMDE", - "description": "Fichiers d'inscription de l'école LMDE" - } + for establishment in self.establishments: + group_data = { + "name": f"Fichiers d'inscription - {establishment.name}", + "description": f"Fichiers d'inscription de l'école {establishment.name}" + } - self.registration_file_group_1, created = RegistrationFileGroup.objects.get_or_create(name=group_data_1["name"], defaults=group_data_1) - self.stdout.write(self.style.SUCCESS('RegistrationFileGroup 1 initialized or updated successfully')) - - group_data_2 = { - "name": "LMDE 2", - "description": "Fichiers d'inscription de l'école LMDE 2" - } - - self.registration_file_group_2, created = RegistrationFileGroup.objects.get_or_create(name=group_data_2["name"], defaults=group_data_2) - self.stdout.write(self.style.SUCCESS('RegistrationFileGroup 2 initialized or updated successfully')) + RegistrationFileGroup.objects.update_or_create(name=group_data["name"], defaults=group_data) + self.stdout.write(self.style.SUCCESS(f'RegistrationFileGroup for {establishment.name} initialized or updated successfully')) def create_register_form(self): fake = Faker('fr_FR') # Utiliser le locale français pour Faker for _ in range(50): + establishment = random.choice(self.establishments) + # Générer des données fictives pour le profil profile_data = { "email": fake.email(), "droit": 2, "username": fake.user_name(), "is_active": True, - "password": "Provisoire01!" + "password": "Provisoire01!", + "establishment": establishment } user, created = Profile.objects.update_or_create( @@ -412,7 +235,8 @@ class Command(BaseCommand): "username": profile_data["username"], "email": profile_data["email"], "is_active": profile_data["is_active"], - "droit": profile_data["droit"] + "droit": profile_data["droit"], + "establishment": profile_data["establishment"] } ) if created: @@ -427,7 +251,7 @@ class Command(BaseCommand): # Générer des données fictives pour l'étudiant student_data = { - "last_name": fake.last_name(), + "last_name": f"{fake.last_name()} - {establishment.name}", "first_name": fake.first_name(), "address": fake.address(), "birth_date": fake.date_of_birth(), @@ -457,8 +281,8 @@ class Command(BaseCommand): # Créer les données du formulaire d'inscription register_form_data = { "student": student, - "fileGroup": self.registration_file_group_1, - "establishment": Establishment.objects.get(id=1), + "fileGroup": RegistrationFileGroup.objects.get(name=f"Fichiers d'inscription - {establishment.name}"), + "establishment": establishment, "status": fake.random_int(min=1, max=3) } @@ -467,7 +291,7 @@ class Command(BaseCommand): register_form.fees.set(fees) register_form.discounts.set(discounts) if not created: - register_form.fileGroup = self.registration_file_group_1 + register_form.fileGroup = RegistrationFileGroup.objects.get(name=f"Fichiers d'inscription - {establishment.name}") register_form.save() self.stdout.write(self.style.SUCCESS('50 RegistrationForms initialized or updated successfully')) \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/discounts.json b/Back-End/School/management/mock_datas/discounts.json new file mode 100644 index 0000000..9643c41 --- /dev/null +++ b/Back-End/School/management/mock_datas/discounts.json @@ -0,0 +1,16 @@ +[ + { + "name": "Parrainage", + "amount": "10.00", + "description": "Réduction pour parrainage", + "discount_type": 1, + "type": 1 + }, + { + "name": "Réinscription", + "amount": "100.00", + "description": "Réduction pour Réinscription", + "discount_type": 1, + "type": 0 + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/establishments.json b/Back-End/School/management/mock_datas/establishments.json new file mode 100644 index 0000000..99a8db5 --- /dev/null +++ b/Back-End/School/management/mock_datas/establishments.json @@ -0,0 +1,23 @@ +[ + { + "name": "Ecole A", + "address": "Adresse de l'Ecole A", + "total_capacity": 69, + "establishment_type": [1, 2], + "licence_code": "" + }, + { + "name": "Ecole B", + "address": "Adresse de l'Ecole B", + "total_capacity": 100, + "establishment_type": [2, 3], + "licence_code": "" + }, + { + "name": "Ecole C", + "address": "Adresse de l'Ecole C", + "total_capacity": 50, + "establishment_type": [1], + "licence_code": "" + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/fees.json b/Back-End/School/management/mock_datas/fees.json new file mode 100644 index 0000000..b54affc --- /dev/null +++ b/Back-End/School/management/mock_datas/fees.json @@ -0,0 +1,37 @@ +[ + { + "name": "Frais d'inscription", + "base_amount": "150.00", + "description": "Montant de base", + "is_active": true, + "type": 0 + }, + { + "name": "Matériel", + "base_amount": "85.00", + "description": "Livres / jouets", + "is_active": true, + "type": 0 + }, + { + "name": "Sorties périscolaires", + "base_amount": "120.00", + "description": "Sorties", + "is_active": true, + "type": 0 + }, + { + "name": "Les colibris", + "base_amount": "4500.00", + "description": "TPS / PS / MS / GS", + "is_active": true, + "type": 1 + }, + { + "name": "Les butterflies", + "base_amount": "5000.00", + "description": "CP / CE1 / CE2 / CM1 / CM2", + "is_active": true, + "type": 1 + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/payment_modes.json b/Back-End/School/management/mock_datas/payment_modes.json new file mode 100644 index 0000000..fd79623 --- /dev/null +++ b/Back-End/School/management/mock_datas/payment_modes.json @@ -0,0 +1,12 @@ +[ + { + "mode": 4, + "type": 0, + "is_active": true + }, + { + "mode": 2, + "type": 1, + "is_active": true + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/payment_plans.json b/Back-End/School/management/mock_datas/payment_plans.json new file mode 100644 index 0000000..96b5a5c --- /dev/null +++ b/Back-End/School/management/mock_datas/payment_plans.json @@ -0,0 +1,22 @@ +[ + { + "frequency": 1, + "type": 0, + "is_active": true + }, + { + "frequency": 3, + "type": 1, + "is_active": true + }, + { + "frequency": 10, + "type": 1, + "is_active": true + }, + { + "frequency": 12, + "type": 1, + "is_active": true + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/school_classes.json b/Back-End/School/management/mock_datas/school_classes.json new file mode 100644 index 0000000..eed817c --- /dev/null +++ b/Back-End/School/management/mock_datas/school_classes.json @@ -0,0 +1,57 @@ +[ + { + "age_range": "3-6", + "number_of_students": 14, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [2, 3, 4], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5], + "teachers": [2] + }, + { + "age_range": "2-3", + "number_of_students": 5, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [1], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5], + "teachers": [3] + }, + { + "age_range": "6-12", + "number_of_students": 21, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [5, 6, 7, 8, 9], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5], + "teachers": [4] + }, + { + "age_range": "4-6", + "number_of_students": 18, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [4, 5], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5], + "teachers": [1] + }, + { + "age_range": "7-9", + "number_of_students": 20, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [6, 7], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5], + "teachers": [2] + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/specialities.json b/Back-End/School/management/mock_datas/specialities.json new file mode 100644 index 0000000..75707d9 --- /dev/null +++ b/Back-End/School/management/mock_datas/specialities.json @@ -0,0 +1,26 @@ +[ + { + "name": "GROUPE", + "color_code": "#FF0000" + }, + { + "name": "MATHS", + "color_code": "#0a98f0" + }, + { + "name": "ANGLAIS", + "color_code": "#f708d7" + }, + { + "name": "FRANCAIS", + "color_code": "#04f108" + }, + { + "name": "HISTOIRE", + "color_code": "#ffb005" + }, + { + "name": "SPORT", + "color_code": "#bbb9b9" + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/teachers.json b/Back-End/School/management/mock_datas/teachers.json new file mode 100644 index 0000000..2318577 --- /dev/null +++ b/Back-End/School/management/mock_datas/teachers.json @@ -0,0 +1,30 @@ +[ + { + "last_name": "DUMBLEDORE", + "first_name": "Albus", + "email": "albus.dumbledore@gmail.com", + "specialities": ["GROUPE"], + "droit": 1 + }, + { + "last_name": "ROGUE", + "first_name": "Severus", + "email": "severus.rogue@gmail.com", + "specialities": ["ANGLAIS"], + "droit": 1 + }, + { + "last_name": "MC GONAGALL", + "first_name": "Minerva", + "email": "minerva.mcgonagall@gmail.com", + "specialities": ["MATHS", "HISTOIRE"], + "droit": 1 + }, + { + "last_name": "CHOURAVE", + "first_name": "Pomona", + "email": "pomona.chourave@gmail.com", + "specialities": ["MATHS", "FRANCAIS", "SPORT"], + "droit": 1 + } +] \ No newline at end of file diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index 89efee9..d9fdc60 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -3,8 +3,7 @@ from django.utils.timezone import now from django.conf import settings from django.utils.translation import gettext_lazy as _ -from Auth.models import Profile -from School.models import SchoolClass, Fee, Discount, Establishment +from School.models import SchoolClass, Fee, Discount from datetime import datetime @@ -29,7 +28,7 @@ class Guardian(models.Model): email = models.CharField(max_length=200, default="", blank=True) phone = models.CharField(max_length=200, default="", blank=True) profession = models.CharField(max_length=200, default="", blank=True) - associated_profile = models.ForeignKey(Profile, on_delete=models.CASCADE) + associated_profile = models.ForeignKey('Auth.Profile', on_delete=models.CASCADE) def __str__(self): return self.last_name + "_" + self.first_name @@ -80,7 +79,7 @@ class Student(models.Model): payment_method = models.IntegerField(choices=PaymentMethod, default=PaymentMethod.NONE, blank=True) # Many-to-Many Relationship - profiles = models.ManyToManyField(Profile, blank=True) + profiles = models.ManyToManyField('Auth.Profile', blank=True) # Many-to-Many Relationship guardians = models.ManyToManyField(Guardian, blank=True) @@ -215,7 +214,7 @@ class RegistrationForm(models.Model): null=True, blank=True) - establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='register_forms') + establishment = models.ForeignKey('School.Establishment', on_delete=models.CASCADE, related_name='register_forms') def __str__(self): return "RF_" + self.student.last_name + "_" + self.student.first_name diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py index 1b11bec..f47ea89 100644 --- a/Back-End/Subscriptions/views/register_form_views.py +++ b/Back-End/Subscriptions/views/register_form_views.py @@ -39,6 +39,7 @@ class RegisterFormView(APIView): openapi.Parameter('filter', openapi.IN_QUERY, description="filtre", type=openapi.TYPE_STRING, enum=['pending', 'archived', 'subscribed'], required=True), openapi.Parameter('search', openapi.IN_QUERY, description="search", type=openapi.TYPE_STRING, required=False), openapi.Parameter('page_size', openapi.IN_QUERY, description="limite de page lors de la pagination", type=openapi.TYPE_INTEGER, required=False), + openapi.Parameter('establishment_id', openapi.IN_QUERY, description="ID de l'établissement", type=openapi.TYPE_INTEGER, required=True), ], responses={200: RegistrationFormSerializer(many=True)}, operation_description="Récupère les dossier d'inscriptions en fonction du filtre passé.", @@ -78,6 +79,7 @@ class RegisterFormView(APIView): filter = request.GET.get('filter', '').strip() search = request.GET.get('search', '').strip() page_size = request.GET.get('page_size', None) + establishment_id = request.GET.get('establishment_id', None) # Gestion du page_size if page_size is not None: @@ -88,7 +90,7 @@ class RegisterFormView(APIView): # Définir le cache_key en fonction du filtre page_number = request.GET.get('page', 1) - cache_key = f'N3WT_ficheInscriptions_{filter}_page_{page_number}_search_{search if filter == "pending" else ""}' + cache_key = f'N3WT_ficheInscriptions_{establishment_id}_{filter}_page_{page_number}_search_{search if filter == "pending" else ""}' cached_page = cache.get(cache_key) if cached_page: return JsonResponse(cached_page, safe=False) @@ -105,6 +107,10 @@ class RegisterFormView(APIView): else: registerForms_List = None + if registerForms_List: + print(f'filtrate sur lestablishment : {establishment_id}') + registerForms_List = registerForms_List.filter(establishment=establishment_id) + if not registerForms_List: return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False) diff --git a/Back-End/Subscriptions/views/student_views.py b/Back-End/Subscriptions/views/student_views.py index a84a65a..d20c671 100644 --- a/Back-End/Subscriptions/views/student_views.py +++ b/Back-End/Subscriptions/views/student_views.py @@ -46,11 +46,23 @@ class StudentListView(APIView): operation_description="Retourne la liste de tous les élèves inscrits ou en cours d'inscription", responses={ 200: openapi.Response('Liste des élèves', StudentByRFCreationSerializer(many=True)) - } + }, + manual_parameters=[ + openapi.Parameter( + 'establishment_id', openapi.IN_QUERY, + description="ID de l'établissement", + type=openapi.TYPE_INTEGER, + required=True + ) + ] ) # Récupération de la liste des élèves inscrits ou en cours d'inscriptions def get(self, request): - students = bdd.getAllObjects(_objectName=Student) + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + students = Student.objects.filter(registrationform__establishment_id=establishment_id) students_serializer = StudentByRFCreationSerializer(students, many=True) return JsonResponse(students_serializer.data, safe=False) diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js index 5e68335..f87377b 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -15,6 +15,7 @@ import { MoreVertical, Send, Edit, Trash2, FileText, CheckCircle, Plus } from ' import Modal from '@/components/Modal'; import InscriptionForm from '@/components/Inscription/InscriptionForm' import AffectationClasseForm from '@/components/AffectationClasseForm' +import { getSession } from 'next-auth/react'; import { PENDING, @@ -52,8 +53,6 @@ import { useCsrfToken } from '@/context/CsrfContext'; import { ESTABLISHMENT_ID } from '@/utils/Url'; import logger from '@/utils/logger'; -const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true'; - export default function Page({ params: { locale } }) { const t = useTranslations('subscriptions'); const [registrationForms, setRegistrationForms] = useState([]); @@ -88,6 +87,8 @@ export default function Page({ params: { locale } }) { const [tuitionFees, setTuitionFees] = useState([]); const [groups, setGroups] = useState([]); + const [establishmentId, setEstablishmentId] = useState(null); + const csrfToken = useCsrfToken(); const openModal = () => { @@ -166,115 +167,131 @@ const registerFormArchivedDataHandler = (data) => { } useEffect(() => { - const fetchInitialData = async () => { - try { - const [classesData, studentsData] = await Promise.all([ - fetchClasses(), - fetchStudents() - ]); + getSession() + .then(session => { + if (session && session.user) { + setEstablishmentId(session.user.establishment); + } + }) + .catch(err => { + logger.error('Error fetching session:', err); + }); +}, []); - setClasses(classesData); - setEleves(studentsData); - logger.debug('Success - Classes:', classesData); - logger.debug('Success - Students:', studentsData); - } catch (error) { - logger.error('Error fetching initial data:', error); - } +useEffect(() => { + if (establishmentId) { + const fetchInitialData = () => { + Promise.all([ + fetchClasses(), + fetchStudents(establishmentId) // Utiliser l'ID de l'établissement ici + ]) + .then(([classesData, studentsData]) => { + setClasses(classesData); + setEleves(studentsData); + logger.debug('Success - Classes:', classesData); + logger.debug('Success - Students:', studentsData); + }) + .catch(error => { + logger.error('Error fetching initial data:', error); + }); }; fetchInitialData(); -}, []); + } +}, [establishmentId]); - useEffect(() => { - const fetchDataAndSetState = () => { - - setIsLoading(true); - if (!useFakeData) { - fetchRegisterForms(PENDING, currentPage, itemsPerPage, searchTerm) - .then(registerFormPendingDataHandler) - .catch(requestErrorHandler) - fetchRegisterForms(SUBSCRIBED) - .then(registerFormSubscribedDataHandler) - .catch(requestErrorHandler) - fetchRegisterForms(ARCHIVED) - .then(registerFormArchivedDataHandler) - .catch(requestErrorHandler) - fetchRegistrationTemplateMaster() - .then((data)=> { - logger.debug(data); - - setTemplateMasters(data) - }) - .catch((err)=>{ err = err.message; logger.debug(err);}) - fetchRegistrationDiscounts() - .then(data => { - setRegistrationDiscounts(data); - }) - .catch(requestErrorHandler) - fetchTuitionDiscounts() - .then(data => { - setTuitionDiscounts(data); - }) - .catch(requestErrorHandler) - fetchRegistrationFees() - .then(data => { - setRegistrationFees(data); - }) - .catch(requestErrorHandler) - fetchTuitionFees() - .then(data => { - setTuitionFees(data); - }) - .catch(requestErrorHandler); - fetchRegistrationFileGroups() - .then(data => { - setGroups(data); - }) - .catch(error => logger.error('Error fetching file groups:', error)); - } else { - setTimeout(() => { - setRegistrationFormsDataPending(mockFicheInscription); - }, 1000); - } - setIsLoading(false); - setReloadFetch(false); - }; - - - fetchDataAndSetState(); - }, [reloadFetch, currentPage]); - useEffect(() => { + if (establishmentId) { + const fetchDataAndSetState = () => { + setIsLoading(true); + Promise.all([ + fetchRegisterForms(establishmentId, PENDING, currentPage, itemsPerPage, searchTerm) + .then(registerFormPendingDataHandler) + .catch(requestErrorHandler), + fetchRegisterForms(establishmentId, SUBSCRIBED) + .then(registerFormSubscribedDataHandler) + .catch(requestErrorHandler), + fetchRegisterForms(establishmentId, ARCHIVED) + .then(registerFormArchivedDataHandler) + .catch(requestErrorHandler), + fetchRegistrationTemplateMaster() + .then(data => { + setTemplateMasters(data); + }) + .catch(err => { + logger.debug(err.message); + }), + fetchRegistrationDiscounts() + .then(data => { + setRegistrationDiscounts(data); + }) + .catch(requestErrorHandler), + fetchTuitionDiscounts() + .then(data => { + setTuitionDiscounts(data); + }) + .catch(requestErrorHandler), + fetchRegistrationFees() + .then(data => { + setRegistrationFees(data); + }) + .catch(requestErrorHandler), + fetchTuitionFees() + .then(data => { + setTuitionFees(data); + }) + .catch(requestErrorHandler), + fetchRegistrationFileGroups() + .then(data => { + setGroups(data); + }) + .catch(error => { + logger.error('Error fetching file groups:', error); + }) + ]) + .then(() => { + setIsLoading(false); + setReloadFetch(false); + }) + .catch(err => { + logger.error(err); + setIsLoading(false); + setReloadFetch(false); + }); + }; + + fetchDataAndSetState(); + } +}, [establishmentId, reloadFetch, currentPage, searchTerm]); + +useEffect(() => { + if (establishmentId) { const fetchDataAndSetState = () => { setIsLoading(true); - if (!useFakeData) { - fetchRegisterForms(PENDING, currentPage, itemsPerPage, searchTerm) - .then(registerFormPendingDataHandler) - .catch(requestErrorHandler) - fetchRegisterForms(SUBSCRIBED) - .then(registerFormSubscribedDataHandler) - .catch(requestErrorHandler) - fetchRegisterForms(ARCHIVED) - .then(registerFormArchivedDataHandler) - .catch(requestErrorHandler) - fetchRegistrationTemplateMaster() - .then((data)=> {setTemplateMasters(data)}) - .catch((err)=>{ err = err.message; logger.debug(err);}); - } else { - setTimeout(() => { - setRegistrationFormsDataPending(mockFicheInscription); - }, 1000); - } + fetchRegisterForms(establishmentId, PENDING, currentPage, itemsPerPage, searchTerm) + .then(registerFormPendingDataHandler) + .catch(requestErrorHandler) + fetchRegisterForms(establishmentId, SUBSCRIBED) + .then(registerFormSubscribedDataHandler) + .catch(requestErrorHandler) + fetchRegisterForms(establishmentId, ARCHIVED) + .then(registerFormArchivedDataHandler) + .catch(requestErrorHandler) + fetchRegistrationTemplateMaster() + .then((data)=> {setTemplateMasters(data)}) + .catch((err)=>{ err = err.message; logger.debug(err);}); + setIsLoading(false); setReloadFetch(false); - }; + } -const timeoutId = setTimeout(() => { - fetchDataAndSetState(); - }, 500); // Debounce la recherche - return () => clearTimeout(timeoutId); + const timeoutId = setTimeout(() => { + fetchDataAndSetState(); + }, 500); // Debounce la recherche + return () => clearTimeout(timeoutId); + } }, [searchTerm]); /** diff --git a/Front-End/src/app/[locale]/users/login/page.js b/Front-End/src/app/[locale]/users/login/page.js index fc8ce7c..8677507 100644 --- a/Front-End/src/app/[locale]/users/login/page.js +++ b/Front-End/src/app/[locale]/users/login/page.js @@ -53,6 +53,11 @@ export default function Page() { } const user = session.user; logger.debug('User Session:', user); + + if (user.establishment_id) { + localStorage.setItem('establishment_id', user.establishment_id); + } + if (user.droit === 0) { // Vue ECOLE } else if (user.droit === 1) { diff --git a/Front-End/src/app/actions/subscriptionAction.js b/Front-End/src/app/actions/subscriptionAction.js index bcda2bc..aaa328a 100644 --- a/Front-End/src/app/actions/subscriptionAction.js +++ b/Front-End/src/app/actions/subscriptionAction.js @@ -22,10 +22,10 @@ const requestResponseHandler = async (response) => { throw error; } -export const fetchRegisterForms = (filter=PENDING, page='', pageSize='', search = '') => { - let url = `${BE_SUBSCRIPTION_REGISTERFORMS_URL}?filter=${filter}`; +export const fetchRegisterForms = (establishment, filter=PENDING, page='', pageSize='', search = '') => { + let url = `${BE_SUBSCRIPTION_REGISTERFORMS_URL}?filter=${filter}&establishment_id=${establishment}`; if (page !== '' && pageSize !== '') { - url = `${BE_SUBSCRIPTION_REGISTERFORMS_URL}?filter=${filter}&page=${page}&search=${search}`; + url = `${BE_SUBSCRIPTION_REGISTERFORMS_URL}?filter=${filter}&establishment_id=${establishment}&page=${page}&search=${search}`; } return fetch(url, { headers: { @@ -99,8 +99,8 @@ export const archiveRegisterForm = (id) => { }).then(requestResponseHandler) } -export const fetchStudents = (id) => { - const url = (id)?`${BE_SUBSCRIPTION_STUDENTS_URL}/${id}`:`${BE_SUBSCRIPTION_STUDENTS_URL}`; +export const fetchStudents = (id=null, establishment) => { + const url = (id)?`${BE_SUBSCRIPTION_STUDENTS_URL}/${id}`:`${BE_SUBSCRIPTION_STUDENTS_URL}?establishment_id=${establishment}`; const request = new Request( url, { diff --git a/Front-End/src/pages/api/auth/[...nextauth].js b/Front-End/src/pages/api/auth/[...nextauth].js index 616f9bd..5ae8993 100644 --- a/Front-End/src/pages/api/auth/[...nextauth].js +++ b/Front-End/src/pages/api/auth/[...nextauth].js @@ -88,7 +88,7 @@ const options = { }, async session({ session, token }) { if (token && token?.token) { - const {user_id, droit, email} = jwt_decode.decode(token.token); + const {user_id, droit, email, establishment} = jwt_decode.decode(token.token); session.user = { ...session.user, token: token.token, @@ -97,6 +97,7 @@ const options = { session.user.user_id = user_id; session.user.droit = droit; session.user.email = email; + session.user.establishment = establishment; } return session; }