feat: Gestion multi-profil multi-école

This commit is contained in:
N3WT DE COMPET
2025-03-09 16:22:28 +01:00
parent 95c154a4a2
commit 16178296ec
51 changed files with 1621 additions and 802 deletions

View File

@ -2,25 +2,31 @@ from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.core.validators import EmailValidator
from Establishment.models import Establishment
class Profile(AbstractUser):
class Droits(models.IntegerChoices):
PROFIL_UNDEFINED = -1, _('NON DEFINI')
PROFIL_ECOLE = 0, _('ECOLE')
PROFIL_ADMIN = 1, _('ADMIN')
PROFIL_PARENT = 2, _('PARENT')
email = models.EmailField(max_length=255, unique=True, default="", validators=[EmailValidator()])
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ('password', )
code = models.CharField(max_length=200, default="", blank=True)
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)
return self.email
class ProfileRole(models.Model):
class RoleType(models.IntegerChoices):
PROFIL_UNDEFINED = -1, _('NON DEFINI')
PROFIL_ECOLE = 0, _('ECOLE')
PROFIL_ADMIN = 1, _('ADMIN')
PROFIL_PARENT = 2, _('PARENT')
profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='roles')
role_type = models.IntegerField(choices=RoleType.choices, default=RoleType.PROFIL_UNDEFINED)
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='profile_roles')
is_active = models.BooleanField(default=False)
def __str__(self):
return f"{self.profile.email} - {self.get_role_type_display()} - {self.establishment.name}"

View File

@ -1,48 +1,97 @@
from rest_framework import serializers
from Auth.models import Profile
from Auth.models import Profile, ProfileRole
from django.core.exceptions import ValidationError
class ProfileRoleSerializer(serializers.ModelSerializer):
class Meta:
model = ProfileRole
fields = ['role_type', 'establishment', 'is_active', 'profile']
class ProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
password = serializers.CharField(write_only=True)
roles = ProfileRoleSerializer(many=True, required=False)
class Meta:
model = Profile
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active', 'establishment']
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
roles_data = validated_data.pop('roles', [])
user = Profile(
username=validated_data['username'],
email=validated_data['email'],
is_active=validated_data['is_active'],
droit=validated_data['droit'],
establishment=validated_data.get('establishment')
code=validated_data.get('code', ''),
datePeremption=validated_data.get('datePeremption', '')
)
user.set_password(validated_data['password'])
user.full_clean()
user.save()
for role_data in roles_data:
ProfileRole.objects.create(profile=user, **role_data)
return user
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['password'] = '********'
return ret
class ProfilUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active']
extra_kwargs = {
'password': {'write_only': True, 'required': False}
}
def update(self, instance, validated_data):
roles_data = validated_data.pop('roles', [])
password = validated_data.pop('password', None)
instance = super().update(instance, validated_data)
if password:
instance.set_password(password)
instance.save()
instance.full_clean()
instance.save()
for role_data in roles_data:
ProfileRole.objects.update_or_create(
profile=instance,
establishment_id=role_data.get('establishment_id'),
defaults={
'role_type': role_data.get('role_type'),
'is_active': role_data.get('is_active', True)
}
)
return instance
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['password'] = '********'
return ret
class ProfilUpdateSerializer(serializers.ModelSerializer):
roles = ProfileRoleSerializer(many=True, required=False)
class Meta:
model = Profile
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles']
extra_kwargs = {
'password': {'write_only': True, 'required': False}
}
def update(self, instance, validated_data):
roles_data = validated_data.pop('roles', [])
password = validated_data.pop('password', None)
instance = super().update(instance, validated_data)
if password:
instance.set_password(password)
instance.full_clean()
instance.save()
for role_data in roles_data:
ProfileRole.objects.update_or_create(
profile=instance,
establishment_id=role_data.get('establishment_id'),
defaults={
'role_type': role_data.get('role_type'),
'is_active': role_data.get('is_active', True)
}
)
return instance

View File

@ -19,7 +19,7 @@ from jwt.exceptions import ExpiredSignatureError, InvalidTokenError
import json
from . import validator
from .models import Profile
from .models import Profile, ProfileRole
from rest_framework.decorators import action, api_view
from Auth.serializers import ProfileSerializer, ProfilUpdateSerializer
@ -56,7 +56,10 @@ class SessionView(APIView):
'user': openapi.Schema(type=openapi.TYPE_OBJECT, properties={
'id': openapi.Schema(type=openapi.TYPE_INTEGER),
'email': openapi.Schema(type=openapi.TYPE_STRING),
'role': openapi.Schema(type=openapi.TYPE_STRING)
'roles': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Items(type=openapi.TYPE_OBJECT, properties={
'role_type': openapi.Schema(type=openapi.TYPE_STRING),
'establishment': openapi.Schema(type=openapi.TYPE_STRING)
}))
})
})),
401: openapi.Response('Session invalide')
@ -67,15 +70,16 @@ class SessionView(APIView):
try:
decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
print(f'decode : {decoded_token}')
userid = decoded_token.get('id')
userid = decoded_token.get('user_id')
user = Profile.objects.get(id=userid)
roles = ProfileRole.objects.filter(profile=user).values('role_type', 'establishment__name')
response_data = {
'user': {
'id': user.id,
'email': user.email,
'role': user.droit, # Assure-toi que le champ 'droit' existe et contient le rôle
'roles': list(roles)
}
}
return JsonResponse(response_data, status=status.HTTP_200_OK)
@ -103,13 +107,11 @@ class ProfileView(APIView):
}
)
def post(self, request):
profil_data=JSONParser().parse(request)
print(f'{profil_data}')
profil_data = JSONParser().parse(request)
profil_serializer = ProfileSerializer(data=profil_data)
if profil_serializer.is_valid():
profil_serializer.save()
profil = profil_serializer.save()
return JsonResponse(profil_serializer.data, safe=False)
return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@ -122,8 +124,8 @@ class ProfileSimpleView(APIView):
responses={200: ProfileSerializer}
)
def get(self, request, id):
profil=bdd.getObject(Profile, "id", id)
profil_serializer=ProfileSerializer(profil)
profil = bdd.getObject(Profile, "id", id)
profil_serializer = ProfileSerializer(profil)
return JsonResponse(profil_serializer.data, safe=False)
@swagger_auto_schema(
@ -135,12 +137,12 @@ class ProfileSimpleView(APIView):
}
)
def put(self, request, id):
data=JSONParser().parse(request)
data = JSONParser().parse(request)
profil = Profile.objects.get(id=id)
profil_serializer = ProfilUpdateSerializer(profil, data=data)
if profil_serializer.is_valid():
profil_serializer.save()
return JsonResponse("Updated Successfully", safe=False)
return JsonResponse(profil_serializer.data, safe=False)
return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@ -157,10 +159,11 @@ class LoginView(APIView):
operation_description="Connexion utilisateur",
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['email', 'password'],
required=['email', 'password', 'role_type'],
properties={
'email': openapi.Schema(type=openapi.TYPE_STRING),
'password': openapi.Schema(type=openapi.TYPE_STRING)
'password': openapi.Schema(type=openapi.TYPE_STRING),
'role_type': openapi.Schema(type=openapi.TYPE_STRING)
}
),
responses={
@ -193,40 +196,45 @@ class LoginView(APIView):
password=data.get('password'),
)
if user is not None:
if user.is_active:
login(request, user)
user.estConnecte = True
user.save()
clear_cache()
retour = ''
# Générer le JWT avec la bonne syntaxe datetime
access_payload = {
'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(),
}
role_type = data.get('role_type')
primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type, is_active=True).first()
access_token = jwt.encode(access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
# Générer le Refresh Token (exp: 7 jours)
refresh_payload = {
'user_id': user.id,
'type': 'refresh',
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
'iat': datetime.utcnow(),
}
refresh_token = jwt.encode(refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
if not primary_role:
return JsonResponse({"errorMessage": "Role not assigned to the user"}, status=status.HTTP_401_UNAUTHORIZED)
return JsonResponse({
'token': access_token,
'refresh': refresh_token
}, safe=False)
login(request, user)
user.save()
clear_cache()
retour = ''
# Récupérer tous les rôles de l'utilisateur avec le type spécifié
roles = ProfileRole.objects.filter(profile=user, role_type=role_type).values('role_type', 'establishment__id', 'establishment__name')
# Générer le JWT avec la bonne syntaxe datetime
access_payload = {
'user_id': user.id,
'email': user.email,
'roles': list(roles),
'type': 'access',
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
'iat': datetime.utcnow(),
}
access_token = jwt.encode(access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
# Générer le Refresh Token (exp: 7 jours)
refresh_payload = {
'user_id': user.id,
'type': 'refresh',
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
'iat': datetime.utcnow(),
}
refresh_token = jwt.encode(refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
return JsonResponse({
'token': access_token,
'refresh': refresh_token
}, safe=False)
else:
retour = error.returnMessage[error.PROFIL_INACTIVE]
else:
retour = error.returnMessage[error.WRONG_ID]
@ -235,7 +243,6 @@ class LoginView(APIView):
'errorMessage': retour,
}, safe=False, status=status.HTTP_400_BAD_REQUEST)
class RefreshJWTView(APIView):
@swagger_auto_schema(
operation_description="Rafraîchir le token d'accès",
@ -295,13 +302,20 @@ class RefreshJWTView(APIView):
# Récupérer les informations utilisateur
user = Profile.objects.get(id=payload['user_id'])
role_type = payload.get('role_type')
# Récupérer le rôle principal de l'utilisateur
primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type).first()
if not primary_role:
return JsonResponse({'errorMessage': 'No role assigned to the user'}, status=400)
# Générer un nouveau Access Token avec les informations complètes
new_access_payload = {
'user_id': user.id,
'email': user.email,
'droit': user.droit,
'establishment': user.establishment.id,
'role_type': primary_role.get_role_type_display(),
'establishment': primary_role.establishment.id,
'type': 'access',
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
'iat': datetime.utcnow(),
@ -311,6 +325,7 @@ class RefreshJWTView(APIView):
new_refresh_payload = {
'user_id': user.id,
'role_type': role_type,
'type': 'refresh',
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
'iat': datetime.utcnow(),
@ -335,6 +350,14 @@ class RefreshJWTView(APIView):
class SubscribeView(APIView):
@swagger_auto_schema(
operation_description="Inscription utilisateur",
manual_parameters=[
openapi.Parameter(
'establishment_id', openapi.IN_QUERY,
description="ID de l'établissement",
type=openapi.TYPE_INTEGER,
required=True
)
],
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['email', 'password1', 'password2'],
@ -359,37 +382,54 @@ class SubscribeView(APIView):
def post(self, request):
retourErreur = error.returnMessage[error.BAD_URL]
retour = ''
newProfilConnection=JSONParser().parse(request)
newProfilConnection = JSONParser().parse(request)
establishment_id = request.GET.get('establishment_id')
if not establishment_id:
return JsonResponse({'message': retour, 'errorMessage': 'establishment_id manquant', "errorFields": {}, "id": -1}, safe=False, status=status.HTTP_400_BAD_REQUEST)
validatorSubscription = validator.ValidatorSubscription(data=newProfilConnection)
validationOk, errorFields = validatorSubscription.validate()
if validationOk:
# On vérifie que l'email existe : si ce n'est pas le cas, on retourne une erreur
profil = bdd.getProfile(Profile.objects.all(), newProfilConnection.get('email'))
if profil == None:
if profil is None:
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
else:
if profil.is_active:
retourErreur=error.returnMessage[error.PROFIL_ACTIVE]
return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields, "id":profil.id}, safe=False)
# Vérifier si le profil a déjà un rôle actif pour l'établissement donné
active_roles = ProfileRole.objects.filter(profile=profil, establishment_id=establishment_id, is_active=True)
if active_roles.exists():
retourErreur = error.returnMessage[error.PROFIL_ACTIVE]
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False)
else:
try:
profil.set_password(newProfilConnection.get('password1'))
profil.is_active = True
profil.full_clean()
profil.save()
# Utiliser le sérialiseur ProfileRoleSerializer pour créer ou mettre à jour le rôle
role_data = {
'profile': profil.id,
'establishment_id': establishment_id,
'role_type': ProfileRole.RoleType.PROFIL_PARENT,
'is_active': True
}
role_serializer = ProfileRoleSerializer(data=role_data)
if role_serializer.is_valid():
role_serializer.save()
else:
return JsonResponse(role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
clear_cache()
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
retourErreur=''
return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields, "id":profil.id}, safe=False)
retourErreur = ''
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False)
except ValidationError as e:
retourErreur = error.returnMessage[error.WRONG_MAIL_FORMAT]
return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields}, safe=False)
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False)
return JsonResponse({'message':retour, 'errorMessage':retourErreur, "errorFields":errorFields, "id":-1}, safe=False)
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": -1}, safe=False)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
@ -417,26 +457,29 @@ class NewPasswordView(APIView):
def post(self, request):
retourErreur = error.returnMessage[error.BAD_URL]
retour = ''
newProfilConnection=JSONParser().parse(request)
newProfilConnection = JSONParser().parse(request)
validatorNewPassword = validator.ValidatorNewPassword(data=newProfilConnection)
validationOk, errorFields = validatorNewPassword.validate()
if validationOk:
profil = bdd.getProfile(Profile.objects.all(), newProfilConnection.get('email'))
if profil == None:
if profil is None:
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
else:
# Génération d'une URL provisoire pour modifier le mot de passe
profil.code = util.genereRandomCode(12)
profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS)
profil.save()
clear_cache()
retourErreur = ''
retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD]%(newProfilConnection.get('email'))
mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code)
try:
# Génération d'une URL provisoire pour modifier le mot de passe
profil.code = util.genereRandomCode(12)
profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS)
profil.save()
clear_cache()
retourErreur = ''
retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD] % (newProfilConnection.get('email'))
mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code)
except ValidationError as e:
retourErreur = error.returnMessage[error.WRONG_MAIL_FORMAT]
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False)
return JsonResponse({'message':retour, 'errorMessage':retourErreur, "errorFields":errorFields}, safe=False)
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
@ -465,7 +508,7 @@ class ResetPasswordView(APIView):
def post(self, request, code):
retourErreur = error.returnMessage[error.BAD_URL]
retour = ''
newProfilConnection=JSONParser().parse(request)
newProfilConnection = JSONParser().parse(request)
validatorResetPassword = validator.ValidatorResetPassword(data=newProfilConnection)
validationOk, errorFields = validatorResetPassword.validate()
@ -474,16 +517,15 @@ class ResetPasswordView(APIView):
if profil:
if datetime.strptime(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'), '%d-%m-%Y %H:%M') > datetime.strptime(profil.datePeremption, '%d-%m-%Y %H:%M'):
retourErreur = error.returnMessage[error.EXPIRED_URL]%(_uuid)
retourErreur = error.returnMessage[error.EXPIRED_URL] % (_uuid)
elif validationOk:
retour = error.returnMessage[error.PASSWORD_CHANGED]
profil.set_password(newProfilConnection.get('password1'))
profil.code = ''
profil.datePeremption = ''
profil.is_active = True
profil.save()
clear_cache()
retourErreur=''
retourErreur = ''
return JsonResponse({'message':retour, "errorMessage":retourErreur, "errorFields":errorFields}, safe=False)
return JsonResponse({'message': retour, "errorMessage": retourErreur, "errorFields": errorFields}, safe=False)

View File

@ -0,0 +1 @@
default_app_config = 'Establishment.apps.EstablishmentConfig'

View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
class EstablishmentConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Establishment'

View File

@ -0,0 +1,20 @@
from django.db import models
from django.contrib.postgres.fields import ArrayField
from django.utils.translation import gettext_lazy as _
class StructureType(models.IntegerChoices):
MATERNELLE = 1, _('Maternelle')
PRIMAIRE = 2, _('Primaire')
SECONDAIRE = 3, _('Secondaire')
class Establishment(models.Model):
name = models.CharField(max_length=255, unique=True)
address = models.CharField(max_length=255)
total_capacity = models.IntegerField()
establishment_type = ArrayField(models.IntegerField(choices=StructureType.choices))
licence_code = models.CharField(max_length=100, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name

View File

@ -0,0 +1,91 @@
from rest_framework import serializers
from .models import Establishment
from School.models import SchoolClass, Teacher, Speciality, Fee, Discount, PaymentMode, PaymentPlan
from Subscriptions.models import RegistrationForm, RegistrationFileGroup
from Auth.models import Profile
class EstablishmentSerializer(serializers.ModelSerializer):
profile_count = serializers.SerializerMethodField()
profiles = serializers.SerializerMethodField()
school_class_count = serializers.SerializerMethodField()
school_classes = serializers.SerializerMethodField()
teacher_count = serializers.SerializerMethodField()
teachers = serializers.SerializerMethodField()
speciality_count = serializers.SerializerMethodField()
specialities = serializers.SerializerMethodField()
fee_count = serializers.SerializerMethodField()
fees = serializers.SerializerMethodField()
discount_count = serializers.SerializerMethodField()
discounts = serializers.SerializerMethodField()
active_payment_mode_count = serializers.SerializerMethodField()
active_payment_modes = serializers.SerializerMethodField()
active_payment_plan_count = serializers.SerializerMethodField()
active_payment_plans = serializers.SerializerMethodField()
file_group_count = serializers.SerializerMethodField()
file_groups = serializers.SerializerMethodField()
registration_form_count = serializers.SerializerMethodField()
registration_forms = serializers.SerializerMethodField()
class Meta:
model = Establishment
fields = '__all__'
def get_profile_count(self, obj):
return Profile.objects.filter(roles__establishment=obj).distinct().count()
def get_profiles(self, obj):
return list(Profile.objects.filter(roles__establishment=obj).distinct().values_list('email', flat=True))
def get_school_class_count(self, obj):
return SchoolClass.objects.filter(establishment=obj).distinct().count()
def get_school_classes(self, obj):
return list(SchoolClass.objects.filter(establishment=obj).distinct().values_list('atmosphere_name', flat=True))
def get_teacher_count(self, obj):
return Teacher.objects.filter(profile_role__establishment=obj).distinct().count()
def get_teachers(self, obj):
return list(Teacher.objects.filter(profile_role__establishment=obj).distinct().values_list('last_name', 'first_name'))
def get_speciality_count(self, obj):
return Speciality.objects.filter(establishment=obj).distinct().count()
def get_specialities(self, obj):
return list(Speciality.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
def get_fee_count(self, obj):
return Fee.objects.filter(establishment=obj).distinct().count()
def get_fees(self, obj):
return list(Fee.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
def get_discount_count(self, obj):
return Discount.objects.filter(establishment=obj).distinct().count()
def get_discounts(self, obj):
return list(Discount.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
def get_active_payment_mode_count(self, obj):
return PaymentMode.objects.filter(establishment=obj, is_active=True).distinct().count()
def get_active_payment_modes(self, obj):
return list(PaymentMode.objects.filter(establishment=obj, is_active=True).distinct().values_list('mode', flat=True))
def get_active_payment_plan_count(self, obj):
return PaymentPlan.objects.filter(establishment=obj, is_active=True).distinct().count()
def get_active_payment_plans(self, obj):
return list(PaymentPlan.objects.filter(establishment=obj, is_active=True).distinct().values_list('frequency', flat=True))
def get_file_group_count(self, obj):
return RegistrationFileGroup.objects.filter(establishment=obj).distinct().count()
def get_file_groups(self, obj):
return list(RegistrationFileGroup.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
def get_registration_form_count(self, obj):
return RegistrationForm.objects.filter(establishment=obj).distinct().count()
def get_registration_forms(self, obj):
return list(RegistrationForm.objects.filter(establishment=obj).distinct().values_list('student__last_name', 'student__first_name'))

View File

@ -0,0 +1,7 @@
from django.urls import path, re_path
from .views import EstablishmentListCreateView, EstablishmentDetailView
urlpatterns = [
re_path(r'^establishments$', EstablishmentListCreateView.as_view(), name='establishment_list_create'),
re_path(r'^establishments/(?P<id>[0-9]+)$', EstablishmentDetailView.as_view(), name="establishment_detail"),
]

View File

@ -0,0 +1,51 @@
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 rest_framework import status
from .models import Establishment
from .serializers import EstablishmentSerializer
from N3wtSchool.bdd import delete_object, getAllObjects, getObject
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class EstablishmentListCreateView(APIView):
def get(self, request):
establishments = getAllObjects(Establishment)
establishments_serializer = EstablishmentSerializer(establishments, many=True)
return JsonResponse(establishments_serializer.data, safe=False, status=status.HTTP_200_OK)
def post(self, request):
establishment_data = JSONParser().parse(request)
establishment_serializer = EstablishmentSerializer(data=establishment_data)
if establishment_serializer.is_valid():
establishment_serializer.save()
return JsonResponse(establishment_serializer.data, safe=False, status=status.HTTP_201_CREATED)
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class EstablishmentDetailView(APIView):
def get(self, request, id=None):
try:
establishment = Establishment.objects.get(id=id)
establishment_serializer = EstablishmentSerializer(establishment)
return JsonResponse(establishment_serializer.data, safe=False)
except Establishment.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
def put(self, request, id):
establishment_data = JSONParser().parse(request)
try:
establishment = Establishment.objects.get(id=id)
except Establishment.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
establishment_serializer = EstablishmentSerializer(establishment, data=establishment_data, partial=True)
if establishment_serializer.is_valid():
establishment_serializer.save()
return JsonResponse(establishment_serializer.data, safe=False)
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
return delete_object(Establishment, id)

View File

@ -4,20 +4,20 @@ from .models import Notification, TypeNotif
from GestionMessagerie.models import Messagerie
from Subscriptions.models import RegistrationForm
@receiver(post_save, sender=Messagerie)
def notification_MESSAGE(sender, instance, created, **kwargs):
if created:
Notification.objects.create(
user=instance.destinataire,
message=(TypeNotif.NOTIF_MESSAGE).label,
typeNotification=TypeNotif.NOTIF_MESSAGE
)
# @receiver(post_save, sender=Messagerie)
# def notification_MESSAGE(sender, instance, created, **kwargs):
# if created:
# Notification.objects.create(
# user=instance.destinataire,
# message=(TypeNotif.NOTIF_MESSAGE).label,
# typeNotification=TypeNotif.NOTIF_MESSAGE
# )
@receiver(post_save, sender=RegistrationForm)
def notification_DI(sender, instance, created, **kwargs):
for responsable in instance.student.guardians.all():
Notification.objects.create(
user=responsable.associated_profile,
message=(TypeNotif.NOTIF_DI).label,
typeNotification=TypeNotif.NOTIF_DI
)
# @receiver(post_save, sender=RegistrationForm)
# def notification_DI(sender, instance, created, **kwargs):
# for responsable in instance.student.guardians.all():
# Notification.objects.create(
# user=responsable.associated_profile,
# message=(TypeNotif.NOTIF_DI).label,
# typeNotification=TypeNotif.NOTIF_DI
# )

View File

@ -44,6 +44,7 @@ INSTALLED_APPS = [
'GestionNotification.apps.GestionNotificationConfig',
'School.apps.SchoolConfig',
'Planning.apps.PlanningConfig',
'Establishment.apps.EstablishmentConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',

View File

@ -46,6 +46,7 @@ urlpatterns = [
path("School/", include(("School.urls", 'School'), namespace='School')),
path("DocuSeal/", include(("DocuSeal.urls", 'DocuSeal'), namespace='DocuSeal')),
path("Planning/", include(("Planning.urls", 'Planning'), namespace='Planning')),
path("Establishment/", include(("Establishment.urls", 'Establishment'), namespace='Establishment')),
# Documentation Api
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),

View File

@ -3,7 +3,7 @@ from django.db import models
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from School.models import Establishment
from Establishment.models import Establishment
class RecursionType(models.IntegerChoices):
RECURSION_NONE = 0, _('Aucune')

View File

@ -1,14 +1,5 @@
from django.apps import AppConfig
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', color_code='#FF0000')
class SchoolConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'School'
def ready(self):
post_migrate.connect(create_speciality, sender=self)

View File

@ -1,5 +1,4 @@
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from Subscriptions.models import (
RegistrationForm,
Student,
@ -10,9 +9,8 @@ from Subscriptions.models import (
RegistrationTemplateMaster,
RegistrationTemplate
)
from Auth.models import Profile
from Auth.models import Profile, ProfileRole
from School.models import (
Establishment,
FeeType,
Speciality,
Teacher,
@ -20,8 +18,7 @@ from School.models import (
PaymentMode,
PaymentModeType,
PaymentPlan,
PaymentPlanType,
StructureType,
PaymentPlanType,
DiscountType
)
from django.utils import timezone
@ -34,6 +31,19 @@ from faker import Faker
import random
import json
from School.serializers import (
FeeSerializer,
DiscountSerializer,
PaymentModeSerializer,
PaymentPlanSerializer,
SpecialitySerializer,
TeacherSerializer,
SchoolClassSerializer
)
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
from Establishment.serializers import EstablishmentSerializer
from Subscriptions.serializers import RegistrationFormSerializer, GuardianSerializer
# Définir le chemin vers le dossier mock_datas
MOCK_DATAS_PATH = os.path.join(settings.BASE_DIR, 'School', 'management', 'mock_datas')
@ -41,176 +51,306 @@ class Command(BaseCommand):
help = 'Initialise toutes les données mock'
def handle(self, *args, **kwargs):
self.create_or_update_establishments()
self.create_or_update_fees()
self.create_or_update_discounts()
self.create_or_update_payment_modes()
self.create_or_update_payment_plans()
self.create_or_update_specialities()
self.create_or_update_teachers()
self.create_or_update_school_classes()
self.create_or_update_registration_file_group()
self.create_register_form()
self.init_establishments()
self.init_profiles()
self.init_fees()
self.init_discounts()
self.init_payment_modes()
self.init_payment_plans()
self.init_specialities()
self.init_teachers()
self.init_guardians()
self.init_school_classes()
self.init_file_group()
self.init_register_form()
def load_data(self, filename):
with open(os.path.join(MOCK_DATAS_PATH, filename), 'r') as file:
return json.load(file)
def create_or_update_establishments(self):
def init_establishments(self):
establishments_data = self.load_data('establishments.json')
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'))
serializer = EstablishmentSerializer(data=establishment_data)
if serializer.is_valid():
establishment = serializer.save()
self.establishments.append(establishment)
self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} created or updated successfully'))
else:
self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} updated successfully'))
self.stdout.write(self.style.ERROR(f'Error in data for establishment: {serializer.errors}'))
def create_or_update_fees(self):
def init_profiles(self):
profiles_data = self.load_data('profiles.json')
for profile_data in profiles_data:
# Randomize the number of roles to create (between 1 et 3)
num_roles = random.randint(1, 3)
selected_roles = []
for _ in range(num_roles):
establishment = random.choice(self.establishments)
role_type = random.choice([ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN, ProfileRole.RoleType.PROFIL_PARENT])
# Ensure no duplicate ADMIN role for the same establishment
if role_type == ProfileRole.RoleType.PROFIL_ADMIN:
if any(role['role_type'] == ProfileRole.RoleType.PROFIL_ADMIN and role['establishment'] == establishment.id for role in selected_roles):
continue
selected_roles.append({
"role_type": role_type,
"establishment": establishment.id,
"establishment_name": establishment.name
})
# Generate email based on the selected roles and establishment
role_types = '-'.join([f"{ProfileRole.RoleType(role['role_type']).name.replace('PROFIL_', '')}_{role['establishment_name'].replace(' ', '')}" for role in selected_roles])
email = f"{profile_data['username']}-{role_types}@exemple.com"
# Add email to profile data
profile_data['email'] = email
serializer = ProfileSerializer(data=profile_data)
if serializer.is_valid():
profile = serializer.save()
profile.set_password(profile_data["password"])
profile.save()
self.stdout.write(self.style.SUCCESS(f'Profile {profile.email} created successfully'))
# Create or update the profile role for each selected role using ProfileRoleSerializer
for role in selected_roles:
role_data = {
"profile": profile.id,
"establishment": role["establishment"],
"role_type": role["role_type"],
"is_active": True
}
role_serializer = ProfileRoleSerializer(data=role_data)
if role_serializer.is_valid():
role_serializer.save()
else:
self.stdout.write(self.style.ERROR(f'Error in data for profile role: {role_serializer.errors}'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for profile: {serializer.errors}'))
def init_fees(self):
fees_data = self.load_data('fees.json')
for fee_data in fees_data:
establishment = random.choice(self.establishments)
print(f'establishment : {establishment}')
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"],
defaults=fee_data
)
fee_data["establishment"] = establishment.id
fee_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])
self.stdout.write(self.style.SUCCESS('Fees initialized or updated successfully'))
serializer = FeeSerializer(data=fee_data)
if serializer.is_valid():
fee = serializer.save()
self.stdout.write(self.style.SUCCESS(f'Fee {fee.name} created successfully'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for fee: {serializer.errors}'))
def create_or_update_discounts(self):
def init_discounts(self):
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"],
discount_type=discount_data["discount_type"],
defaults=discount_data
)
discount_data["establishment"] = establishment.id
discount_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])
discount_data["discount_type"] = random.choice([DiscountType.CURRENCY, DiscountType.PERCENT])
self.stdout.write(self.style.SUCCESS('Discounts initialized or updated successfully'))
serializer = DiscountSerializer(data=discount_data)
if serializer.is_valid():
discount = serializer.save()
self.stdout.write(self.style.SUCCESS(f'Discount {discount.name} created successfully'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for discount: {serializer.errors}'))
def create_or_update_payment_modes(self):
payment_modes_data = self.load_data('payment_modes.json')
def init_payment_modes(self):
modes = [PaymentModeType.SEPA, PaymentModeType.TRANSFER, PaymentModeType.CHECK, PaymentModeType.CASH]
types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]
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
)
for establishment in self.establishments:
for mode in modes:
for type in types:
payment_mode_data = {
"mode": mode,
"type": type,
"is_active": random.choice([True, False]),
"establishment": establishment.id
}
self.stdout.write(self.style.SUCCESS('Payment Modes initialized or updated successfully'))
serializer = PaymentModeSerializer(data=payment_mode_data)
if serializer.is_valid():
payment_mode = serializer.save()
self.stdout.write(self.style.SUCCESS(f'Payment Mode {payment_mode} created successfully'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for payment mode: {serializer.errors}'))
def create_or_update_payment_plans(self):
payment_plans_data = self.load_data('payment_plans.json')
def init_payment_plans(self):
frequencies = [PaymentPlanType.ONE_TIME, PaymentPlanType.THREE_TIMES, PaymentPlanType.TEN_TIMES, PaymentPlanType.TWELVE_TIMES]
types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]
current_date = timezone.now().date()
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)]
for establishment in self.establishments:
for frequency in frequencies:
for type in types:
payment_plan_data = {
"frequency": frequency,
"type": type,
"is_active": random.choice([True, False]),
"establishment": establishment.id,
"due_dates": self.generate_due_dates(frequency, current_date)
}
PaymentPlan.objects.update_or_create(
frequency=payment_plan_data["frequency"],
type=payment_plan_data["type"],
defaults=payment_plan_data
)
serializer = PaymentPlanSerializer(data=payment_plan_data)
if serializer.is_valid():
payment_plan = serializer.save()
self.stdout.write(self.style.SUCCESS(f'Payment Plan {payment_plan} created successfully'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for payment plan: {serializer.errors}'))
self.stdout.write(self.style.SUCCESS('Payment Plans initialized or updated successfully'))
def generate_due_dates(self, frequency, start_date):
if frequency == PaymentPlanType.ONE_TIME:
return [start_date + relativedelta(months=1)]
elif frequency == PaymentPlanType.THREE_TIMES:
return [start_date + relativedelta(months=1+4*i) for i in range(3)]
elif frequency == PaymentPlanType.TEN_TIMES:
return [start_date + relativedelta(months=1+i) for i in range(10)]
elif frequency == PaymentPlanType.TWELVE_TIMES:
return [start_date + relativedelta(months=1+i) for i in range(12)]
def create_or_update_specialities(self):
def init_specialities(self):
specialities_data = self.load_data('specialities.json')
for speciality_data in specialities_data:
Speciality.objects.update_or_create(
name=speciality_data["name"],
defaults=speciality_data
)
self.stdout.write(self.style.SUCCESS('Specialities initialized or updated successfully'))
def create_or_update_teachers(self):
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)
speciality_data["name"] = f"{speciality_data['name']} - {establishment.name}"
speciality_data["establishment"] = establishment.id
# Create or update the user profile
user, created = Profile.objects.update_or_create(
email=email,
defaults={
"username": email,
"email": email,
"is_active": True,
"password": "Provisoire01!",
"droit": droit,
"establishment": establishment
serializer = SpecialitySerializer(data=speciality_data)
if serializer.is_valid():
speciality = serializer.save()
self.stdout.write(self.style.SUCCESS(f'Speciality {speciality.name} created successfully'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for speciality: {serializer.errors}'))
def init_teachers(self):
fake = Faker()
# Récupérer tous les profils dont le role_type est soit ECOLE soit ADMIN
profiles = Profile.objects.filter(roles__role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN]).distinct()
for profile in profiles:
# Récupérer les rôles associés au profil
profile_roles = ProfileRole.objects.filter(profile=profile, role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN])
for profile_role in profile_roles:
establishment = profile_role.establishment
teacher_data = {
"last_name": fake.last_name(),
"first_name": f"{fake.first_name()} - {establishment.name}",
"profile_role": profile_role.id
}
)
if created:
user.set_password("Provisoire01!")
user.save()
# Create or update the teacher
teacher, created = Teacher.objects.update_or_create(
email=email,
defaults={**teacher_data, "associated_profile_id": user.id}
)
teacher.specialities.set(Speciality.objects.filter(name__in=specialities))
teacher.save()
establishment_specialities = list(Speciality.objects.filter(establishment=establishment))
num_specialities = min(random.randint(1, 3), len(establishment_specialities))
selected_specialities = random.sample(establishment_specialities, num_specialities)
# Créer l'enseignant si il n'existe pas
teacher_serializer = TeacherSerializer(data=teacher_data)
if teacher_serializer.is_valid():
teacher = teacher_serializer.save()
# Associer les spécialités
teacher.specialities.set(selected_specialities)
teacher.save()
self.stdout.write(self.style.SUCCESS(f'Teacher {teacher.last_name} created successfully for establishment {establishment.name}'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for teacher: {teacher_serializer.errors}'))
self.stdout.write(self.style.SUCCESS('Teachers initialized or updated successfully'))
def create_or_update_school_classes(self):
def init_guardians(self):
fake = Faker()
# Récupérer tous les profils dont le role_type est PROFIL_PARENT
profiles = Profile.objects.filter(roles__role_type=ProfileRole.RoleType.PROFIL_PARENT).distinct()
for profile in profiles:
# Récupérer les rôles associés au profil
profile_roles = ProfileRole.objects.filter(profile=profile, role_type=ProfileRole.RoleType.PROFIL_PARENT)
for profile_role in profile_roles:
establishment = profile_role.establishment
guardian_data = {
"last_name": fake.last_name(),
"first_name": f"{fake.first_name()} - {establishment.name}",
"profile_role": profile_role.id,
"birth_date": fake.date_of_birth().strftime('%Y-%m-%d'), # Convertir en chaîne de caractères valide
"address": fake.address(),
"phone": fake.phone_number(),
"profession": fake.job()
}
# Créer le guardian si il n'existe pas
guardian_serializer = GuardianSerializer(data=guardian_data)
if guardian_serializer.is_valid():
guardian = guardian_serializer.save()
self.stdout.write(self.style.SUCCESS(f'Guardian {guardian.last_name} created successfully for establishment {establishment.name}'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for guardian: {guardian_serializer.errors}'))
self.stdout.write(self.style.SUCCESS('Guardians initialized or updated successfully'))
def init_school_classes(self):
school_classes_data = self.load_data('school_classes.json')
for index, class_data in enumerate(school_classes_data, start=1):
teachers_ids = class_data.pop("teachers")
# Randomize establishment
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"],
defaults=class_data
)
school_class.teachers.set(teachers_ids)
school_class.save()
class_data["establishment"] = establishment.id
# Randomize levels
class_data["levels"] = random.sample(range(1, 10), random.randint(1, 5))
# Randomize teachers
establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment))
num_teachers = min(random.randint(1, 10), len(establishment_teachers))
selected_teachers = random.sample(establishment_teachers, num_teachers)
teachers_ids = [teacher.id for teacher in selected_teachers]
# Use the serializer to create or update the school class
class_data["teachers"] = teachers_ids
serializer = SchoolClassSerializer(data=class_data)
if serializer.is_valid():
school_class = serializer.save()
self.stdout.write(self.style.SUCCESS(f'SchoolClass {school_class.atmosphere_name} created or updated successfully'))
else:
self.stdout.write(self.style.ERROR(f'Error in data for school class: {serializer.errors}'))
self.stdout.write(self.style.SUCCESS('SchoolClasses initialized or updated successfully'))
def create_or_update_registration_file_group(self):
file_groups_data = self.load_data('file_groups.json')
def init_file_group(self):
fake = Faker()
for group_data in file_groups_data:
RegistrationFileGroup.objects.update_or_create(name=group_data["name"], defaults=group_data)
self.stdout.write(self.style.SUCCESS(f'RegistrationFileGroup {group_data["name"]} initialized or updated successfully'))
for establishment in self.establishments:
for i in range(1, 4): # Créer 3 groupes de fichiers par établissement
name = f"Fichiers d'inscription - {fake.word()} - {establishment.name}"
description = fake.sentence()
group_data = {
"name": name,
"description": description,
"establishment": establishment
}
RegistrationFileGroup.objects.update_or_create(name=name, defaults=group_data)
self.stdout.write(self.style.SUCCESS(f'RegistrationFileGroup {name} initialized or updated successfully'))
def create_register_form(self):
self.stdout.write(self.style.SUCCESS('All RegistrationFileGroups initialized or updated successfully'))
def init_register_form(self):
fake = Faker('fr_FR') # Utiliser le locale français pour Faker
file_group_count = RegistrationFileGroup.objects.count()
@ -218,40 +358,13 @@ class Command(BaseCommand):
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!",
"establishment": establishment
}
user, created = Profile.objects.update_or_create(
email=profile_data["email"],
defaults={
"username": profile_data["username"],
"email": profile_data["email"],
"is_active": profile_data["is_active"],
"droit": profile_data["droit"],
"establishment": profile_data["establishment"]
}
)
if created:
user.set_password(profile_data["password"])
user.save()
# Générer des données fictives pour le guardian
guardian_data = {
"associated_profile_id": user.id,
"email": profile_data["email"],
}
# Récupérer un guardian aléatoire déjà créé
guardian = Guardian.objects.order_by('?').first()
# Générer des données fictives pour l'étudiant
student_data = {
"last_name": f"{fake.last_name()} - {establishment.name}",
"first_name": fake.first_name(),
"last_name": fake.last_name(),
"first_name": f"{fake.first_name()} - {establishment.name}",
"address": fake.address(),
"birth_date": fake.date_of_birth(),
"birth_place": fake.city(),
@ -261,16 +374,12 @@ class Command(BaseCommand):
"level": fake.random_int(min=1, max=6)
}
# Créer ou mettre à jour l'étudiant et le guardian
# Créer ou mettre à jour l'étudiant
student, created = Student.objects.get_or_create(
last_name=student_data["last_name"],
first_name=student_data["first_name"],
defaults=student_data
)
guardian, created = Guardian.objects.get_or_create(
last_name=guardian_data["email"],
defaults=guardian_data
)
student.guardians.add(guardian)
# Récupérer les frais et les réductions
@ -279,18 +388,23 @@ class Command(BaseCommand):
# Créer les données du formulaire d'inscription
register_form_data = {
"student": student,
"fileGroup": RegistrationFileGroup.objects.get(id=fake.random_int(min=1, max=file_group_count)),
"establishment": establishment,
"status": fake.random_int(min=1, max=3)
}
# Créer ou mettre à jour le formulaire d'inscription
register_form, created = RegistrationForm.objects.get_or_create(student=student, defaults=register_form_data)
register_form.fees.set(fees)
register_form.discounts.set(discounts)
if not created:
register_form.fileGroup = file_group
register_form.save()
register_form, created = RegistrationForm.objects.get_or_create(
student=student,
establishment=establishment,
defaults=register_form_data
)
if created:
register_form.fees.set(fees)
register_form.discounts.set(discounts)
self.stdout.write(self.style.SUCCESS(f'RegistrationForm for student {student.last_name} created successfully'))
else:
self.stdout.write(self.style.SUCCESS(f'RegistrationForm for student {student.last_name} already exists'))
self.stdout.write(self.style.SUCCESS('50 RegistrationForms initialized or updated successfully'))

View File

@ -2,15 +2,41 @@
{
"name": "Parrainage",
"amount": "10.00",
"description": "Réduction pour parrainage",
"discount_type": 1,
"type": 1
"description": "Réduction pour parrainage"
},
{
"name": "Réinscription",
"amount": "100.00",
"description": "Réduction pour Réinscription",
"discount_type": 1,
"type": 0
"description": "Réduction pour Réinscription"
},
{
"name": "Famille nombreuse",
"amount": "50.00",
"description": "Réduction pour les familles nombreuses"
},
{
"name": "Excellence académique",
"amount": "200.00",
"description": "Réduction pour les élèves ayant des résultats académiques exceptionnels"
},
{
"name": "Sportif de haut niveau",
"amount": "150.00",
"description": "Réduction pour les élèves pratiquant un sport de haut niveau"
},
{
"name": "Artiste talentueux",
"amount": "100.00",
"description": "Réduction pour les élèves ayant des talents artistiques"
},
{
"name": "Bourse d'études",
"amount": "300.00",
"description": "Réduction pour les élèves bénéficiant d'une bourse d'études"
},
{
"name": "Réduction spéciale",
"amount": "75.00",
"description": "Réduction spéciale pour des occasions particulières"
}
]

View File

@ -3,35 +3,30 @@
"name": "Frais d'inscription",
"base_amount": "150.00",
"description": "Montant de base",
"is_active": true,
"type": 0
"is_active": true
},
{
"name": "Matériel",
"base_amount": "85.00",
"description": "Livres / jouets",
"is_active": true,
"type": 0
"is_active": true
},
{
"name": "Sorties périscolaires",
"base_amount": "120.00",
"description": "Sorties",
"is_active": true,
"type": 0
"is_active": true
},
{
"name": "Les colibris",
"base_amount": "4500.00",
"description": "TPS / PS / MS / GS",
"is_active": true,
"type": 1
"is_active": true
},
{
"name": "Les butterflies",
"base_amount": "5000.00",
"description": "CP / CE1 / CE2 / CM1 / CM2",
"is_active": true,
"type": 1
"is_active": true
}
]

View File

@ -1,22 +0,0 @@
[
{
"name": "Fichiers d'inscription - Classe 1 - Ecole A",
"description": "Fichiers d'inscription pour la Classe 1 de l'école Ecole A"
},
{
"name": "Fichiers d'inscription - Classe 2 - Ecole B",
"description": "Fichiers d'inscription pour la Classe 2 de l'école Ecole B"
},
{
"name": "Fichiers d'inscription - Classe 3 - Ecole C",
"description": "Fichiers d'inscription pour la Classe 3 de l'école Ecole C"
},
{
"name": "Fichiers d'inscription - Classe 4 - Ecole A",
"description": "Fichiers d'inscription pour la Classe 4 de l'école Ecole A"
},
{
"name": "Fichiers d'inscription - Classe 5 - Ecole B",
"description": "Fichiers d'inscription pour la Classe 5 de l'école Ecole B"
}
]

View File

@ -1,12 +0,0 @@
[
{
"mode": 4,
"type": 0,
"is_active": true
},
{
"mode": 2,
"type": 1,
"is_active": true
}
]

View File

@ -1,22 +0,0 @@
[
{
"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
}
]

View File

@ -0,0 +1,34 @@
[
{
"username": "albus.dumbledore",
"password": "Provisoire01!"
},
{
"username": "severus.rogue",
"password": "Provisoire01!"
},
{
"username": "minerva.mcgonagall",
"password": "Provisoire01!"
},
{
"username": "pomona.chourave",
"password": "Provisoire01!"
},
{
"username": "rubeus.hagrid",
"password": "Provisoire01!"
},
{
"username": "filius.flitwick",
"password": "Provisoire01!"
},
{
"username": "pomona.sprout",
"password": "Provisoire01!"
},
{
"username": "aurora.sinistra",
"password": "Provisoire01!"
}
]

View File

@ -7,8 +7,7 @@
"levels": [2, 3, 4],
"type": 1,
"time_range": ["08:30", "17:30"],
"opening_days": [1, 2, 4, 5],
"teachers": [2]
"opening_days": [1, 2, 4, 5]
},
{
"age_range": "2-3",
@ -18,8 +17,7 @@
"levels": [1],
"type": 1,
"time_range": ["08:30", "17:30"],
"opening_days": [1, 2, 4, 5],
"teachers": [3]
"opening_days": [1, 2, 4, 5]
},
{
"age_range": "6-12",
@ -29,8 +27,7 @@
"levels": [5, 6, 7, 8, 9],
"type": 1,
"time_range": ["08:30", "17:30"],
"opening_days": [1, 2, 4, 5],
"teachers": [4]
"opening_days": [1, 2, 4, 5]
},
{
"age_range": "4-6",
@ -40,8 +37,7 @@
"levels": [4, 5],
"type": 1,
"time_range": ["08:30", "17:30"],
"opening_days": [1, 2, 4, 5],
"teachers": [1]
"opening_days": [1, 2, 4, 5]
},
{
"age_range": "7-9",
@ -51,7 +47,6 @@
"levels": [6, 7],
"type": 1,
"time_range": ["08:30", "17:30"],
"opening_days": [1, 2, 4, 5],
"teachers": [2]
"opening_days": [1, 2, 4, 5]
}
]

View File

@ -1,58 +0,0 @@
[
{
"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": 2
},
{
"last_name": "MC GONAGALL",
"first_name": "Minerva",
"email": "minerva.mcgonagall@gmail.com",
"specialities": ["MATHS", "HISTOIRE"],
"droit": 2
},
{
"last_name": "CHOURAVE",
"first_name": "Pomona",
"email": "pomona.chourave@gmail.com",
"specialities": ["MATHS", "FRANCAIS", "SPORT"],
"droit": 1
},
{
"last_name": "HAGRID",
"first_name": "Rubeus",
"email": "rubeus.hagrid@gmail.com",
"specialities": ["SCIENCES"],
"droit": 2
},
{
"last_name": "FLITWICK",
"first_name": "Filius",
"email": "filius.flitwick@gmail.com",
"specialities": ["MUSIQUE"],
"droit": 1
},
{
"last_name": "SPROUT",
"first_name": "Pomona",
"email": "pomona.sprout@gmail.com",
"specialities": ["ART"],
"droit": 2
},
{
"last_name": "SINISTRA",
"first_name": "Aurora",
"email": "aurora.sinistra@gmail.com",
"specialities": ["INFORMATIQUE"],
"droit": 2
}
]

View File

@ -1,5 +1,6 @@
from django.db import models
from Auth.models import Profile
from Auth.models import ProfileRole
from Establishment.models import Establishment
from django.db.models import JSONField
from django.dispatch import receiver
from django.contrib.postgres.fields import ArrayField
@ -18,27 +19,11 @@ LEVEL_CHOICES = [
(9, 'Cours Moyen 2 (CM2)')
]
class StructureType(models.IntegerChoices):
MATERNELLE = 1, _('Maternelle')
PRIMAIRE = 2, _('Primaire')
SECONDAIRE = 3, _('Secondaire')
class Establishment(models.Model):
name = models.CharField(max_length=255, unique=True)
address = models.CharField(max_length=255)
total_capacity = models.IntegerField()
establishment_type = ArrayField(models.IntegerField(choices=StructureType.choices))
licence_code = models.CharField(max_length=100, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Speciality(models.Model):
name = models.CharField(max_length=100)
updated_date = models.DateTimeField(auto_now=True)
color_code = models.CharField(max_length=7, default='#FFFFFF')
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='specialities')
def __str__(self):
return self.name
@ -46,9 +31,8 @@ class Speciality(models.Model):
class Teacher(models.Model):
last_name = models.CharField(max_length=100)
first_name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
specialities = models.ManyToManyField(Speciality, blank=True)
associated_profile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True, blank=True)
profile_role = models.OneToOneField(ProfileRole, on_delete=models.CASCADE, related_name='teacher_profile', null=True, blank=True)
updated_date = models.DateTimeField(auto_now=True)
def __str__(self):

View File

@ -1,7 +1,8 @@
from rest_framework import serializers
from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, Fee, PaymentPlan, PaymentMode, Establishment
from Auth.models import Profile
from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, Fee, PaymentPlan, PaymentMode
from Auth.models import Profile, ProfileRole
from Subscriptions.models import Student
from Establishment.models import Establishment
from N3wtSchool import settings, bdd
from django.utils import timezone
import pytz
@ -30,9 +31,10 @@ class TeacherDetailSerializer(serializers.ModelSerializer):
class TeacherSerializer(serializers.ModelSerializer):
specialities = serializers.PrimaryKeyRelatedField(queryset=Speciality.objects.all(), many=True, required=False)
specialities_details = serializers.SerializerMethodField()
associated_profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=True)
profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=True)
updated_date_formatted = serializers.SerializerMethodField()
droit = serializers.SerializerMethodField()
role_type = serializers.SerializerMethodField()
associated_profile_email = serializers.SerializerMethodField()
class Meta:
model = Teacher
@ -40,12 +42,12 @@ class TeacherSerializer(serializers.ModelSerializer):
def create(self, validated_data):
specialities_data = validated_data.pop('specialities', None)
associated_profile = validated_data.pop('associated_profile', None)
profile_role = validated_data.pop('profile_role', None)
teacher = Teacher.objects.create(**validated_data)
if specialities_data:
teacher.specialities.set(specialities_data)
if associated_profile:
teacher.associated_profile = associated_profile
if profile_role:
teacher.profile_role = profile_role
teacher.save()
return teacher
@ -54,7 +56,7 @@ class TeacherSerializer(serializers.ModelSerializer):
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.associated_profile = validated_data.get('associated_profile', instance.associated_profile)
instance.profile_role = validated_data.get('profile_role', instance.profile_role)
instance.save()
if specialities_data:
instance.specialities.set(specialities_data)
@ -64,17 +66,18 @@ class TeacherSerializer(serializers.ModelSerializer):
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")
def get_droit(self, obj):
if obj.associated_profile:
return obj.associated_profile.droit
return None
def get_role_type(self, obj):
profile_role = obj.profile_role
return {'role_type': profile_role.role_type, 'establishment': profile_role.establishment.name}
def get_specialities_details(self, obj):
return [{'id': speciality.id, 'name': speciality.name, 'color_code': speciality.color_code} for speciality in obj.specialities.all()]
def get_associated_profile_email(self, obj):
return obj.profile_role.profile.email
class PlanningSerializer(serializers.ModelSerializer):
class Meta:
model = Planning
@ -207,9 +210,4 @@ class PaymentPlanSerializer(serializers.ModelSerializer):
class PaymentModeSerializer(serializers.ModelSerializer):
class Meta:
model = PaymentMode
fields = '__all__'
class EstablishmentSerializer(serializers.ModelSerializer):
class Meta:
model = Establishment
fields = '__all__'

View File

@ -16,9 +16,7 @@ from .views import (
PaymentPlanListCreateView,
PaymentPlanDetailView,
PaymentModeListCreateView,
PaymentModeDetailView,
EstablishmentListCreateView,
EstablishmentDetailView
PaymentModeDetailView
)
urlpatterns = [
@ -44,8 +42,5 @@ urlpatterns = [
re_path(r'^paymentPlans/(?P<id>[0-9]+)$', PaymentPlanDetailView.as_view(), name="payment_plan_detail"),
re_path(r'^paymentModes$', PaymentModeListCreateView.as_view(), name="payment_mode_list_create"),
re_path(r'^paymentModes/(?P<id>[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail"),
re_path(r'^establishments$', EstablishmentListCreateView.as_view(), name='establishment_list_create'),
re_path(r'^establishments/(?P<id>[0-9]+)$', EstablishmentDetailView.as_view(), name="establishment_detail"),
re_path(r'^paymentModes/(?P<id>[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail")
]

View File

@ -12,8 +12,7 @@ from .models import (
Discount,
Fee,
PaymentPlan,
PaymentMode,
Establishment
PaymentMode
)
from .serializers import (
TeacherSerializer,
@ -23,8 +22,7 @@ from .serializers import (
DiscountSerializer,
FeeSerializer,
PaymentPlanSerializer,
PaymentModeSerializer,
EstablishmentSerializer
PaymentModeSerializer
)
from N3wtSchool.bdd import delete_object, getAllObjects, getObject
@ -32,8 +30,14 @@ from N3wtSchool.bdd import delete_object, getAllObjects, getObject
@method_decorator(ensure_csrf_cookie, name='dispatch')
class SpecialityListCreateView(APIView):
def get(self, request):
specialitiesList = getAllObjects(Speciality)
specialities_serializer = SpecialitySerializer(specialitiesList, many=True)
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)
specialities_list = getAllObjects(Speciality)
if establishment_id:
specialities_list = specialities_list.filter(establishment__id=establishment_id).distinct()
specialities_serializer = SpecialitySerializer(specialities_list, many=True)
return JsonResponse(specialities_serializer.data, safe=False)
def post(self, request):
@ -51,7 +55,7 @@ class SpecialityListCreateView(APIView):
class SpecialityDetailView(APIView):
def get(self, request, id):
speciality = getObject(_objectName=Speciality, _columnName='id', _value=id)
speciality_serializer = SpecialitySerializer(speciality)
speciality_serializer=SpecialitySerializer(speciality)
return JsonResponse(speciality_serializer.data, safe=False)
def put(self, request, id):
@ -71,9 +75,14 @@ class SpecialityDetailView(APIView):
@method_decorator(ensure_csrf_cookie, name='dispatch')
class TeacherListCreateView(APIView):
def get(self, request):
teachersList=getAllObjects(Teacher)
teachers_serializer=TeacherSerializer(teachersList, many=True)
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)
teachers_list = getAllObjects(Teacher)
if teachers_list:
teachers_list = teachers_list.filter(profile_role__establishment_id=establishment_id)
teachers_serializer = TeacherSerializer(teachers_list, many=True)
return JsonResponse(teachers_serializer.data, safe=False)
def post(self, request):
@ -107,14 +116,20 @@ class TeacherDetailView(APIView):
return JsonResponse(teacher_serializer.errors, safe=False)
def delete(self, request, id):
return delete_object(Teacher, id, related_field='associated_profile')
return delete_object(Teacher, id, related_field='profile')
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class SchoolClassListCreateView(APIView):
def get(self, request):
classesList=getAllObjects(SchoolClass)
classes_serializer=SchoolClassSerializer(classesList, many=True)
def get(self, request):
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)
school_classes_list = getAllObjects(SchoolClass)
if school_classes_list:
school_classes_list = school_classes_list.filter(establishment=establishment_id).distinct()
classes_serializer = SchoolClassSerializer(school_classes_list, many=True)
return JsonResponse(classes_serializer.data, safe=False)
def post(self, request):
@ -201,9 +216,14 @@ class PlanningDetailView(APIView):
@method_decorator(ensure_csrf_cookie, name='dispatch')
class FeeListCreateView(APIView):
def get(self, request, *args, **kwargs):
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)
filter = request.GET.get('filter', '').strip()
fee_type_value = 0 if filter == 'registration' else 1
fees = Fee.objects.filter(type=fee_type_value)
fees = Fee.objects.filter(type=fee_type_value, establishment_id=establishment_id).distinct()
fee_serializer = FeeSerializer(fees, many=True)
return JsonResponse(fee_serializer.data, safe=False, status=status.HTTP_200_OK)
@ -244,11 +264,16 @@ class FeeDetailView(APIView):
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class DiscountListCreateView(APIView):
class DiscountListCreateView(APIView):
def get(self, request, *args, **kwargs):
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)
filter = request.GET.get('filter', '').strip()
discount_type_value = 0 if filter == 'registration' else 1
discounts = Discount.objects.filter(type=discount_type_value)
discounts = Discount.objects.filter(type=discount_type_value, establishment_id=establishment_id).distinct()
discounts_serializer = DiscountSerializer(discounts, many=True)
return JsonResponse(discounts_serializer.data, safe=False, status=status.HTTP_200_OK)
@ -291,10 +316,15 @@ class DiscountDetailView(APIView):
@method_decorator(ensure_csrf_cookie, name='dispatch')
class PaymentPlanListCreateView(APIView):
def get(self, request, *args, **kwargs):
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)
filter = request.GET.get('filter', '').strip()
type_value = 0 if filter == 'registration' else 1
paymentPlans = PaymentPlan.objects.filter(type=type_value)
payment_plans_serializer = PaymentPlanSerializer(paymentPlans, many=True)
payment_plans = PaymentPlan.objects.filter(type=type_value, establishment_id=establishment_id).distinct()
payment_plans_serializer = PaymentPlanSerializer(payment_plans, many=True)
return JsonResponse(payment_plans_serializer.data, safe=False, status=status.HTTP_200_OK)
@ -336,10 +366,15 @@ class PaymentPlanDetailView(APIView):
@method_decorator(ensure_csrf_cookie, name='dispatch')
class PaymentModeListCreateView(APIView):
def get(self, request):
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)
filter = request.GET.get('filter', '').strip()
type_value = 0 if filter == 'registration' else 1
paymentModes = PaymentMode.objects.filter(type=type_value)
payment_modes_serializer = PaymentModeSerializer(paymentModes, many=True)
payment_modes = PaymentMode.objects.filter(type=type_value, establishment_id=establishment_id).distinct()
payment_modes_serializer = PaymentModeSerializer(payment_modes, many=True)
return JsonResponse(payment_modes_serializer.data, safe=False, status=status.HTTP_200_OK)
@ -376,45 +411,3 @@ 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 EstablishmentListCreateView(APIView):
def get(self, request):
establishments=getAllObjects(Establishment)
establishments_serializer=EstablishmentSerializer(establishments, many=True)
return JsonResponse(establishments_serializer.data, safe=False, status=status.HTTP_200_OK)
def post(self, request):
establishment_data = JSONParser().parse(request)
establishment_serializer = EstablishmentSerializer(data=establishment_data)
if establishment_serializer.is_valid():
establishment_serializer.save()
return JsonResponse(establishment_serializer.data, safe=False, status=status.HTTP_201_CREATED)
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class EstablishmentDetailView(APIView):
def get(self, request, id=None):
try:
establishment = Establishment.objects.get(id=id)
establishment_serializer = EstablishmentSerializer(establishment)
return JsonResponse(establishment_serializer.data, safe=False)
except Establishment.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
def put(self, request, id):
establishment_data = JSONParser().parse(request)
try:
establishment = Establishment.objects.get(id=id)
except Establishment.DoesNotExist:
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
establishment_serializer = EstablishmentSerializer(establishment, data=establishment_data, partial=True)
if establishment_serializer.is_valid():
establishment_serializer.save()
return JsonResponse(establishment_serializer.data, safe=False)
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
return delete_object(Establishment, id)

View File

@ -4,6 +4,8 @@ from django.conf import settings
from django.utils.translation import gettext_lazy as _
from School.models import SchoolClass, Fee, Discount
from Auth.models import ProfileRole
from Establishment.models import Establishment
from datetime import datetime
@ -25,10 +27,9 @@ class Guardian(models.Model):
first_name = models.CharField(max_length=200, default="")
birth_date = models.CharField(max_length=200, default="", blank=True)
address = models.CharField(max_length=200, default="", blank=True)
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('Auth.Profile', on_delete=models.CASCADE)
profile_role = models.OneToOneField(ProfileRole, on_delete=models.CASCADE, related_name='guardian_profile', null=True, blank=True)
def __str__(self):
return self.last_name + "_" + self.first_name
@ -163,6 +164,7 @@ class Student(models.Model):
class RegistrationFileGroup(models.Model):
name = models.CharField(max_length=255, default="")
description = models.TextField(blank=True, null=True)
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='file_group', null=True, blank=True)
def __str__(self):
return self.name
@ -214,7 +216,7 @@ class RegistrationForm(models.Model):
null=True,
blank=True)
establishment = models.ForeignKey('School.Establishment', on_delete=models.CASCADE, related_name='register_forms')
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='register_forms')
def __str__(self):
return "RF_" + self.student.last_name + "_" + self.student.first_name

View File

@ -2,7 +2,7 @@ from rest_framework import serializers
from .models import RegistrationFileGroup, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationTemplateMaster, RegistrationTemplate
from School.models import SchoolClass, Fee, Discount, FeeType
from School.serializers import FeeSerializer, DiscountSerializer
from Auth.models import Profile
from Auth.models import ProfileRole
from Auth.serializers import ProfileSerializer
from GestionMessagerie.models import Messagerie
from GestionNotification.models import Notification
@ -68,7 +68,7 @@ class SiblingSerializer(serializers.ModelSerializer):
class GuardianSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
associated_profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=True)
profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=True)
associated_profile_email = serializers.SerializerMethodField()
class Meta:
@ -76,7 +76,7 @@ class GuardianSerializer(serializers.ModelSerializer):
fields = '__all__'
def get_associated_profile_email(self, obj):
return obj.associated_profile.email
return obj.profile_role.profile.email
class StudentSerializer(serializers.ModelSerializer):
@ -248,7 +248,7 @@ class GuardianByDICreationSerializer(serializers.ModelSerializer):
class Meta:
model = Guardian
fields = ['id', 'last_name', 'first_name', 'email', 'associated_profile']
fields = ['id', 'last_name', 'first_name', 'email', 'profile']
class StudentByRFCreationSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)

View File

@ -90,6 +90,12 @@ class ChildrenListView(APIView):
# Récupération des élèves d'un parent
# idProfile : identifiant du profil connecté rattaché aux fiches d'élèves
def get(self, request, id):
students = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__associated_profile__id', _value=id)
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 = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__profile_role__profile__id', _value=id)
if students:
students = students.filter(establishment=establishment_id).distinct()
students_serializer = RegistrationFormByParentSerializer(students, many=True)
return JsonResponse(students_serializer.data, safe=False)

View File

@ -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", "Establishment", "--noinput"],
["python", "manage.py", "makemigrations", "Subscriptions", "--noinput"],
["python", "manage.py", "makemigrations", "Planning", "--noinput"],
["python", "manage.py", "makemigrations", "GestionNotification", "--noinput"],