mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
Merge pull request 'refactoring' (#46) from refactoring into develop
Reviewed-on: https://git.v0id.ovh/n3wt-innov/n3wt-school/pulls/46
This commit is contained in:
1
.env
Normal file
1
.env
Normal file
@ -0,0 +1 @@
|
||||
DOCUSEAL_API_KEY="LRvUTQCbMSSpManYKshdQk9Do6rBQgjHyPrbGfxU3Jg"
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,9 +1,3 @@
|
||||
Back-End/*/Configuration/application.json
|
||||
.venv/
|
||||
__pycache__/
|
||||
node_modules/
|
||||
Back-End/*/migrations/*
|
||||
Back-End/documents
|
||||
Back-End/*.dmp
|
||||
Back-End/staticfiles
|
||||
hardcoded-strings-report.md
|
||||
7
Back-End/.gitignore
vendored
Normal file
7
Back-End/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
__pycache__
|
||||
/*/migrations/*
|
||||
documents
|
||||
data
|
||||
*.dmp
|
||||
staticfiles
|
||||
/*/Configuration/application.json
|
||||
@ -3,5 +3,5 @@ from django.db.models.signals import post_migrate
|
||||
|
||||
class GestionloginConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'GestionLogin'
|
||||
name = 'Auth'
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from GestionLogin.models import Profil
|
||||
from Auth.models import Profile
|
||||
from N3wtSchool import bdd
|
||||
|
||||
class EmailBackend(ModelBackend):
|
||||
def authenticate(self, request, username=None, password=None, **kwargs):
|
||||
|
||||
if username is None:
|
||||
username = kwargs.get(Profil.USERNAME_FIELD)
|
||||
username = kwargs.get(Profile.USERNAME_FIELD)
|
||||
|
||||
try:
|
||||
user = Profil.objects.get(email=username)
|
||||
user = Profile.objects.get(email=username)
|
||||
|
||||
# Vérifie le mot de passe de l'utilisateur
|
||||
if user.check_password(password):
|
||||
return user
|
||||
except Profil.DoesNotExist:
|
||||
except Profile.DoesNotExist:
|
||||
return None
|
||||
|
||||
32
Back-End/Auth/models.py
Normal file
32
Back-End/Auth/models.py
Normal file
@ -0,0 +1,32 @@
|
||||
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):
|
||||
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)
|
||||
|
||||
def __str__(self):
|
||||
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}"
|
||||
144
Back-End/Auth/serializers.py
Normal file
144
Back-End/Auth/serializers.py
Normal file
@ -0,0 +1,144 @@
|
||||
from rest_framework import serializers
|
||||
from Auth.models import Profile, ProfileRole
|
||||
from Establishment.models import Establishment
|
||||
from Subscriptions.models import Guardian, RegistrationForm
|
||||
from School.models import Teacher
|
||||
|
||||
class ProfileSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
password = serializers.CharField(write_only=True)
|
||||
roles = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Profile
|
||||
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles']
|
||||
extra_kwargs = {'password': {'write_only': True}}
|
||||
|
||||
def get_roles(self, obj):
|
||||
roles = ProfileRole.objects.filter(profile=obj)
|
||||
roles_data = []
|
||||
for role in roles:
|
||||
# Récupérer l'ID de l'associated_person en fonction du type de rôle
|
||||
if role.role_type == ProfileRole.RoleType.PROFIL_PARENT:
|
||||
guardian = Guardian.objects.filter(profile_role=role).first()
|
||||
id_associated_person = guardian.id if guardian else None
|
||||
else:
|
||||
teacher = Teacher.objects.filter(profile_role=role).first()
|
||||
id_associated_person = teacher.id if teacher else None
|
||||
|
||||
roles_data.append({
|
||||
'id_associated_person': id_associated_person,
|
||||
'role_type': role.role_type,
|
||||
'establishment': role.establishment.id,
|
||||
'establishment_name': role.establishment.name,
|
||||
'is_active': role.is_active,
|
||||
})
|
||||
return roles_data
|
||||
|
||||
def create(self, validated_data):
|
||||
user = Profile(
|
||||
username=validated_data['username'],
|
||||
email=validated_data['email'],
|
||||
code=validated_data.get('code', ''),
|
||||
datePeremption=validated_data.get('datePeremption', '')
|
||||
)
|
||||
user.set_password(validated_data['password'])
|
||||
user.full_clean()
|
||||
user.save()
|
||||
return user
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
password = validated_data.pop('password', None)
|
||||
instance = super().update(instance, validated_data)
|
||||
|
||||
if password:
|
||||
instance.set_password(password)
|
||||
|
||||
instance.full_clean()
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
def to_representation(self, instance):
|
||||
ret = super().to_representation(instance)
|
||||
ret['password'] = '********'
|
||||
ret['roles'] = self.get_roles(instance)
|
||||
return ret
|
||||
|
||||
class ProfileRoleSerializer(serializers.ModelSerializer):
|
||||
profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=False)
|
||||
profile_data = ProfileSerializer(write_only=True, required=False)
|
||||
associated_profile_email = serializers.SerializerMethodField()
|
||||
associated_person = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = ProfileRole
|
||||
fields = ['id', 'role_type', 'establishment', 'is_active', 'profile', 'profile_data', 'associated_profile_email', 'associated_person']
|
||||
|
||||
def create(self, validated_data):
|
||||
profile_data = validated_data.pop('profile_data', None)
|
||||
profile = validated_data.pop('profile', None)
|
||||
|
||||
if profile_data:
|
||||
profile_serializer = ProfileSerializer(data=profile_data)
|
||||
profile_serializer.is_valid(raise_exception=True)
|
||||
profile = profile_serializer.save()
|
||||
elif profile:
|
||||
profile = Profile.objects.get(id=profile.id)
|
||||
|
||||
profile_role = ProfileRole.objects.create(profile=profile, **validated_data)
|
||||
return profile_role
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
profile_data = validated_data.pop('profile_data', None)
|
||||
profile = validated_data.pop('profile', None)
|
||||
|
||||
if profile_data:
|
||||
profile_serializer = ProfileSerializer(instance.profile, data=profile_data)
|
||||
profile_serializer.is_valid(raise_exception=True)
|
||||
profile = profile_serializer.save()
|
||||
elif profile:
|
||||
profile = Profile.objects.get(id=profile.id)
|
||||
|
||||
instance.role_type = validated_data.get('role_type', instance.role_type)
|
||||
instance.establishment_id = validated_data.get('establishment', instance.establishment.id)
|
||||
instance.is_active = validated_data.get('is_active', instance.is_active)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
def get_associated_profile_email(self, obj):
|
||||
if obj.profile:
|
||||
return obj.profile.email
|
||||
return None
|
||||
|
||||
def get_associated_person(self, obj):
|
||||
if obj.role_type == ProfileRole.RoleType.PROFIL_PARENT:
|
||||
guardian = Guardian.objects.filter(profile_role=obj).first()
|
||||
if guardian:
|
||||
students = guardian.student_set.all()
|
||||
students_list = []
|
||||
for student in students:
|
||||
registration_form = RegistrationForm.objects.filter(student=student).first()
|
||||
registration_status = registration_form.status if registration_form else None
|
||||
students_list.append({
|
||||
"student_name": f"{student.last_name} {student.first_name}",
|
||||
"registration_status": registration_status
|
||||
})
|
||||
return {
|
||||
"id": guardian.id,
|
||||
"guardian_name": f"{guardian.last_name} {guardian.first_name}",
|
||||
"students": students_list
|
||||
}
|
||||
else:
|
||||
teacher = Teacher.objects.filter(profile_role=obj).first()
|
||||
if teacher:
|
||||
classes = teacher.schoolclass_set.all()
|
||||
classes_list = [{"id": classe.id, "name": classe.atmosphere_name} for classe in classes]
|
||||
specialities = teacher.specialities.all()
|
||||
specialities_list = [{"name": speciality.name, "color_code": speciality.color_code} for speciality in specialities]
|
||||
return {
|
||||
"id": teacher.id,
|
||||
"teacher_name": f"{teacher.last_name} {teacher.first_name}",
|
||||
"classes": classes_list,
|
||||
"specialities": specialities_list
|
||||
}
|
||||
return None
|
||||
22
Back-End/Auth/urls.py
Normal file
22
Back-End/Auth/urls.py
Normal file
@ -0,0 +1,22 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import views
|
||||
import Auth.views
|
||||
from Auth.views import ProfileRoleView, ProfileRoleSimpleView, ProfileSimpleView, ProfileView, SessionView, LoginView, RefreshJWTView, SubscribeView, NewPasswordView, ResetPasswordView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^csrf$', Auth.views.csrf, name='csrf'),
|
||||
|
||||
re_path(r'^login$', LoginView.as_view(), name="login"),
|
||||
re_path(r'^refreshJWT$', RefreshJWTView.as_view(), name="refresh_jwt"),
|
||||
re_path(r'^subscribe$', SubscribeView.as_view(), name='subscribe'),
|
||||
re_path(r'^newPassword$', NewPasswordView.as_view(), name='newPassword'),
|
||||
re_path(r'^resetPassword/(?P<code>[a-zA-Z]+)$', ResetPasswordView.as_view(), name='resetPassword'),
|
||||
re_path(r'^infoSession$', SessionView.as_view(), name='infoSession'),
|
||||
|
||||
re_path(r'^profiles$', ProfileView.as_view(), name="profile"),
|
||||
re_path(r'^profiles/(?P<id>[0-9]+)$', ProfileSimpleView.as_view(), name="profile"),
|
||||
|
||||
re_path(r'^profileRoles$', ProfileRoleView.as_view(), name="profileRoles"),
|
||||
re_path(r'^profileRoles/(?P<id>[0-9]+)$', ProfileRoleSimpleView.as_view(), name="profileRoles"),
|
||||
]
|
||||
606
Back-End/Auth/views.py
Normal file
606
Back-End/Auth/views.py
Normal file
@ -0,0 +1,606 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import login, authenticate, get_user_model
|
||||
from django.http.response import JsonResponse
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt, csrf_protect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.middleware.csrf import get_token
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.parsers import JSONParser
|
||||
from rest_framework import status
|
||||
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
import jwt
|
||||
from jwt.exceptions import ExpiredSignatureError, InvalidTokenError
|
||||
import json
|
||||
|
||||
from . import validator
|
||||
from .models import Profile, ProfileRole
|
||||
from rest_framework.decorators import action, api_view
|
||||
|
||||
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
|
||||
from Subscriptions.models import RegistrationForm
|
||||
import Subscriptions.mailManager as mailer
|
||||
import Subscriptions.util as util
|
||||
import logging
|
||||
from N3wtSchool import bdd, error
|
||||
|
||||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||
|
||||
logger = logging.getLogger("AuthViews")
|
||||
|
||||
|
||||
@swagger_auto_schema(
|
||||
method='get',
|
||||
operation_description="Obtenir un token CSRF",
|
||||
responses={200: openapi.Response('Token CSRF', schema=openapi.Schema(type=openapi.TYPE_OBJECT, properties={
|
||||
'csrfToken': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}))}
|
||||
)
|
||||
@api_view(['GET'])
|
||||
def csrf(request):
|
||||
token = get_token(request)
|
||||
return JsonResponse({'csrfToken': token})
|
||||
|
||||
class SessionView(APIView):
|
||||
@swagger_auto_schema(
|
||||
operation_description="Vérifier une session utilisateur",
|
||||
manual_parameters=[openapi.Parameter('Authorization', openapi.IN_HEADER, type=openapi.TYPE_STRING, description='Bearer token')],
|
||||
responses={
|
||||
200: openapi.Response('Session valide', schema=openapi.Schema(type=openapi.TYPE_OBJECT, properties={
|
||||
'user': openapi.Schema(type=openapi.TYPE_OBJECT, properties={
|
||||
'id': openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||
'email': 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')
|
||||
}
|
||||
)
|
||||
def get(self, request):
|
||||
token = request.META.get('HTTP_AUTHORIZATION', '').split('Bearer ')[-1]
|
||||
|
||||
try:
|
||||
decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
|
||||
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,
|
||||
'roles': list(roles)
|
||||
}
|
||||
}
|
||||
return JsonResponse(response_data, status=status.HTTP_200_OK)
|
||||
except jwt.ExpiredSignatureError:
|
||||
return JsonResponse({"error": "Token has expired"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
except jwt.InvalidTokenError:
|
||||
return JsonResponse({"error": "Invalid token"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
class ProfileView(APIView):
|
||||
@swagger_auto_schema(
|
||||
operation_description="Obtenir la liste des profils",
|
||||
responses={200: ProfileSerializer(many=True)}
|
||||
)
|
||||
def get(self, request):
|
||||
profilsList = bdd.getAllObjects(_objectName=Profile)
|
||||
profils_serializer = ProfileSerializer(profilsList, many=True)
|
||||
return JsonResponse(profils_serializer.data, safe=False)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Créer un nouveau profil",
|
||||
request_body=ProfileSerializer,
|
||||
responses={
|
||||
200: ProfileSerializer,
|
||||
400: 'Données invalides'
|
||||
}
|
||||
)
|
||||
def post(self, request):
|
||||
profil_data = JSONParser().parse(request)
|
||||
profil_serializer = ProfileSerializer(data=profil_data)
|
||||
|
||||
if profil_serializer.is_valid():
|
||||
profil = profil_serializer.save()
|
||||
return JsonResponse(profil_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ProfileSimpleView(APIView):
|
||||
@swagger_auto_schema(
|
||||
operation_description="Obtenir un profil par son ID",
|
||||
responses={200: ProfileSerializer}
|
||||
)
|
||||
def get(self, request, id):
|
||||
profil = bdd.getObject(Profile, "id", id)
|
||||
profil_serializer = ProfileSerializer(profil)
|
||||
return JsonResponse(profil_serializer.data, safe=False)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Mettre à jour un profil",
|
||||
request_body=ProfileSerializer,
|
||||
responses={
|
||||
200: 'Mise à jour réussie',
|
||||
400: 'Données invalides'
|
||||
}
|
||||
)
|
||||
def put(self, request, id):
|
||||
data = JSONParser().parse(request)
|
||||
profil = Profile.objects.get(id=id)
|
||||
profil_serializer = ProfileSerializer(profil, data=data)
|
||||
if profil_serializer.is_valid():
|
||||
profil_serializer.save()
|
||||
return JsonResponse(profil_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Supprimer un profil",
|
||||
responses={200: 'Suppression réussie'}
|
||||
)
|
||||
def delete(self, request, id):
|
||||
return bdd.delete_object(Profile, id)
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class LoginView(APIView):
|
||||
@swagger_auto_schema(
|
||||
operation_description="Connexion utilisateur",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['email', 'password', 'role_type'],
|
||||
properties={
|
||||
'email': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'password': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'role_type': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
),
|
||||
responses={
|
||||
200: openapi.Response('Connexion réussie', schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'token': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'refresh': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
)),
|
||||
400: openapi.Response('Connexion échouée', schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT),
|
||||
'errorMessage': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
))
|
||||
}
|
||||
)
|
||||
def post(self, request):
|
||||
data = JSONParser().parse(request)
|
||||
validatorAuthentication = validator.ValidatorAuthentication(data=data)
|
||||
retour = error.returnMessage[error.WRONG_ID]
|
||||
validationOk, errorFields = validatorAuthentication.validate()
|
||||
user = None
|
||||
|
||||
if validationOk:
|
||||
user = authenticate(
|
||||
email=data.get('email'),
|
||||
password=data.get('password'),
|
||||
)
|
||||
if user is not None:
|
||||
role_type = data.get('role_type')
|
||||
primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type, is_active=True).first()
|
||||
|
||||
if not primary_role:
|
||||
return JsonResponse({"errorMessage": "Profil inactif"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
login(request, user)
|
||||
user.save()
|
||||
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.WRONG_ID]
|
||||
|
||||
return JsonResponse({
|
||||
'errorFields': errorFields,
|
||||
'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",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['refresh'],
|
||||
properties={
|
||||
'refresh': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
),
|
||||
responses={
|
||||
200: openapi.Response('Token rafraîchi avec succès', schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'token': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'refresh': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
}
|
||||
)),
|
||||
400: openapi.Response('Échec du rafraîchissement', schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'errorMessage': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
))
|
||||
}
|
||||
)
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
def post(self, request):
|
||||
data = JSONParser().parse(request)
|
||||
refresh_token = data.get("refresh")
|
||||
logger.info(f"Token reçu: {refresh_token[:20]}...") # Ne pas logger le token complet pour la sécurité
|
||||
|
||||
if not refresh_token:
|
||||
return JsonResponse({'errorMessage': 'Refresh token manquant'}, status=400)
|
||||
|
||||
try:
|
||||
# Décoder le Refresh Token
|
||||
logger.info("Tentative de décodage du token")
|
||||
logger.info(f"Algorithme utilisé: {settings.SIMPLE_JWT['ALGORITHM']}")
|
||||
|
||||
# Vérifier le format du token avant décodage
|
||||
token_parts = refresh_token.split('.')
|
||||
if len(token_parts) != 3:
|
||||
logger.error("Format de token invalide - pas 3 parties")
|
||||
return JsonResponse({'errorMessage': 'Format de token invalide'}, status=400)
|
||||
|
||||
payload = jwt.decode(
|
||||
refresh_token,
|
||||
settings.SIMPLE_JWT['SIGNING_KEY'],
|
||||
algorithms=[settings.SIMPLE_JWT['ALGORITHM']] # Noter le passage en liste
|
||||
)
|
||||
|
||||
logger.info(f"Token décodé avec succès. Type: {payload.get('type')}")
|
||||
# Vérifier s'il s'agit bien d'un Refresh Token
|
||||
if payload.get('type') != 'refresh':
|
||||
return JsonResponse({'errorMessage': 'Token invalide'}, status=400)
|
||||
|
||||
# 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, is_active=True).first()
|
||||
|
||||
if not primary_role:
|
||||
return JsonResponse({'errorMessage': 'Profil inactif'}, status=400)
|
||||
|
||||
# Générer un nouveau Access Token avec les informations complètes
|
||||
new_access_payload = {
|
||||
'user_id': user.id,
|
||||
'email': user.email,
|
||||
'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(),
|
||||
}
|
||||
|
||||
new_access_token = jwt.encode(new_access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
||||
|
||||
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(),
|
||||
}
|
||||
new_refresh_token = jwt.encode(new_refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
||||
|
||||
return JsonResponse({'token': new_access_token, 'refresh': new_refresh_token}, status=200)
|
||||
|
||||
except ExpiredSignatureError as e:
|
||||
logger.error(f"Token expiré: {str(e)}")
|
||||
return JsonResponse({'errorMessage': 'Refresh token expiré'}, status=400)
|
||||
except InvalidTokenError as e:
|
||||
logger.error(f"Token invalide: {str(e)}")
|
||||
return JsonResponse({'errorMessage': f'Token invalide: {str(e)}'}, status=400)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur inattendue: {str(e)}")
|
||||
return JsonResponse({'errorMessage': f'Erreur inattendue: {str(e)}'}, status=400)
|
||||
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
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'],
|
||||
properties={
|
||||
'email': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'password1': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'password2': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
),
|
||||
responses={
|
||||
200: openapi.Response('Inscription réussie', schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'message': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'errorMessage': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT),
|
||||
'id': openapi.Schema(type=openapi.TYPE_INTEGER)
|
||||
}
|
||||
))
|
||||
}
|
||||
)
|
||||
def post(self, request):
|
||||
retourErreur = error.returnMessage[error.BAD_URL]
|
||||
retour = ''
|
||||
newProfilConnection = JSONParser().parse(request)
|
||||
establishment_id = newProfilConnection['establishment_id']
|
||||
|
||||
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 is None:
|
||||
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
|
||||
else:
|
||||
# Vérifier si le profil a déjà un rôle actif pour l'établissement donné
|
||||
active_roles = ProfileRole.objects.filter(profile=profil, establishment=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.full_clean()
|
||||
profil.save()
|
||||
|
||||
# Récupérer le ProfileRole existant pour l'établissement et le profil
|
||||
profile_role = ProfileRole.objects.filter(profile=profil, establishment=establishment_id).first()
|
||||
if profile_role:
|
||||
profile_role.is_active = True
|
||||
profile_role.save()
|
||||
else:
|
||||
# Si aucun ProfileRole n'existe, en créer un nouveau
|
||||
role_data = {
|
||||
'profile': profil.id,
|
||||
'establishment': establishment_id,
|
||||
'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)
|
||||
|
||||
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
|
||||
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, "id": -1}, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class NewPasswordView(APIView):
|
||||
@swagger_auto_schema(
|
||||
operation_description="Demande de nouveau mot de passe",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['email'],
|
||||
properties={
|
||||
'email': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
),
|
||||
responses={
|
||||
200: openapi.Response('Demande réussie', schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'message': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'errorMessage': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT)
|
||||
}
|
||||
))
|
||||
}
|
||||
)
|
||||
def post(self, request):
|
||||
retourErreur = error.returnMessage[error.BAD_URL]
|
||||
retour = ''
|
||||
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 is None:
|
||||
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
|
||||
else:
|
||||
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()
|
||||
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)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ResetPasswordView(APIView):
|
||||
@swagger_auto_schema(
|
||||
operation_description="Réinitialisation du mot de passe",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['password1', 'password2'],
|
||||
properties={
|
||||
'password1': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'password2': openapi.Schema(type=openapi.TYPE_STRING)
|
||||
}
|
||||
),
|
||||
responses={
|
||||
200: openapi.Response('Réinitialisation réussie', schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'message': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'errorMessage': openapi.Schema(type=openapi.TYPE_STRING),
|
||||
'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT)
|
||||
}
|
||||
))
|
||||
}
|
||||
)
|
||||
def post(self, request, code):
|
||||
retourErreur = error.returnMessage[error.BAD_URL]
|
||||
retour = ''
|
||||
newProfilConnection = JSONParser().parse(request)
|
||||
|
||||
validatorResetPassword = validator.ValidatorResetPassword(data=newProfilConnection)
|
||||
validationOk, errorFields = validatorResetPassword.validate()
|
||||
|
||||
profil = bdd.getObject(Profile, "code", code)
|
||||
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)
|
||||
elif validationOk:
|
||||
retour = error.returnMessage[error.PASSWORD_CHANGED]
|
||||
|
||||
profil.set_password(newProfilConnection.get('password1'))
|
||||
profil.code = ''
|
||||
profil.datePeremption = ''
|
||||
profil.save()
|
||||
retourErreur = ''
|
||||
|
||||
return JsonResponse({'message': retour, "errorMessage": retourErreur, "errorFields": errorFields}, safe=False)
|
||||
|
||||
class ProfileRoleView(APIView):
|
||||
@swagger_auto_schema(
|
||||
operation_description="Obtenir la liste des profile_roles",
|
||||
responses={200: ProfileRoleSerializer(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)
|
||||
|
||||
profiles_roles_List = bdd.getAllObjects(_objectName=ProfileRole)
|
||||
if profiles_roles_List:
|
||||
profiles_roles_List = profiles_roles_List.filter(establishment=establishment_id).distinct()
|
||||
profile_roles_serializer = ProfileRoleSerializer(profiles_roles_List, many=True)
|
||||
return JsonResponse(profile_roles_serializer.data, safe=False)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Créer un nouveau profile_role",
|
||||
request_body=ProfileRoleSerializer,
|
||||
responses={
|
||||
200: ProfileRoleSerializer,
|
||||
400: 'Données invalides'
|
||||
}
|
||||
)
|
||||
def post(self, request):
|
||||
profile_role_data = JSONParser().parse(request)
|
||||
profile_role_serializer = ProfileRoleSerializer(data=profile_role_data)
|
||||
|
||||
if profile_role_serializer.is_valid():
|
||||
profile_role = profile_role_serializer.save()
|
||||
return JsonResponse(profile_role_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(profile_role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ProfileRoleSimpleView(APIView):
|
||||
@swagger_auto_schema(
|
||||
operation_description="Obtenir un profile_role par son ID",
|
||||
responses={200: ProfileRoleSerializer}
|
||||
)
|
||||
def get(self, request, id):
|
||||
profile_role = bdd.getObject(ProfileRole, "id", id)
|
||||
profile_role_serializer = ProfileRoleSerializer(profile_role)
|
||||
return JsonResponse(profile_role_serializer.data, safe=False)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Mettre à jour un profile_role",
|
||||
request_body=ProfileRoleSerializer,
|
||||
responses={
|
||||
200: 'Mise à jour réussie',
|
||||
400: 'Données invalides'
|
||||
}
|
||||
)
|
||||
def put(self, request, id):
|
||||
data = JSONParser().parse(request)
|
||||
profile_role = ProfileRole.objects.get(id=id)
|
||||
profile_role_serializer = ProfileRoleSerializer(profile_role, data=data)
|
||||
if profile_role_serializer.is_valid():
|
||||
profile_role_serializer.save()
|
||||
return JsonResponse(profile_role_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(profile_role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@swagger_auto_schema(
|
||||
operation_description="Supprimer un profile_role",
|
||||
responses={200: 'Suppression réussie'}
|
||||
)
|
||||
def delete(self, request, id):
|
||||
profile_role = ProfileRole.objects.get(id=id)
|
||||
profile = profile_role.profile
|
||||
profile_role.delete()
|
||||
|
||||
# Vérifier si le profil n'a plus de rôles associés
|
||||
if not ProfileRole.objects.filter(profile=profile).exists():
|
||||
profile.delete()
|
||||
|
||||
return JsonResponse({'message': 'Suppression réussie'}, safe=False)
|
||||
@ -3,14 +3,15 @@
|
||||
# The first instruction is what image we want to base our container on
|
||||
# We Use an official Python runtime as a parent image
|
||||
FROM python:3.12.7
|
||||
WORKDIR /Back-End
|
||||
|
||||
# Allows docker to cache installed dependencies between builds
|
||||
COPY requirements.txt requirements.txt
|
||||
RUN pip install -r requirements.txt
|
||||
RUN pip install pymupdf
|
||||
|
||||
# Mounts the application code to the image
|
||||
COPY . .
|
||||
WORKDIR /Back-End
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
|
||||
1
Back-End/DocuSeal/__init__.py
Normal file
1
Back-End/DocuSeal/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
# This file is intentionally left blank to make this directory a Python package.
|
||||
9
Back-End/DocuSeal/urls.py
Normal file
9
Back-End/DocuSeal/urls.py
Normal file
@ -0,0 +1,9 @@
|
||||
from django.urls import path, re_path
|
||||
from .views import generate_jwt_token, clone_template, remove_template, download_template
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'generateToken$', generate_jwt_token, name='generate_jwt_token'),
|
||||
re_path(r'cloneTemplate$', clone_template, name='clone_template'),
|
||||
re_path(r'removeTemplate/(?P<id>[0-9]+)$', remove_template, name='remove_template'),
|
||||
re_path(r'downloadTemplate/(?P<slug>[\w-]+)$', download_template, name='download_template')
|
||||
]
|
||||
164
Back-End/DocuSeal/views.py
Normal file
164
Back-End/DocuSeal/views.py
Normal file
@ -0,0 +1,164 @@
|
||||
from django.conf import settings
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
import jwt
|
||||
import datetime
|
||||
import requests
|
||||
|
||||
@csrf_exempt
|
||||
@api_view(['POST'])
|
||||
def generate_jwt_token(request):
|
||||
# Vérifier la clé API
|
||||
api_key = request.headers.get('X-Auth-Token')
|
||||
if not api_key or api_key != settings.DOCUSEAL_JWT["API_KEY"]:
|
||||
return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
# Récupérer les données de la requête
|
||||
user_email = request.data.get('user_email')
|
||||
documents_urls = request.data.get('documents_urls', [])
|
||||
id = request.data.get('id') # Récupérer le id
|
||||
|
||||
# Vérifier les données requises
|
||||
if not user_email:
|
||||
return Response({'error': 'User email is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Utiliser la configuration JWT de DocuSeal depuis les settings
|
||||
jwt_secret = settings.DOCUSEAL_JWT['API_KEY']
|
||||
jwt_algorithm = settings.DOCUSEAL_JWT['ALGORITHM']
|
||||
expiration_delta = settings.DOCUSEAL_JWT['EXPIRATION_DELTA']
|
||||
|
||||
# Définir le payload
|
||||
payload = {
|
||||
'user_email': user_email,
|
||||
'documents_urls': documents_urls,
|
||||
'template_id': id, # Ajouter le id au payload
|
||||
'exp': datetime.datetime.utcnow() + expiration_delta # Temps d'expiration du token
|
||||
}
|
||||
|
||||
# Générer le token JWT
|
||||
token = jwt.encode(payload, jwt_secret, algorithm=jwt_algorithm)
|
||||
|
||||
return Response({'token': token}, status=status.HTTP_200_OK)
|
||||
|
||||
@csrf_exempt
|
||||
@api_view(['POST'])
|
||||
def clone_template(request):
|
||||
# Vérifier la clé API
|
||||
api_key = request.headers.get('X-Auth-Token')
|
||||
if not api_key or api_key != settings.DOCUSEAL_JWT["API_KEY"]:
|
||||
return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
# Récupérer les données de la requête
|
||||
document_id = request.data.get('templateId')
|
||||
email = request.data.get('email')
|
||||
is_required = request.data.get('is_required')
|
||||
print(f'test is required = {is_required}')
|
||||
|
||||
# Vérifier les données requises
|
||||
if not document_id :
|
||||
return Response({'error': 'template ID is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# URL de l'API de DocuSeal pour cloner le template
|
||||
clone_url = f'https://docuseal.com/api/templates/{document_id}/clone'
|
||||
|
||||
# Faire la requête pour cloner le template
|
||||
try:
|
||||
response = requests.post(clone_url, headers={
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
|
||||
})
|
||||
|
||||
if response.status_code != status.HTTP_200_OK:
|
||||
return Response({'error': 'Failed to clone template'}, status=response.status_code)
|
||||
|
||||
data = response.json()
|
||||
|
||||
if is_required:
|
||||
print(f'REQUIRED -> création dune submission')
|
||||
# URL de l'API de DocuSeal pour créer une submission
|
||||
submission_url = f'https://docuseal.com/api/submissions'
|
||||
|
||||
# Faire la requête pour cloner le template
|
||||
try:
|
||||
clone_id = data['id']
|
||||
response = requests.post(submission_url, json={'template_id':clone_id, 'send_email': False, 'submitters': [{'email': email}]}, headers={
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
|
||||
})
|
||||
|
||||
if response.status_code != status.HTTP_200_OK:
|
||||
return Response({'error': 'Failed to create submission'}, status=response.status_code)
|
||||
|
||||
data = response.json()
|
||||
data[0]['id'] = clone_id
|
||||
print(f'DATA RESPONSE : {data[0]}')
|
||||
return Response(data[0], status=status.HTTP_200_OK)
|
||||
|
||||
except requests.RequestException as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
else :
|
||||
print(f'NOT REQUIRED -> on ne crée pas de submission')
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
||||
except requests.RequestException as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
@csrf_exempt
|
||||
@api_view(['DELETE'])
|
||||
def remove_template(request, id):
|
||||
# Vérifier la clé API
|
||||
api_key = request.headers.get('X-Auth-Token')
|
||||
if not api_key or api_key != settings.DOCUSEAL_JWT["API_KEY"]:
|
||||
return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
# URL de l'API de DocuSeal pour cloner le template
|
||||
clone_url = f'https://docuseal.com/api/templates/{id}'
|
||||
|
||||
# Faire la requête pour cloner le template
|
||||
try:
|
||||
response = requests.delete(clone_url, headers={
|
||||
'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
|
||||
})
|
||||
|
||||
if response.status_code != status.HTTP_200_OK:
|
||||
return Response({'error': 'Failed to remove template'}, status=response.status_code)
|
||||
|
||||
data = response.json()
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
||||
except requests.RequestException as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
@csrf_exempt
|
||||
@api_view(['GET'])
|
||||
def download_template(request, slug):
|
||||
# Vérifier la clé API
|
||||
api_key = request.headers.get('X-Auth-Token')
|
||||
if not api_key or api_key != settings.DOCUSEAL_JWT["API_KEY"]:
|
||||
return Response({'error': 'Invalid API key'}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
# Vérifier les données requises
|
||||
if not slug :
|
||||
return Response({'error': 'slug is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# URL de l'API de DocuSeal pour cloner le template
|
||||
download_url = f'https://docuseal.com/submitters/{slug}/download'
|
||||
|
||||
# Faire la requête pour cloner le template
|
||||
try:
|
||||
response = requests.get(download_url, headers={
|
||||
'Content-Type': 'application/json',
|
||||
'X-Auth-Token': settings.DOCUSEAL_JWT['API_KEY']
|
||||
})
|
||||
|
||||
if response.status_code != status.HTTP_200_OK:
|
||||
return Response({'error': 'Failed to download template'}, status=response.status_code)
|
||||
|
||||
data = response.json()
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
||||
except requests.RequestException as e:
|
||||
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
1
Back-End/Establishment/__init__.py
Normal file
1
Back-End/Establishment/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
default_app_config = 'Establishment.apps.EstablishmentConfig'
|
||||
@ -1,6 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class GestionenseignantsConfig(AppConfig):
|
||||
class EstablishmentConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'GestionEnseignants'
|
||||
name = 'Establishment'
|
||||
|
||||
|
||||
20
Back-End/Establishment/models.py
Normal file
20
Back-End/Establishment/models.py
Normal 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
|
||||
91
Back-End/Establishment/serializers.py
Normal file
91
Back-End/Establishment/serializers.py
Normal 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'))
|
||||
7
Back-End/Establishment/urls.py
Normal file
7
Back-End/Establishment/urls.py
Normal 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"),
|
||||
]
|
||||
51
Back-End/Establishment/views.py
Normal file
51
Back-End/Establishment/views.py
Normal 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)
|
||||
@ -1 +0,0 @@
|
||||
default_app_config = 'GestionEnseignants.apps.GestionenseignantsConfig'
|
||||
@ -1,32 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class Specialite(models.Model):
|
||||
nom = models.CharField(max_length=100)
|
||||
dateCreation = models.DateTimeField(auto_now=True)
|
||||
codeCouleur = models.CharField(max_length=7, default='#FFFFFF')
|
||||
|
||||
def __str__(self):
|
||||
return self.nom
|
||||
|
||||
class Enseignant(models.Model):
|
||||
nom = models.CharField(max_length=100)
|
||||
prenom = models.CharField(max_length=100)
|
||||
mail = models.EmailField(unique=True)
|
||||
specialite = models.ForeignKey(Specialite, on_delete=models.SET_NULL, null=True, blank=True, related_name='enseignants')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.nom} {self.prenom}"
|
||||
|
||||
class Classe(models.Model):
|
||||
nom_ambiance = models.CharField(max_length=255)
|
||||
tranche_age = models.JSONField()
|
||||
nombre_eleves = models.PositiveIntegerField()
|
||||
langue_enseignement = models.CharField(max_length=255)
|
||||
annee_scolaire = models.CharField(max_length=9)
|
||||
dateCreation = models.DateTimeField(auto_now_add=True)
|
||||
specialites = models.ManyToManyField(Specialite, related_name='classes')
|
||||
enseignant_principal = models.ForeignKey(Enseignant, on_delete=models.SET_NULL, null=True, blank=True, related_name='classes_principal')
|
||||
|
||||
def __str__(self):
|
||||
return self.nom_ambiance
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import Enseignant, Specialite, Classe
|
||||
from N3wtSchool import settings
|
||||
from django.utils import timezone
|
||||
import pytz
|
||||
|
||||
class SpecialiteSerializer(serializers.ModelSerializer):
|
||||
dateCreation_formattee = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = Specialite
|
||||
fields = '__all__'
|
||||
|
||||
def get_dateCreation_formattee(self, obj):
|
||||
utc_time = timezone.localtime(obj.dateCreation) # Convertir en heure locale
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
local_time = utc_time.astimezone(local_tz)
|
||||
|
||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
class ClasseSerializer(serializers.ModelSerializer):
|
||||
specialites = SpecialiteSerializer(many=True, read_only=True)
|
||||
specialites_ids = serializers.PrimaryKeyRelatedField(queryset=Specialite.objects.all(), many=True, source='specialites')
|
||||
dateCreation_formattee = serializers.SerializerMethodField()
|
||||
enseignant_principal = serializers.SerializerMethodField()
|
||||
enseignant_principal_id = serializers.PrimaryKeyRelatedField(queryset=Enseignant.objects.all(), source='enseignant_principal', write_only=False, read_only=False)
|
||||
|
||||
class Meta:
|
||||
model = Classe
|
||||
fields = ['id', 'nom_ambiance', 'tranche_age', 'nombre_eleves', 'langue_enseignement', 'specialites', 'specialites_ids', 'enseignant_principal', 'enseignant_principal_id', 'annee_scolaire', 'dateCreation', 'dateCreation_formattee']
|
||||
|
||||
def get_enseignant_principal(self, obj):
|
||||
from .serializers import EnseignantDetailSerializer
|
||||
if obj.enseignant_principal:
|
||||
return EnseignantDetailSerializer(obj.enseignant_principal).data
|
||||
return None
|
||||
|
||||
def create(self, validated_data):
|
||||
specialites_data = validated_data.pop('specialites', [])
|
||||
classe = Classe.objects.create(**validated_data)
|
||||
classe.specialites.set(specialites_data)
|
||||
return classe
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
specialites_data = validated_data.pop('specialites', [])
|
||||
instance.nom_ambiance = validated_data.get('nom_ambiance', instance.nom_ambiance)
|
||||
instance.tranche_age = validated_data.get('tranche_age', instance.tranche_age)
|
||||
instance.nombre_eleves = validated_data.get('nombre_eleves', instance.nombre_eleves)
|
||||
instance.langue_enseignement = validated_data.get('langue_enseignement', instance.langue_enseignement)
|
||||
instance.annee_scolaire = validated_data.get('annee_scolaire', instance.annee_scolaire)
|
||||
instance.enseignant_principal = validated_data.get('enseignant_principal', instance.enseignant_principal)
|
||||
instance.save()
|
||||
instance.specialites.set(specialites_data)
|
||||
return instance
|
||||
|
||||
def get_dateCreation_formattee(self, obj):
|
||||
utc_time = timezone.localtime(obj.dateCreation) # Convertir en heure locale
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
local_time = utc_time.astimezone(local_tz)
|
||||
|
||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
class EnseignantSerializer(serializers.ModelSerializer):
|
||||
specialite = SpecialiteSerializer(read_only=True)
|
||||
specialite_id = serializers.PrimaryKeyRelatedField(queryset=Specialite.objects.all(), source='specialite', write_only=False, read_only=False)
|
||||
classes_principal = ClasseSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Enseignant
|
||||
fields = ['id', 'nom', 'prenom', 'mail', 'specialite', 'specialite_id', 'classes_principal']
|
||||
|
||||
def create(self, validated_data):
|
||||
specialite = validated_data.pop('specialite', None)
|
||||
enseignant = Enseignant.objects.create(**validated_data)
|
||||
enseignant.specialite = specialite
|
||||
enseignant.save()
|
||||
return enseignant
|
||||
|
||||
class EnseignantDetailSerializer(serializers.ModelSerializer):
|
||||
specialite = SpecialiteSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Enseignant
|
||||
fields = ['id', 'nom', 'prenom', 'mail', 'specialite']
|
||||
@ -1,17 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from GestionEnseignants.views import EnseignantsView, EnseignantView, SpecialitesView, SpecialiteView, ClassesView, ClasseView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^enseignants$', EnseignantsView.as_view(), name="enseignants"),
|
||||
re_path(r'^enseignant$', EnseignantView.as_view(), name="enseignant"),
|
||||
re_path(r'^enseignant/([0-9]+)$', EnseignantView.as_view(), name="enseignant"),
|
||||
|
||||
re_path(r'^specialites$', SpecialitesView.as_view(), name="specialites"),
|
||||
re_path(r'^specialite$', SpecialiteView.as_view(), name="specialite"),
|
||||
re_path(r'^specialite/([0-9]+)$', SpecialiteView.as_view(), name="specialite"),
|
||||
|
||||
re_path(r'^classes$', ClassesView.as_view(), name="classes"),
|
||||
re_path(r'^classe$', ClasseView.as_view(), name="classe"),
|
||||
re_path(r'^classe/([0-9]+)$', ClasseView.as_view(), name="classe"),
|
||||
]
|
||||
@ -1,180 +0,0 @@
|
||||
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 django.core.cache import cache
|
||||
from .models import Enseignant, Specialite, Classe
|
||||
from .serializers import EnseignantSerializer, SpecialiteSerializer, ClasseSerializer
|
||||
from N3wtSchool import bdd
|
||||
|
||||
class EnseignantsView(APIView):
|
||||
def get(self, request):
|
||||
enseignantsList=bdd.getAllObjects(Enseignant)
|
||||
enseignants_serializer=EnseignantSerializer(enseignantsList, many=True)
|
||||
|
||||
return JsonResponse(enseignants_serializer.data, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class EnseignantView(APIView):
|
||||
def get (self, request, _id):
|
||||
enseignant = bdd.getObject(_objectName=Enseignant, _columnName='id', _value=_id)
|
||||
enseignant_serializer=EnseignantSerializer(enseignant)
|
||||
|
||||
return JsonResponse(enseignant_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
enseignant_data=JSONParser().parse(request)
|
||||
enseignant_serializer = EnseignantSerializer(data=enseignant_data)
|
||||
|
||||
if enseignant_serializer.is_valid():
|
||||
enseignant_serializer.save()
|
||||
|
||||
return JsonResponse(enseignant_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(enseignant_serializer.errors, safe=False)
|
||||
|
||||
def put(self, request, _id):
|
||||
enseignant_data=JSONParser().parse(request)
|
||||
enseignant = bdd.getObject(_objectName=Enseignant, _columnName='id', _value=_id)
|
||||
enseignant_serializer = EnseignantSerializer(enseignant, data=enseignant_data)
|
||||
if enseignant_serializer.is_valid():
|
||||
enseignant_serializer.save()
|
||||
return JsonResponse(enseignant_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(enseignant_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, _id):
|
||||
enseignant = bdd.getObject(_objectName=Enseignant, _columnName='id', _value=_id)
|
||||
if enseignant != None:
|
||||
enseignant.delete()
|
||||
|
||||
return JsonResponse("La suppression de la spécialité a été effectuée avec succès", safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class SpecialitesView(APIView):
|
||||
def get(self, request):
|
||||
specialitesList=bdd.getAllObjects(Specialite)
|
||||
specialites_serializer=SpecialiteSerializer(specialitesList, many=True)
|
||||
|
||||
return JsonResponse(specialites_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
specialites_data=JSONParser().parse(request)
|
||||
all_valid = True
|
||||
for specialite_data in specialites_data:
|
||||
specialite_serializer = SpecialiteSerializer(data=specialite_data)
|
||||
|
||||
if specialite_serializer.is_valid():
|
||||
specialite_serializer.save()
|
||||
else:
|
||||
all_valid = False
|
||||
break
|
||||
if all_valid:
|
||||
specialitesList = bdd.getAllObjects(Specialite)
|
||||
specialites_serializer = SpecialiteSerializer(specialitesList, many=True)
|
||||
|
||||
return JsonResponse(specialite_serializer.errors, safe=False)
|
||||
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class SpecialiteView(APIView):
|
||||
def get (self, request, _id):
|
||||
specialite = bdd.getObject(_objectName=Specialite, _columnName='id', _value=_id)
|
||||
specialite_serializer=SpecialiteSerializer(specialite)
|
||||
|
||||
return JsonResponse(specialite_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
specialite_data=JSONParser().parse(request)
|
||||
specialite_serializer = SpecialiteSerializer(data=specialite_data)
|
||||
|
||||
if specialite_serializer.is_valid():
|
||||
specialite_serializer.save()
|
||||
return JsonResponse(specialite_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(specialite_serializer.errors, safe=False)
|
||||
|
||||
def put(self, request, _id):
|
||||
specialite_data=JSONParser().parse(request)
|
||||
specialite = bdd.getObject(_objectName=Specialite, _columnName='id', _value=_id)
|
||||
specialite_serializer = SpecialiteSerializer(specialite, data=specialite_data)
|
||||
if specialite_serializer.is_valid():
|
||||
specialite_serializer.save()
|
||||
return JsonResponse(specialite_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(specialite_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, _id):
|
||||
specialite = bdd.getObject(_objectName=Specialite, _columnName='id', _value=_id)
|
||||
if specialite != None:
|
||||
specialite.delete()
|
||||
|
||||
return JsonResponse("La suppression de la spécialité a été effectuée avec succès", safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ClassesView(APIView):
|
||||
def get(self, request):
|
||||
classesList=bdd.getAllObjects(Classe)
|
||||
classes_serializer=ClasseSerializer(classesList, many=True)
|
||||
return JsonResponse(classes_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
all_valid = True
|
||||
classes_data=JSONParser().parse(request)
|
||||
for classe_data in classes_data:
|
||||
classe_serializer = ClasseSerializer(data=classe_data)
|
||||
|
||||
if classe_serializer.is_valid():
|
||||
classe_serializer.save()
|
||||
else:
|
||||
all_valid = False
|
||||
break
|
||||
|
||||
if all_valid:
|
||||
classesList = bdd.getAllObjects(Classe)
|
||||
classes_serializer = ClasseSerializer(classesList, many=True)
|
||||
|
||||
return JsonResponse(classes_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(classe_serializer.errors, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ClasseView(APIView):
|
||||
def get (self, request, _id):
|
||||
classe = bdd.getObject(_objectName=Classe, _columnName='id', _value=_id)
|
||||
classe_serializer=ClasseSerializer(classe)
|
||||
|
||||
return JsonResponse(classe_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
classe_data=JSONParser().parse(request)
|
||||
classe_serializer = ClasseSerializer(data=classe_data)
|
||||
|
||||
if classe_serializer.is_valid():
|
||||
classe_serializer.save()
|
||||
return JsonResponse(classe_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(classe_serializer.errors, safe=False)
|
||||
|
||||
def put(self, request, _id):
|
||||
classe_data=JSONParser().parse(request)
|
||||
classe = bdd.getObject(_objectName=Classe, _columnName='id', _value=_id)
|
||||
classe_serializer = ClasseSerializer(classe, data=classe_data)
|
||||
if classe_serializer.is_valid():
|
||||
classe_serializer.save()
|
||||
return JsonResponse(classe_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(classe_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, _id):
|
||||
classe = bdd.getObject(_objectName=Classe, _columnName='id', _value=_id)
|
||||
if classe != None:
|
||||
classe.delete()
|
||||
|
||||
return JsonResponse("La suppression de la classe a été effectuée avec succès", safe=False)
|
||||
@ -1 +0,0 @@
|
||||
default_app_config = 'GestionInscriptions.apps.GestionInscriptionsConfig'
|
||||
@ -1,45 +0,0 @@
|
||||
# state_machine.py
|
||||
import json
|
||||
from GestionInscriptions.models import FicheInscription
|
||||
from GestionInscriptions.signals import clear_cache
|
||||
|
||||
state_mapping = {
|
||||
"ABSENT": FicheInscription.EtatDossierInscription.DI_ABSENT,
|
||||
"CREE": FicheInscription.EtatDossierInscription.DI_CREE,
|
||||
"ENVOYE": FicheInscription.EtatDossierInscription.DI_ENVOYE,
|
||||
"EN_VALIDATION": FicheInscription.EtatDossierInscription.DI_EN_VALIDATION,
|
||||
"A_RELANCER": FicheInscription.EtatDossierInscription.DI_A_RELANCER,
|
||||
"VALIDE": FicheInscription.EtatDossierInscription.DI_VALIDE,
|
||||
"ARCHIVE": FicheInscription.EtatDossierInscription.DI_ARCHIVE
|
||||
}
|
||||
|
||||
def load_config(config_file):
|
||||
with open(config_file, 'r') as file:
|
||||
config = json.load(file)
|
||||
return config
|
||||
|
||||
def getStateMachineObject(etat) :
|
||||
return Automate_DI_Inscription(etat)
|
||||
|
||||
def getStateMachineObjectState(etat):
|
||||
return Automate_DI_Inscription(etat).state
|
||||
|
||||
def updateStateMachine(di, transition) :
|
||||
automateModel = load_config('GestionInscriptions/Configuration/automate.json')
|
||||
state_machine = getStateMachineObject(di.etat)
|
||||
print(f'etat DI : {state_machine.state}')
|
||||
if state_machine.trigger(transition, automateModel):
|
||||
di.etat = state_machine.state
|
||||
di.save()
|
||||
clear_cache()
|
||||
|
||||
class Automate_DI_Inscription:
|
||||
def __init__(self, initial_state):
|
||||
self.state = initial_state
|
||||
|
||||
def trigger(self, transition_name, config):
|
||||
for transition in config["transitions"]:
|
||||
if transition["name"] == transition_name and self.state == state_mapping[transition["from"]]:
|
||||
self.state = state_mapping[transition["to"]]
|
||||
return True
|
||||
return False
|
||||
@ -1,74 +0,0 @@
|
||||
from django.core.mail import send_mail
|
||||
import re
|
||||
from N3wtSchool import settings
|
||||
|
||||
def envoieReinitMotDePasse(recipients, code):
|
||||
send_mail(
|
||||
settings.EMAIL_REINIT_SUBJECT,
|
||||
settings.EMAIL_REINIT_CORPUS%(str(code)),
|
||||
settings.EMAIL_HOST_USER,
|
||||
[recipients],
|
||||
fail_silently=False,
|
||||
)
|
||||
|
||||
def envoieDossierInscription(recipients):
|
||||
errorMessage = ''
|
||||
try:
|
||||
print(f'{settings.EMAIL_HOST_USER}')
|
||||
send_mail(
|
||||
settings.EMAIL_INSCRIPTION_SUBJECT,
|
||||
settings.EMAIL_INSCRIPTION_CORPUS%[recipients],
|
||||
settings.EMAIL_HOST_USER,
|
||||
[recipients],
|
||||
fail_silently=False,
|
||||
)
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
|
||||
return errorMessage
|
||||
|
||||
def envoieRelanceDossierInscription(recipients, code):
|
||||
errorMessage = ''
|
||||
try:
|
||||
send_mail(
|
||||
settings.EMAIL_RELANCE_SUBJECT,
|
||||
settings.EMAIL_RELANCE_CORPUS%str(code),
|
||||
settings.EMAIL_HOST_USER,
|
||||
[recipients],
|
||||
fail_silently=False,
|
||||
)
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
|
||||
return errorMessage
|
||||
|
||||
|
||||
def envoieSEPA(recipients, ref):
|
||||
send_mail(
|
||||
settings.EMAIL_SEPA_SUBJECT%str(ref),
|
||||
settings.EMAIL_SEPA_CORPUS,
|
||||
settings.EMAIL_HOST_USER,
|
||||
[recipients],
|
||||
fail_silently=False,
|
||||
)
|
||||
|
||||
def isValid(message, fiche_inscription):
|
||||
# Est-ce que la référence du dossier est VALIDE
|
||||
subject = message.subject
|
||||
print ("++++ " + subject)
|
||||
responsableMail = message.from_header
|
||||
result = re.search('<(.*)>', responsableMail)
|
||||
|
||||
if result:
|
||||
responsableMail = result.group(1)
|
||||
|
||||
result = re.search(r'.*\[Ref(.*)\].*', subject)
|
||||
idMail = -1
|
||||
if result:
|
||||
idMail = result.group(1).strip()
|
||||
|
||||
eleve = fiche_inscription.eleve
|
||||
responsable = eleve.getResponsablePrincipal()
|
||||
mailReponsableAVerifier = responsable.mail
|
||||
|
||||
return responsableMail == mailReponsableAVerifier and str(idMail) == str(fiche_inscription.eleve.id)
|
||||
@ -1,123 +0,0 @@
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from GestionLogin.models import Profil
|
||||
|
||||
class Langue(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
libelle = models.CharField(max_length=200, default="")
|
||||
|
||||
def __str__(self):
|
||||
return "LANGUE"
|
||||
|
||||
class Responsable(models.Model):
|
||||
nom = models.CharField(max_length=200, default="")
|
||||
prenom = models.CharField(max_length=200, default="")
|
||||
dateNaissance = models.CharField(max_length=200, default="", blank=True)
|
||||
adresse = models.CharField(max_length=200, default="", blank=True)
|
||||
mail = models.CharField(max_length=200, default="", blank=True)
|
||||
telephone = models.CharField(max_length=200, default="", blank=True)
|
||||
profession = models.CharField(max_length=200, default="", blank=True)
|
||||
profilAssocie = models.ForeignKey(Profil, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return self.nom + "_" + self.prenom
|
||||
|
||||
class Frere(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
nom = models.CharField(max_length=200, default="")
|
||||
prenom = models.CharField(max_length=200, default="")
|
||||
dateNaissance = models.CharField(max_length=200, default="", blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return "FRERE"
|
||||
|
||||
class Eleve(models.Model):
|
||||
|
||||
class GenreEleve(models.IntegerChoices):
|
||||
NONE = 0, _('Sélection du genre')
|
||||
MALE = 1, _('Garçon')
|
||||
FEMALE = 2, _('Fille')
|
||||
|
||||
class NiveauEleve(models.IntegerChoices):
|
||||
NONE = 0, _('Sélection du niveau')
|
||||
TPS = 1, _('TPS - Très Petite Section')
|
||||
PS = 2, _('PS - Petite Section')
|
||||
MS = 3, _('MS - Moyenne Section')
|
||||
GS = 4, _('GS - Grande Section')
|
||||
|
||||
class ModePaiement(models.IntegerChoices):
|
||||
NONE = 0, _('Sélection du mode de paiement')
|
||||
PRELEVEMENT_SEPA = 1, _('Prélèvement SEPA')
|
||||
CHEQUE = 2, _('Chèque')
|
||||
|
||||
nom = models.CharField(max_length=200, default="")
|
||||
prenom = models.CharField(max_length=200, default="")
|
||||
genre = models.IntegerField(choices=GenreEleve, default=GenreEleve.NONE, blank=True)
|
||||
niveau = models.IntegerField(choices=NiveauEleve, default=NiveauEleve.NONE, blank=True)
|
||||
nationalite = models.CharField(max_length=200, default="", blank=True)
|
||||
adresse = models.CharField(max_length=200, default="", blank=True)
|
||||
dateNaissance = models.CharField(max_length=200, default="", blank=True)
|
||||
lieuNaissance = models.CharField(max_length=200, default="", blank=True)
|
||||
codePostalNaissance = models.IntegerField(default=0, blank=True)
|
||||
medecinTraitant = models.CharField(max_length=200, default="", blank=True)
|
||||
modePaiement = models.IntegerField(choices=ModePaiement, default=ModePaiement.NONE, blank=True)
|
||||
|
||||
# Relation N-N
|
||||
profils = models.ManyToManyField(Profil, blank=True)
|
||||
|
||||
# Relation N-N
|
||||
responsables = models.ManyToManyField(Responsable, blank=True)
|
||||
|
||||
# Relation N-N
|
||||
freres = models.ManyToManyField(Frere, blank=True)
|
||||
|
||||
# Relation N-N
|
||||
languesParlees = models.ManyToManyField(Langue, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.nom + "_" + self.prenom
|
||||
|
||||
def getLanguesParlees(self):
|
||||
return self.languesParlees.all()
|
||||
|
||||
def getResponsablePrincipal(self):
|
||||
return self.responsables.all()[0]
|
||||
|
||||
def getResponsables(self):
|
||||
return self.responsables.all()
|
||||
|
||||
def getProfils(self):
|
||||
return self.profils.all()
|
||||
|
||||
def getFreres(self):
|
||||
return self.freres.all()
|
||||
|
||||
def getNbFreres(self):
|
||||
return self.freres.count()
|
||||
|
||||
class FicheInscription(models.Model):
|
||||
|
||||
class EtatDossierInscription(models.IntegerChoices):
|
||||
DI_ABSENT = 0, _('Pas de dossier d\'inscription')
|
||||
DI_CREE = 1, _('Dossier d\'inscription créé')
|
||||
DI_ENVOYE = 2, _('Dossier d\'inscription envoyé')
|
||||
DI_EN_VALIDATION = 3, _('Dossier d\'inscription en cours de validation')
|
||||
DI_A_RELANCER = 4, _('Dossier d\'inscription à relancer')
|
||||
DI_VALIDE = 5, _('Dossier d\'inscription validé')
|
||||
DI_ARCHIVE = 6, _('Dossier d\'inscription archivé')
|
||||
|
||||
# Relation 1-1
|
||||
eleve = models.OneToOneField(Eleve, on_delete=models.CASCADE, primary_key=True)
|
||||
etat = models.IntegerField(choices=EtatDossierInscription, default=EtatDossierInscription.DI_ABSENT)
|
||||
dateMAJ = models.DateTimeField(auto_now=True)
|
||||
notes = models.CharField(max_length=200, blank=True)
|
||||
codeLienInscription = models.CharField(max_length=200, default="", blank=True)
|
||||
fichierInscription = models.FileField(upload_to=settings.DOCUMENT_DIR, default="", blank=True)
|
||||
di_associe = models.CharField(max_length=200, default="", blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return "FI_" + self.eleve.nom + "_" + self.eleve.prenom
|
||||
|
||||
@ -1,176 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from GestionInscriptions.models import FicheInscription, Eleve, Responsable, Frere, Langue
|
||||
from GestionLogin.models import Profil
|
||||
from GestionLogin.serializers import ProfilSerializer
|
||||
from GestionMessagerie.models import Messagerie
|
||||
from GestionNotification.models import Notification
|
||||
from N3wtSchool import settings
|
||||
from django.utils import timezone
|
||||
import pytz
|
||||
|
||||
class LanguesSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = Langue
|
||||
fields = '__all__'
|
||||
|
||||
class FrereSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = Frere
|
||||
fields = '__all__'
|
||||
|
||||
class ResponsableSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
profil_associe = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = Responsable
|
||||
fields = '__all__'
|
||||
|
||||
def get_profil_associe(self, obj):
|
||||
return obj.profilAssocie.email
|
||||
|
||||
class EleveSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
responsables = ResponsableSerializer(many=True, required=False)
|
||||
freres = FrereSerializer(many=True, required=False)
|
||||
langues = LanguesSerializer(many=True, required=False)
|
||||
class Meta:
|
||||
model = Eleve
|
||||
fields = '__all__'
|
||||
|
||||
def get_or_create_packages(self, responsables_data):
|
||||
responsables_ids = []
|
||||
for responsable_data in responsables_data:
|
||||
responsable_instance, created = Responsable.objects.get_or_create( id=responsable_data.get('id'),
|
||||
defaults=responsable_data)
|
||||
responsables_ids.append(responsable_instance.id)
|
||||
return responsables_ids
|
||||
|
||||
def create(self, validated_data):
|
||||
responsables_data = validated_data.pop('responsables', [])
|
||||
freres_data = validated_data.pop('freres', [])
|
||||
langues_data = validated_data.pop('languesParlees', [])
|
||||
eleve = Eleve.objects.create(**validated_data)
|
||||
eleve.responsables.set(self.get_or_create_packages(responsables_data))
|
||||
eleve.freres.set(self.get_or_create_packages(freres_data))
|
||||
eleve.languesParlees.set(self.get_or_create_packages(langues_data))
|
||||
|
||||
return eleve
|
||||
|
||||
def create_or_update_packages(self, responsables_data):
|
||||
responsables_ids = []
|
||||
|
||||
|
||||
for responsable_data in responsables_data:
|
||||
responsable_instance, created = Responsable.objects.update_or_create( id=responsable_data.get('id'),
|
||||
defaults=responsable_data)
|
||||
|
||||
responsables_ids.append(responsable_instance.id)
|
||||
return responsables_ids
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
responsables_data = validated_data.pop('responsables', [])
|
||||
freres_data = validated_data.pop('freres', [])
|
||||
langues_data = validated_data.pop('languesParlees', [])
|
||||
if responsables_data:
|
||||
instance.responsables.set(self.create_or_update_packages(responsables_data))
|
||||
if freres_data:
|
||||
instance.freres.set(self.create_or_update_packages(freres_data))
|
||||
if langues_data:
|
||||
instance.freres.set(self.create_or_update_packages(langues_data))
|
||||
|
||||
for field in self.fields:
|
||||
try:
|
||||
setattr(instance, field, validated_data[field])
|
||||
except KeyError:
|
||||
pass
|
||||
instance.save()
|
||||
|
||||
return instance
|
||||
|
||||
class FicheInscriptionSerializer(serializers.ModelSerializer):
|
||||
eleve = EleveSerializer(many=False, required=True)
|
||||
fichierInscription = serializers.FileField(required=False)
|
||||
etat_label = serializers.SerializerMethodField()
|
||||
dateMAJ_formattee = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = FicheInscription
|
||||
fields = '__all__'
|
||||
|
||||
def create(self, validated_data):
|
||||
eleve_data = validated_data.pop('eleve')
|
||||
eleve = EleveSerializer.create(EleveSerializer(), eleve_data)
|
||||
ficheEleve = FicheInscription.objects.create(eleve=eleve, **validated_data)
|
||||
return ficheEleve
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
eleve_data = validated_data.pop('eleve')
|
||||
eleve = instance.eleve
|
||||
eleve_serializer = EleveSerializer.update(EleveSerializer(), eleve, eleve_data)
|
||||
|
||||
for field in self.fields:
|
||||
try:
|
||||
setattr(instance, field, validated_data[field])
|
||||
except KeyError:
|
||||
pass
|
||||
instance.save()
|
||||
|
||||
return instance
|
||||
|
||||
def get_etat_label(self, obj):
|
||||
return obj.get_etat_display()
|
||||
|
||||
def get_dateMAJ_formattee(self, obj):
|
||||
utc_time = timezone.localtime(obj.dateMAJ) # Convertir en heure locale
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
local_time = utc_time.astimezone(local_tz)
|
||||
|
||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
class EleveByParentSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = Eleve
|
||||
fields = ['id', 'nom', 'prenom']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EleveByParentSerializer , self).__init__(*args, **kwargs)
|
||||
for field in self.fields:
|
||||
self.fields[field].required = False
|
||||
|
||||
class FicheInscriptionByParentSerializer(serializers.ModelSerializer):
|
||||
eleve = EleveByParentSerializer(many=False, required=True)
|
||||
class Meta:
|
||||
model = FicheInscription
|
||||
fields = ['eleve', 'etat']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FicheInscriptionByParentSerializer, self).__init__(*args, **kwargs)
|
||||
for field in self.fields:
|
||||
self.fields[field].required = False
|
||||
|
||||
class ResponsableByDICreationSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = Responsable
|
||||
fields = ['id', 'nom', 'prenom', 'mail', 'profilAssocie']
|
||||
|
||||
class EleveByDICreationSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
responsables = ResponsableByDICreationSerializer(many=True, required=False)
|
||||
class Meta:
|
||||
model = Eleve
|
||||
fields = ['id', 'nom', 'prenom', 'responsables']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EleveByDICreationSerializer , self).__init__(*args, **kwargs)
|
||||
for field in self.fields:
|
||||
self.fields[field].required = False
|
||||
|
||||
class NotificationSerializer(serializers.ModelSerializer):
|
||||
typeNotification_label = serializers.ReadOnlyField()
|
||||
class Meta:
|
||||
model = Notification
|
||||
fields = '__all__'
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
from django.db.models.signals import post_save, post_delete, m2m_changed
|
||||
from django.dispatch import receiver
|
||||
from django.core.cache import cache
|
||||
from GestionInscriptions.models import FicheInscription, Eleve, Responsable
|
||||
from GestionLogin.models import Profil
|
||||
from N3wtSchool import settings
|
||||
from N3wtSchool.redis_client import redis_client
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def clear_cache():
|
||||
# Préfixes des clés à supprimer
|
||||
prefixes = ['N3WT_']
|
||||
|
||||
for prefix in prefixes:
|
||||
# Utiliser le motif pour obtenir les clés correspondant au préfixe
|
||||
pattern = f'*{prefix}*'
|
||||
logger.debug(f'pattern : {pattern}')
|
||||
for key in redis_client.scan_iter(pattern):
|
||||
redis_client.delete(key)
|
||||
logger.debug(f'deleting : {key}')
|
||||
|
||||
@receiver(post_save, sender=FicheInscription)
|
||||
@receiver(post_delete, sender=FicheInscription)
|
||||
def clear_cache_after_change(sender, instance, **kwargs):
|
||||
clear_cache()
|
||||
|
||||
@receiver(m2m_changed, sender=Eleve.responsables.through)
|
||||
def check_orphan_reponsables(sender, **kwargs):
|
||||
action = kwargs.pop('action', None)
|
||||
instance = kwargs.pop('instance', None)
|
||||
# pre_clear : lors de la suppression d'une FI (on fait un "clear" sur chaque relation)
|
||||
if action in ('post_remove', 'post_clear'):
|
||||
if instance.responsables.all():
|
||||
Responsable.objects.filter(eleve=None).delete()
|
||||
|
||||
@receiver(m2m_changed, sender=Eleve.profils.through)
|
||||
def check_orphan_profils(sender, **kwargs):
|
||||
action = kwargs.pop('action', None)
|
||||
instance = kwargs.pop('instance', None)
|
||||
# pre_clear : lors de la suppression d'une FI (on fait un "clear" sur chaque relation)
|
||||
if action in ('post_remove', 'post_clear'):
|
||||
if instance.profils.all():
|
||||
Profil.objects.filter(eleve=None).delete()
|
||||
@ -1,97 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ pdf_title }}</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-weight: 200;
|
||||
font-size: 14px;
|
||||
}
|
||||
.header {
|
||||
font-size: 20px;
|
||||
font-weight: 100;
|
||||
text-align: center;
|
||||
color: #007cae;
|
||||
}
|
||||
.title {
|
||||
font-size: 22px;
|
||||
font-weight: 100;
|
||||
/* text-align: right;*/
|
||||
padding: 10px 20px 0px 20px;
|
||||
}
|
||||
.title span {
|
||||
color: #007cae;
|
||||
}
|
||||
.details {
|
||||
padding: 10px 20px 0px 20px;
|
||||
text-align: left !important;
|
||||
/*margin-left: 40%;*/
|
||||
}
|
||||
.hrItem {
|
||||
border: none;
|
||||
height: 1px;
|
||||
/* Set the hr color */
|
||||
color: #333; /* old IE */
|
||||
background-color: #fff; /* Modern Browsers */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% load myTemplateTag %}
|
||||
<div class='wrapper'>
|
||||
<div class='header'>
|
||||
<p class='title'>{{ pdf_title }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class='details'>
|
||||
Signé le : <b>{{ dateSignature }}</b> <br/>
|
||||
A : <b>{{ heureSignature }}</b>
|
||||
<hr class='hrItem' />
|
||||
<h1>ELEVE</h1>
|
||||
{% with niveau=eleve|recupereNiveauEleve %}
|
||||
{% with genre=eleve|recupereGenreEleve %}
|
||||
NOM : <b>{{ eleve.nom }}</b> <br/>
|
||||
PRENOM : <b>{{ eleve.prenom }}</b> <br/>
|
||||
ADRESSE : <b>{{ eleve.adresse }}</b> <br/>
|
||||
GENRE : <b>{{ genre }}</b> <br/>
|
||||
NE(E) LE : <b>{{ eleve.dateNaissance }}</b> <br/>
|
||||
A : <b>{{ eleve.lieuNaissance }} ({{ eleve.codePostalNaissance }})</b> <br/>
|
||||
NATIONALITE : <b>{{ eleve.nationalite }}</b> <br/>
|
||||
NIVEAU : <b>{{ niveau }}</b> <br/>
|
||||
MEDECIN TRAITANT : <b>{{ eleve.medecinTraitant }}</b> <br/>
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
<hr class='hrItem' />
|
||||
<h1>RESPONSABLES</h1>
|
||||
{% with responsables_List=eleve.getResponsables %}
|
||||
{% with freres_List=eleve.getFreres %}
|
||||
{% for responsable in responsables_List%}
|
||||
<h2>Responsable {{ forloop.counter }}</h2>
|
||||
NOM : <b>{{ responsable.nom }}</b> <br/>
|
||||
PRENOM : <b>{{ responsable.prenom }}</b> <br/>
|
||||
ADRESSE : <b>{{ responsable.adresse }}</b> <br/>
|
||||
NE(E) LE : <b>{{ responsable.dateNaissance }}</b> <br/>
|
||||
MAIL : <b>{{ responsable.mail }}</b> <br/>
|
||||
TEL : <b>{{ responsable.telephone }}</b> <br/>
|
||||
PROFESSION : <b>{{ responsable.profession }}</b> <br/>
|
||||
{% endfor %}
|
||||
<hr class='hrItem' />
|
||||
<h1>FRATRIE</h1>
|
||||
{% for frere in freres_List%}
|
||||
<h2>Frère - Soeur {{ forloop.counter }}</h2>
|
||||
NOM : <b>{{ frere.nom }}</b> <br/>
|
||||
PRENOM : <b>{{ frere.prenom }}</b> <br/>
|
||||
NE(E) LE : <b>{{ frere.dateNaissance }}</b> <br/>
|
||||
{% endfor %}
|
||||
<hr class='hrItem' />
|
||||
<h1>MODALITES DE PAIEMENT</h1>
|
||||
{% with modePaiement=eleve|recupereModePaiement %}
|
||||
<b>{{ modePaiement }}</b> <br/>
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,23 +0,0 @@
|
||||
from GestionInscriptions.models import FicheInscription, Eleve
|
||||
from django import template
|
||||
register = template.Library()
|
||||
|
||||
# @register.filter
|
||||
# def recupereFichiersDossierInscription(pk):
|
||||
# fichiers_list = FicheInscription.objects.filter(fiche_inscription=pk)
|
||||
# return fichiers_list
|
||||
|
||||
@register.filter
|
||||
def recupereModePaiement(pk):
|
||||
ficheInscription = FicheInscription.objects.get(eleve=pk)
|
||||
return Eleve.ModePaiement(int(ficheInscription.eleve.modePaiement)).label
|
||||
|
||||
@register.filter
|
||||
def recupereNiveauEleve(pk):
|
||||
ficheInscription = FicheInscription.objects.get(eleve=pk)
|
||||
return Eleve.NiveauEleve(int(ficheInscription.eleve.niveau)).label
|
||||
|
||||
@register.filter
|
||||
def recupereGenreEleve(pk):
|
||||
ficheInscription = FicheInscription.objects.get(eleve=pk)
|
||||
return Eleve.GenreEleve(int(ficheInscription.eleve.genre)).label
|
||||
@ -1,31 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import views
|
||||
from GestionInscriptions.views import ListFichesInscriptionView, FicheInscriptionView, EleveView, ResponsableView, ListeEnfantsView, ListeElevesView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^fichesInscription/([a-zA-z]+)$', ListFichesInscriptionView.as_view(), name="listefichesInscriptions"),
|
||||
re_path(r'^ficheInscription$', FicheInscriptionView.as_view(), name="fichesInscriptions"),
|
||||
re_path(r'^ficheInscription/([0-9]+)$', FicheInscriptionView.as_view(), name="fichesInscriptions"),
|
||||
|
||||
# Page de formulaire d'inscription - ELEVE
|
||||
re_path(r'^eleve/([0-9]+)$', EleveView.as_view(), name="eleves"),
|
||||
|
||||
# Page de formulaire d'inscription - RESPONSABLE
|
||||
re_path(r'^recupereDernierResponsable$', ResponsableView.as_view(), name="recupereDernierResponsable"),
|
||||
|
||||
# Envoi d'un dossier d'inscription
|
||||
re_path(r'^send/([0-9]+)$', views.send, name="send"),
|
||||
|
||||
# Archivage d'un dossier d'inscription
|
||||
re_path(r'^archive/([0-9]+)$', views.archive, name="archive"),
|
||||
|
||||
# Envoi d'une relance de dossier d'inscription
|
||||
re_path(r'^sendRelance/([0-9]+)$', views.relance, name="relance"),
|
||||
|
||||
# Page PARENT - Liste des enfants
|
||||
re_path(r'^enfants/([0-9]+)$', ListeEnfantsView.as_view(), name="enfants"),
|
||||
|
||||
# Page INSCRIPTION - Liste des élèves
|
||||
re_path(r'^eleves$', ListeElevesView.as_view(), name="enfants"),
|
||||
]
|
||||
@ -1,181 +0,0 @@
|
||||
from django.shortcuts import render,get_object_or_404,get_list_or_404
|
||||
from .models import FicheInscription, Eleve, Responsable, Frere
|
||||
import time
|
||||
from datetime import date, datetime, timedelta
|
||||
from zoneinfo import ZoneInfo
|
||||
from django.conf import settings
|
||||
from N3wtSchool import renderers
|
||||
from N3wtSchool import bdd
|
||||
|
||||
from io import BytesIO
|
||||
from django.core.files import File
|
||||
from pathlib import Path
|
||||
import os
|
||||
from enum import Enum
|
||||
|
||||
import random
|
||||
import string
|
||||
from rest_framework.parsers import JSONParser
|
||||
|
||||
def recupereListeFichesInscription():
|
||||
context = {
|
||||
"ficheInscriptions_list": bdd.getAllObjects(FicheInscription),
|
||||
}
|
||||
return context
|
||||
|
||||
def recupereListeFichesInscriptionEnAttenteSEPA():
|
||||
|
||||
ficheInscriptionsSEPA_list = FicheInscription.objects.filter(modePaiement="Prélèvement SEPA").filter(etat=FicheInscription.EtatDossierInscription['SEPA_ENVOYE'])
|
||||
return ficheInscriptionsSEPA_list
|
||||
|
||||
def updateEleve(eleve, inputs, erase=False):
|
||||
eleve.nom = inputs["nomEleve"]
|
||||
eleve.prenom = inputs["prenomEleve"]
|
||||
eleve.ambiance = inputs["ambiance"]
|
||||
eleve.genre = inputs["genre"]
|
||||
eleve.adresse = inputs["adresseEleve"]
|
||||
eleve.dateNaissance = inputs["dateNaissanceEleve"]
|
||||
eleve.lieuNaissance = inputs["lieuNaissanceEleve"]
|
||||
eleve.codePostalNaissance = inputs["codePostalNaissanceEleve"]
|
||||
eleve.nationalite = inputs["nationaliteEleve"]
|
||||
eleve.medecinTraitant = inputs["medecinTraitantEleve"]
|
||||
|
||||
|
||||
responsable=eleve.getResponsablePrincipal()
|
||||
responsable.adresse = inputs["adresseResponsable1"]
|
||||
responsable.dateNaissance = inputs["dateNaissanceResponsable1"]
|
||||
responsable.profession = inputs["professionResponsable1"]
|
||||
responsable.save()
|
||||
|
||||
# Création du 2ème responsable
|
||||
if inputs["nomResponsable2"] != "" and inputs["prenomResponsable2"] != "":
|
||||
responsable2 = Responsable.objects.create(nom=inputs["nomResponsable2"],
|
||||
prenom=inputs["prenomResponsable2"],
|
||||
dateNaissance=inputs["dateNaissanceResponsable2"],
|
||||
adresse=inputs["adresseResponsable2"],
|
||||
mail=inputs["mailResponsable2"],
|
||||
telephone=inputs["telephoneResponsable2"],
|
||||
profession=inputs["professionResponsable2"])
|
||||
responsable2.save()
|
||||
eleve.responsables.add(responsable2)
|
||||
|
||||
# Création du 1er frère
|
||||
if inputs["nomFrere1"] != "" and inputs["prenomFrere1"] != "":
|
||||
frere1 = Frere.objects.create(nom=inputs["nomFrere1"],
|
||||
prenom=inputs["prenomFrere1"],
|
||||
dateNaissance=inputs["dateNaissanceFrere1"])
|
||||
frere1.save()
|
||||
eleve.freres.add(frere1)
|
||||
|
||||
# Création du 2ème frère
|
||||
if inputs["nomFrere2"] != "" and inputs["prenomFrere2"] != "":
|
||||
frere2 = Frere.objects.create(nom=inputs["nomFrere2"],
|
||||
prenom=inputs["prenomFrere2"],
|
||||
dateNaissance=inputs["dateNaissanceFrere2"])
|
||||
frere2.save()
|
||||
eleve.freres.add(frere2)
|
||||
|
||||
eleve.save()
|
||||
|
||||
def _now():
|
||||
return datetime.now(ZoneInfo(settings.TZ_APPLI))
|
||||
|
||||
def convertToStr(dateValue, dateFormat):
|
||||
return dateValue.strftime(dateFormat)
|
||||
|
||||
def convertToDate(date_time):
|
||||
format = '%d-%m-%Y %H:%M'
|
||||
datetime_str = datetime.strptime(date_time, format)
|
||||
|
||||
return datetime_str
|
||||
|
||||
def convertTelephone(telephoneValue, separator='-'):
|
||||
return f"{telephoneValue[:2]}{separator}{telephoneValue[2:4]}{separator}{telephoneValue[4:6]}{separator}{telephoneValue[6:8]}{separator}{telephoneValue[8:10]}"
|
||||
|
||||
def generePDF(ficheEleve):
|
||||
data = {
|
||||
'pdf_title': "Dossier d'inscription de %s"%ficheEleve.eleve.prenom,
|
||||
'dateSignature': convertToStr(_now(), '%d-%m-%Y'),
|
||||
'heureSignature': convertToStr(_now(), '%H:%M'),
|
||||
'eleve':ficheEleve.eleve,
|
||||
}
|
||||
|
||||
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
||||
|
||||
nomFichierPDF = "Dossier_Inscription_%s_%s.pdf"%(ficheEleve.eleve.nom, ficheEleve.eleve.prenom)
|
||||
pathFichier = Path(settings.DOCUMENT_DIR + "/" + nomFichierPDF)
|
||||
if os.path.exists(str(pathFichier)):
|
||||
os.remove(str(pathFichier))
|
||||
|
||||
receipt_file = BytesIO(pdf.content)
|
||||
# fichier = Fichier.objects.create(fiche_inscription=ficheEleve)
|
||||
# fichier.document = File(receipt_file, nomFichierPDF)
|
||||
# fichier.save()
|
||||
|
||||
def genereRandomCode(length):
|
||||
return ''.join(random.choice(string.ascii_letters) for i in range(length))
|
||||
|
||||
def calculeDatePeremption(_start, nbDays):
|
||||
return convertToStr(_start + timedelta(days=nbDays), settings.DATE_FORMAT)
|
||||
|
||||
# Fonction permettant de retourner la valeur du QueryDict
|
||||
# QueryDict [ index ] -> Dernière valeur d'une liste
|
||||
# dict (QueryDict [ index ]) -> Toutes les valeurs de la liste
|
||||
def _(liste):
|
||||
return liste[0]
|
||||
|
||||
def toNewEleveJSONRequest(jsonOrigin):
|
||||
etat=FicheInscription.EtatDossierInscription.DI_CREE
|
||||
telephone = convertTelephone(_(jsonOrigin['telephoneResponsable']))
|
||||
finalJSON = {
|
||||
"eleve":
|
||||
{
|
||||
"nom" : _(jsonOrigin['nomEleve']),
|
||||
"prenom" : _(jsonOrigin['prenomEleve']),
|
||||
"responsables" : [
|
||||
{
|
||||
"nom" : _(jsonOrigin['nomResponsable']),
|
||||
"prenom" : _(jsonOrigin['prenomResponsable']),
|
||||
"mail" : _(jsonOrigin['mailResponsable']),
|
||||
"telephone" : telephone
|
||||
}
|
||||
],
|
||||
"profils" : [
|
||||
],
|
||||
},
|
||||
"etat": str(etat),
|
||||
"dateMAJ": str(convertToStr(_now(), '%d-%m-%Y %H:%M')),
|
||||
}
|
||||
print(finalJSON)
|
||||
return finalJSON
|
||||
|
||||
def toEditEleveJSONRequest(jsonOrigin):
|
||||
telephone = convertTelephone(_(jsonOrigin['telephoneResponsable']), '.')
|
||||
finalJSON = {
|
||||
"eleve":
|
||||
{
|
||||
"id" : _(jsonOrigin['fiche_id']),
|
||||
"nom" : _(jsonOrigin['nomEleve']),
|
||||
"prenom" : _(jsonOrigin['prenomEleve']),
|
||||
"responsables" : [
|
||||
{
|
||||
"id" : _(jsonOrigin['responsable_id']),
|
||||
"nom" : _(jsonOrigin['nomResponsable']),
|
||||
"prenom" : _(jsonOrigin['prenomResponsable']),
|
||||
"mail" : _(jsonOrigin['mailResponsable']),
|
||||
"telephone" : telephone
|
||||
}
|
||||
],
|
||||
"profils" : [
|
||||
],
|
||||
},
|
||||
"dateMAJ": str(convertToStr(_now(), '%d-%m-%Y %H:%M')),
|
||||
}
|
||||
print(finalJSON)
|
||||
return finalJSON
|
||||
|
||||
def getArgFromRequest(_argument, _request):
|
||||
resultat = None
|
||||
data=JSONParser().parse(_request)
|
||||
resultat = data[_argument]
|
||||
return resultat
|
||||
@ -1,289 +0,0 @@
|
||||
from django.http.response import JsonResponse
|
||||
from django.contrib.auth import login, authenticate, get_user_model
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.core.cache import cache
|
||||
from django.core.paginator import Paginator
|
||||
from django.core.files import File
|
||||
from django.db.models import Q # Ajout de cet import
|
||||
from rest_framework.parsers import JSONParser
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import status
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import os
|
||||
from io import BytesIO
|
||||
|
||||
import GestionInscriptions.mailManager as mailer
|
||||
import GestionInscriptions.util as util
|
||||
from GestionInscriptions.serializers import FicheInscriptionSerializer, EleveSerializer, FicheInscriptionByParentSerializer, EleveByDICreationSerializer
|
||||
from GestionInscriptions.pagination import CustomPagination
|
||||
from GestionInscriptions.signals import clear_cache
|
||||
from .models import Eleve, Responsable, FicheInscription
|
||||
from GestionInscriptions.automate import Automate_DI_Inscription, load_config, getStateMachineObjectState, updateStateMachine
|
||||
|
||||
from GestionLogin.models import Profil
|
||||
|
||||
from N3wtSchool import settings, renderers, bdd
|
||||
|
||||
class ListFichesInscriptionView(APIView):
|
||||
pagination_class = CustomPagination
|
||||
|
||||
def get(self, request, _filter):
|
||||
if _filter == 'all':
|
||||
# Récupération des paramètres
|
||||
search = request.GET.get('search', '').strip()
|
||||
page_size = request.GET.get('page_size', None)
|
||||
|
||||
# Gestion du page_size
|
||||
if page_size is not None:
|
||||
try:
|
||||
page_size = int(page_size)
|
||||
except ValueError:
|
||||
page_size = settings.NB_RESULT_PER_PAGE
|
||||
|
||||
cached_page_size = cache.get('N3WT_page_size')
|
||||
if cached_page_size != page_size:
|
||||
clear_cache()
|
||||
cache.set('N3WT_page_size', page_size)
|
||||
|
||||
# Gestion du cache
|
||||
page_number = request.GET.get('page', 1)
|
||||
cache_key = f'N3WT_ficheInscriptions_page_{page_number}_search_{search}'
|
||||
cached_page = cache.get(cache_key)
|
||||
if cached_page:
|
||||
return JsonResponse(cached_page, safe=False)
|
||||
|
||||
# Filtrage des résultats
|
||||
if search:
|
||||
# Utiliser la nouvelle fonction de recherche
|
||||
ficheInscriptions_List = bdd.searchObjects(
|
||||
FicheInscription,
|
||||
search,
|
||||
_excludeState=6 # Exclure les fiches archivées
|
||||
)
|
||||
else:
|
||||
# Récupère toutes les fiches non archivées
|
||||
ficheInscriptions_List = bdd.getObjects(FicheInscription, 'etat', 6, _reverseCondition=True)
|
||||
|
||||
# Pagination
|
||||
paginator = self.pagination_class()
|
||||
page = paginator.paginate_queryset(ficheInscriptions_List, request)
|
||||
if page is not None:
|
||||
ficheInscriptions_serializer = FicheInscriptionSerializer(page, many=True)
|
||||
response_data = paginator.get_paginated_response(ficheInscriptions_serializer.data)
|
||||
cache.set(cache_key, response_data, timeout=60*15)
|
||||
return JsonResponse(response_data, safe=False)
|
||||
|
||||
elif _filter == 'archived' :
|
||||
page_size = request.GET.get('page_size', None)
|
||||
if page_size is not None:
|
||||
try:
|
||||
page_size = int(page_size)
|
||||
except ValueError:
|
||||
page_size = settings.NB_RESULT_PER_PAGE
|
||||
|
||||
cached_page_size = cache.get('N3WT_archived_page_size')
|
||||
|
||||
# Comparer avec le nouveau page_size
|
||||
if cached_page_size != page_size:
|
||||
# Appeler cached_page() et mettre à jour le cache
|
||||
clear_cache()
|
||||
cache.set('N3WT_archived_page_size',page_size)
|
||||
|
||||
page_number = request.GET.get('page', 1)
|
||||
cache_key_page = f'N3WT_ficheInscriptions_archives_page_{page_number}'
|
||||
cached_page = cache.get(cache_key_page)
|
||||
if cached_page:
|
||||
return JsonResponse(cached_page, safe=False)
|
||||
|
||||
ficheInscriptions_List=bdd.getObjects(FicheInscription, 'etat', 6)
|
||||
paginator = self.pagination_class()
|
||||
page = paginator.paginate_queryset(ficheInscriptions_List, request)
|
||||
if page is not None:
|
||||
ficheInscriptions_serializer = FicheInscriptionSerializer(page, many=True)
|
||||
response_data = paginator.get_paginated_response(ficheInscriptions_serializer.data)
|
||||
cache.set(cache_key_page, response_data, timeout=60*15)
|
||||
|
||||
return JsonResponse(response_data, safe=False)
|
||||
|
||||
return JsonResponse(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def post(self, request):
|
||||
fichesEleve_data=JSONParser().parse(request)
|
||||
for ficheEleve_data in fichesEleve_data:
|
||||
# Ajout de la date de mise à jour
|
||||
ficheEleve_data["dateMAJ"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
json.dumps(ficheEleve_data)
|
||||
# Ajout du code d'inscription
|
||||
code = util.genereRandomCode(12)
|
||||
ficheEleve_data["codeLienInscription"] = code
|
||||
ficheEleve_serializer = FicheInscriptionSerializer(data=ficheEleve_data)
|
||||
|
||||
if ficheEleve_serializer.is_valid():
|
||||
ficheEleve_serializer.save()
|
||||
|
||||
return JsonResponse(ficheEleve_serializer.errors, safe=False)
|
||||
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class FicheInscriptionView(APIView):
|
||||
pagination_class = CustomPagination
|
||||
|
||||
def get(self, request, _id):
|
||||
ficheInscription=bdd.getObject(FicheInscription, "eleve__id", _id)
|
||||
fiche_serializer=FicheInscriptionSerializer(ficheInscription)
|
||||
return JsonResponse(fiche_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
ficheEleve_data=JSONParser().parse(request)
|
||||
# Ajout de la date de mise à jour
|
||||
ficheEleve_data["dateMAJ"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
json.dumps(ficheEleve_data)
|
||||
# Ajout du code d'inscription
|
||||
code = util.genereRandomCode(12)
|
||||
ficheEleve_data["codeLienInscription"] = code
|
||||
|
||||
responsablesId = ficheEleve_data.pop('idResponsables', [])
|
||||
ficheEleve_serializer = FicheInscriptionSerializer(data=ficheEleve_data)
|
||||
|
||||
if ficheEleve_serializer.is_valid():
|
||||
di = ficheEleve_serializer.save()
|
||||
|
||||
# Mise à jour de l'automate
|
||||
updateStateMachine(di, 'creationDI')
|
||||
|
||||
# Récupération du reponsable associé
|
||||
for responsableId in responsablesId:
|
||||
responsable = Responsable.objects.get(id=responsableId)
|
||||
di.eleve.responsables.add(responsable)
|
||||
di.save()
|
||||
|
||||
ficheInscriptions_List=bdd.getAllObjects(FicheInscription)
|
||||
return JsonResponse({'totalInscrits':len(ficheInscriptions_List)}, safe=False)
|
||||
|
||||
return JsonResponse(ficheEleve_serializer.errors, safe=False)
|
||||
|
||||
def put(self, request, id):
|
||||
ficheEleve_data=JSONParser().parse(request)
|
||||
admin = ficheEleve_data.pop('admin', 1)
|
||||
ficheEleve_data["dateMAJ"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'))
|
||||
ficheEleve = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
|
||||
currentState = getStateMachineObjectState(ficheEleve.etat)
|
||||
if admin == 0 and currentState == FicheInscription.EtatDossierInscription.DI_ENVOYE:
|
||||
json.dumps(ficheEleve_data)
|
||||
|
||||
# Ajout du fichier d'inscriptions
|
||||
data = {
|
||||
'pdf_title': "Dossier d'inscription de %s"%ficheEleve.eleve.prenom,
|
||||
'dateSignature': util.convertToStr(util._now(), '%d-%m-%Y'),
|
||||
'heureSignature': util.convertToStr(util._now(), '%H:%M'),
|
||||
'eleve':ficheEleve.eleve,
|
||||
}
|
||||
|
||||
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
||||
|
||||
nomFichierPDF = "Dossier_Inscription_%s_%s.pdf"%(ficheEleve.eleve.nom, ficheEleve.eleve.prenom)
|
||||
pathFichier = Path(settings.DOCUMENT_DIR + "/" + nomFichierPDF)
|
||||
if os.path.exists(str(pathFichier)):
|
||||
print(f'File exists : {str(pathFichier)}')
|
||||
os.remove(str(pathFichier))
|
||||
|
||||
receipt_file = BytesIO(pdf.content)
|
||||
ficheEleve.fichierInscription = File(receipt_file, nomFichierPDF)
|
||||
|
||||
# Mise à jour de l'automate
|
||||
updateStateMachine(di, 'saisiDI')
|
||||
|
||||
ficheEleve_serializer = FicheInscriptionSerializer(ficheEleve, data=ficheEleve_data)
|
||||
if ficheEleve_serializer.is_valid():
|
||||
di = ficheEleve_serializer.save()
|
||||
return JsonResponse("Updated Successfully", safe=False)
|
||||
|
||||
return JsonResponse(ficheEleve_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, id):
|
||||
fiche_inscription = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
|
||||
if fiche_inscription != None:
|
||||
eleve = fiche_inscription.eleve
|
||||
eleve.responsables.clear()
|
||||
eleve.profils.clear()
|
||||
eleve.delete()
|
||||
clear_cache()
|
||||
|
||||
return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False)
|
||||
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
|
||||
|
||||
class EleveView(APIView):
|
||||
def get(self, request, _id):
|
||||
eleve = bdd.getObject(_objectName=Eleve, _columnName='id', _value=_id)
|
||||
eleve_serializer = EleveSerializer(eleve)
|
||||
return JsonResponse(eleve_serializer.data, safe=False)
|
||||
|
||||
class ResponsableView(APIView):
|
||||
def get(self, request):
|
||||
lastResponsable = bdd.getLastId(Responsable)
|
||||
return JsonResponse({"lastid":lastResponsable}, safe=False)
|
||||
|
||||
def send(request, id):
|
||||
fiche_inscription = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
|
||||
if fiche_inscription != None:
|
||||
eleve = fiche_inscription.eleve
|
||||
responsable = eleve.getResponsablePrincipal()
|
||||
mail = responsable.mail
|
||||
errorMessage = mailer.envoieDossierInscription(mail)
|
||||
if errorMessage == '':
|
||||
fiche_inscription.dateMAJ=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
# Mise à jour de l'automate
|
||||
updateStateMachine(fiche_inscription, 'envoiDI')
|
||||
|
||||
return JsonResponse({"errorMessage":errorMessage}, safe=False)
|
||||
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
|
||||
|
||||
def archive(request, id):
|
||||
fiche_inscription = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
|
||||
if fiche_inscription != None:
|
||||
fiche_inscription.dateMAJ=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
# Mise à jour de l'automate
|
||||
updateStateMachine(fiche_inscription, 'archiveDI')
|
||||
|
||||
return JsonResponse({"errorMessage":''}, safe=False)
|
||||
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
|
||||
|
||||
def relance(request, id):
|
||||
fiche_inscription = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
|
||||
if fiche_inscription != None:
|
||||
eleve = fiche_inscription.eleve
|
||||
responsable = eleve.getResponsablePrincipal()
|
||||
mail = responsable.mail
|
||||
errorMessage = mailer.envoieRelanceDossierInscription(mail, fiche_inscription.codeLienInscription)
|
||||
if errorMessage == '':
|
||||
fiche_inscription.etat=FicheInscription.EtatDossierInscription.DI_ENVOYE
|
||||
fiche_inscription.dateMAJ=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
fiche_inscription.save()
|
||||
|
||||
return JsonResponse({"errorMessage":errorMessage}, safe=False)
|
||||
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
|
||||
|
||||
# API utilisée pour la vue parent
|
||||
class ListeEnfantsView(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, _idProfile):
|
||||
students = bdd.getObjects(_objectName=FicheInscription, _columnName='eleve__responsables__profilAssocie__id', _value=_idProfile)
|
||||
students_serializer = FicheInscriptionByParentSerializer(students, many=True)
|
||||
return JsonResponse(students_serializer.data, safe=False)
|
||||
|
||||
# API utilisée pour la vue de création d'un DI
|
||||
class ListeElevesView(APIView):
|
||||
# Récupération de la liste des élèves inscrits ou en cours d'inscriptions
|
||||
def get(self, request):
|
||||
students = bdd.getAllObjects(_objectName=Eleve)
|
||||
students_serializer = EleveByDICreationSerializer(students, many=True)
|
||||
return JsonResponse(students_serializer.data, safe=False)
|
||||
@ -1,25 +0,0 @@
|
||||
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
|
||||
|
||||
class Profil(AbstractUser):
|
||||
class Droits(models.IntegerChoices):
|
||||
PROFIL_UNDEFINED = -1, _('Profil non défini')
|
||||
PROFIL_ECOLE = 0, _('Profil école')
|
||||
PROFIL_PARENT = 1, _('Profil parent')
|
||||
PROFIL_ADMIN = 2, _('Profil administrateur')
|
||||
|
||||
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)
|
||||
|
||||
def __str__(self):
|
||||
return self.email + " - " + str(self.droit)
|
||||
@ -1,28 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from GestionLogin.models import Profil
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
class ProfilSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
password = serializers.CharField(write_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Profil
|
||||
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active']
|
||||
extra_kwargs = {'password': {'write_only': True}}
|
||||
|
||||
def create(self, validated_data):
|
||||
user = Profil(
|
||||
username=validated_data['username'],
|
||||
email=validated_data['email'],
|
||||
is_active=validated_data['is_active'],
|
||||
droit=validated_data['droit']
|
||||
)
|
||||
user.set_password(validated_data['password'])
|
||||
user.save()
|
||||
return user
|
||||
|
||||
def to_representation(self, instance):
|
||||
ret = super().to_representation(instance)
|
||||
ret['password'] = '********'
|
||||
return ret
|
||||
@ -1,22 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import views
|
||||
import GestionLogin.views
|
||||
from GestionLogin.views import ProfilView, ListProfilView, SessionView, LoginView, SubscribeView, NewPasswordView, ResetPasswordView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^csrf$', GestionLogin.views.csrf, name='csrf'),
|
||||
|
||||
re_path(r'^login$', LoginView.as_view(), name="login"),
|
||||
re_path(r'^subscribe$', SubscribeView.as_view(), name='subscribe'),
|
||||
re_path(r'^newPassword$', NewPasswordView.as_view(), name='newPassword'),
|
||||
re_path(r'^resetPassword/([a-zA-Z]+)$', ResetPasswordView.as_view(), name='resetPassword'),
|
||||
re_path(r'^infoSession$', GestionLogin.views.infoSession, name='infoSession'),
|
||||
|
||||
re_path(r'^profils$', ListProfilView.as_view(), name="profil"),
|
||||
re_path(r'^profil$', ProfilView.as_view(), name="profil"),
|
||||
re_path(r'^profil/([0-9]+)$', ProfilView.as_view(), name="profil"),
|
||||
|
||||
# Test SESSION VIEW
|
||||
re_path(r'^session$', SessionView.as_view(), name="session"),
|
||||
]
|
||||
@ -1,264 +0,0 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import login, authenticate, get_user_model
|
||||
from django.http.response import JsonResponse
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt, csrf_protect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.cache import cache
|
||||
from django.middleware.csrf import get_token
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.parsers import JSONParser
|
||||
from rest_framework import status
|
||||
|
||||
from datetime import datetime
|
||||
import jwt
|
||||
import json
|
||||
|
||||
from . import validator
|
||||
from .models import Profil
|
||||
|
||||
from GestionInscriptions.models import FicheInscription
|
||||
from GestionInscriptions.serializers import ProfilSerializer
|
||||
from GestionInscriptions.signals import clear_cache
|
||||
import GestionInscriptions.mailManager as mailer
|
||||
import GestionInscriptions.util as util
|
||||
|
||||
from N3wtSchool import bdd, error
|
||||
|
||||
def csrf(request):
|
||||
token = get_token(request)
|
||||
return JsonResponse({'csrfToken': token})
|
||||
|
||||
class SessionView(APIView):
|
||||
|
||||
def post(self, request):
|
||||
token = request.META.get('HTTP_AUTHORIZATION', '').split('Bearer ')[-1]
|
||||
|
||||
try:
|
||||
decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
|
||||
print(f'decode : {decoded_token}')
|
||||
user_id = decoded_token.get('id')
|
||||
user = Profil.objects.get(id=user_id)
|
||||
|
||||
response_data = {
|
||||
'user': {
|
||||
'id': user.id,
|
||||
'email': user.email,
|
||||
'role': user.droit, # Assure-toi que le champ 'droit' existe et contient le rôle
|
||||
}
|
||||
}
|
||||
return JsonResponse(response_data, status=status.HTTP_200_OK)
|
||||
except jwt.ExpiredSignatureError:
|
||||
return JsonResponse({"error": "Token has expired"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
except jwt.InvalidTokenError:
|
||||
return JsonResponse({"error": "Invalid token"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
class ListProfilView(APIView):
|
||||
def get(self, request):
|
||||
profilsList = bdd.getAllObjects(_objectName=Profil)
|
||||
profils_serializer = ProfilSerializer(profilsList, many=True)
|
||||
return JsonResponse(profils_serializer.data, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ProfilView(APIView):
|
||||
def get(self, request, _id):
|
||||
profil=bdd.getObject(Profil, "id", _id)
|
||||
profil_serializer=ProfilSerializer(profil)
|
||||
return JsonResponse(profil_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
profil_data=JSONParser().parse(request)
|
||||
print(f'{profil_data}')
|
||||
profil_serializer = ProfilSerializer(data=profil_data)
|
||||
|
||||
if profil_serializer.is_valid():
|
||||
profil_serializer.save()
|
||||
|
||||
return JsonResponse(profil_serializer.data, safe=False)
|
||||
|
||||
|
||||
return JsonResponse(profil_serializer.errors, safe=False)
|
||||
|
||||
def put(self, request, _id):
|
||||
data=JSONParser().parse(request)
|
||||
profil = Profil.objects.get(id=_id)
|
||||
profil_serializer = ProfilSerializer(profil, data=data)
|
||||
if profil_serializer.is_valid():
|
||||
profil_serializer.save()
|
||||
return JsonResponse("Updated Successfully", safe=False)
|
||||
|
||||
return JsonResponse(profil_serializer.errors, safe=False)
|
||||
|
||||
def infoSession(request):
|
||||
profilCache = cache.get('session_cache')
|
||||
if profilCache:
|
||||
return JsonResponse({"cacheSession":True,"typeProfil":profilCache.droit, "username":profilCache.email}, safe=False)
|
||||
else:
|
||||
return JsonResponse({"cacheSession":False,"typeProfil":Profil.Droits.PROFIL_UNDEFINED, "username":""}, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class LoginView(APIView):
|
||||
|
||||
def get(self, request):
|
||||
return JsonResponse({
|
||||
'errorFields':'',
|
||||
'errorMessage':'',
|
||||
'profil':0,
|
||||
}, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
data=JSONParser().parse(request)
|
||||
validatorAuthentication = validator.ValidatorAuthentication(data=data)
|
||||
retour = error.returnMessage[error.WRONG_ID]
|
||||
validationOk, errorFields = validatorAuthentication.validate()
|
||||
user = None
|
||||
if validationOk:
|
||||
user = authenticate(
|
||||
email=data.get('email'),
|
||||
password=data.get('password'),
|
||||
)
|
||||
if user is not None:
|
||||
if user.is_active:
|
||||
login(request, user)
|
||||
user.estConnecte = True
|
||||
user.save()
|
||||
clear_cache()
|
||||
retour = ''
|
||||
else:
|
||||
retour = error.returnMessage[error.PROFIL_INACTIVE]
|
||||
|
||||
# Génération du token JWT
|
||||
# jwt_token = jwt.encode({
|
||||
# 'id': user.id,
|
||||
# 'email': user.email,
|
||||
# 'role': "admin"
|
||||
# }, settings.SECRET_KEY, algorithm='HS256')
|
||||
else:
|
||||
retour = error.returnMessage[error.WRONG_ID]
|
||||
|
||||
|
||||
return JsonResponse({
|
||||
'errorFields':errorFields,
|
||||
'errorMessage':retour,
|
||||
'profil':user.id if user else -1,
|
||||
#'jwtToken':jwt_token if profil != -1 else ''
|
||||
}, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class SubscribeView(APIView):
|
||||
|
||||
def get(self, request):
|
||||
return JsonResponse({
|
||||
'message':'',
|
||||
'errorFields':'',
|
||||
'errorMessage':''
|
||||
}, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
retourErreur = error.returnMessage[error.BAD_URL]
|
||||
retour = ''
|
||||
newProfilConnection=JSONParser().parse(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(Profil.objects.all(), newProfilConnection.get('email'))
|
||||
if profil == 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)
|
||||
else:
|
||||
try:
|
||||
profil.set_password(newProfilConnection.get('password1'))
|
||||
profil.is_active = True
|
||||
profil.full_clean()
|
||||
profil.save()
|
||||
clear_cache()
|
||||
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
|
||||
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, "id":-1}, safe=False)
|
||||
|
||||
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class NewPasswordView(APIView):
|
||||
def get(self, request):
|
||||
return JsonResponse({
|
||||
'message':'',
|
||||
'errorFields':'',
|
||||
'errorMessage':''
|
||||
}, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
retourErreur = error.returnMessage[error.BAD_URL]
|
||||
retour = ''
|
||||
newProfilConnection=JSONParser().parse(request)
|
||||
|
||||
validatorNewPassword = validator.ValidatorNewPassword(data=newProfilConnection)
|
||||
validationOk, errorFields = validatorNewPassword.validate()
|
||||
if validationOk:
|
||||
|
||||
profil = bdd.getProfile(Profil.objects.all(), newProfilConnection.get('email'))
|
||||
if profil == 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)
|
||||
|
||||
return JsonResponse({'message':retour, 'errorMessage':retourErreur, "errorFields":errorFields}, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ResetPasswordView(APIView):
|
||||
def get(self, request, _uuid):
|
||||
return JsonResponse({
|
||||
'message':'',
|
||||
'errorFields':'',
|
||||
'errorMessage':''
|
||||
}, safe=False)
|
||||
|
||||
def post(self, request, _uuid):
|
||||
retourErreur = error.returnMessage[error.BAD_URL]
|
||||
retour = ''
|
||||
newProfilConnection=JSONParser().parse(request)
|
||||
|
||||
validatorResetPassword = validator.ValidatorResetPassword(data=newProfilConnection)
|
||||
validationOk, errorFields = validatorResetPassword.validate()
|
||||
|
||||
profil = bdd.getObject(Profil, "code", _uuid)
|
||||
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)
|
||||
elif validationOk:
|
||||
retour = error.returnMessage[error.PASSWORD_CHANGED]
|
||||
|
||||
profil.set_password(newProfilConnection.get('password1'))
|
||||
profil.code = ''
|
||||
profil.datePeremption = ''
|
||||
profil.save()
|
||||
clear_cache()
|
||||
retourErreur=''
|
||||
|
||||
return JsonResponse({'message':retour, "errorMessage":retourErreur, "errorFields":errorFields}, safe=False)
|
||||
@ -2,13 +2,13 @@ from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.conf import settings
|
||||
from GestionLogin.models import Profil
|
||||
from Auth.models import Profile
|
||||
|
||||
class Messagerie(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
objet = models.CharField(max_length=200, default="", blank=True)
|
||||
emetteur = models.ForeignKey(Profil, on_delete=models.PROTECT, related_name='messages_envoyes')
|
||||
destinataire = models.ForeignKey(Profil, on_delete=models.PROTECT, related_name='messages_recus')
|
||||
emetteur = models.ForeignKey(Profile, on_delete=models.PROTECT, related_name='messages_envoyes')
|
||||
destinataire = models.ForeignKey(Profile, on_delete=models.PROTECT, related_name='messages_recus')
|
||||
corpus = models.CharField(max_length=200, default="", blank=True)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from rest_framework import serializers
|
||||
from GestionLogin.models import Profil
|
||||
from Auth.models import Profile
|
||||
from GestionMessagerie.models import Messagerie
|
||||
|
||||
class MessageSerializer(serializers.ModelSerializer):
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from GestionMessagerie.views import MessagerieView, MessageView
|
||||
from GestionMessagerie.views import MessagerieView, MessageView, MessageSimpleView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^messagerie/([0-9]+)$', MessagerieView.as_view(), name="messagerie"),
|
||||
re_path(r'^message$', MessageView.as_view(), name="message"),
|
||||
re_path(r'^message/([0-9]+)$', MessageView.as_view(), name="message"),
|
||||
re_path(r'^messagerie/(?P<profile_id>[0-9]+)$', MessagerieView.as_view(), name="messagerie"),
|
||||
re_path(r'^messages$', MessageView.as_view(), name="messages"),
|
||||
re_path(r'^messages/(?P<id>[0-9]+)$', MessageSimpleView.as_view(), name="messages"),
|
||||
]
|
||||
@ -9,17 +9,12 @@ from GestionMessagerie.serializers import MessageSerializer
|
||||
from N3wtSchool import bdd
|
||||
|
||||
class MessagerieView(APIView):
|
||||
def get(self, request, _idProfile):
|
||||
messagesList = bdd.getObjects(_objectName=Messagerie, _columnName='destinataire__id', _value=_idProfile)
|
||||
def get(self, request, profile_id):
|
||||
messagesList = bdd.getObjects(_objectName=Messagerie, _columnName='destinataire__id', _value=profile_id)
|
||||
messages_serializer = MessageSerializer(messagesList, many=True)
|
||||
return JsonResponse(messages_serializer.data, safe=False)
|
||||
|
||||
class MessageView(APIView):
|
||||
def get(self, request, _id):
|
||||
message=bdd.getObject(Messagerie, "id", _id)
|
||||
message_serializer=MessageSerializer(message)
|
||||
return JsonResponse(message_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
message_data=JSONParser().parse(request)
|
||||
message_serializer = MessageSerializer(data=message_data)
|
||||
@ -30,3 +25,10 @@ class MessageView(APIView):
|
||||
return JsonResponse('Nouveau Message ajouté', safe=False)
|
||||
|
||||
return JsonResponse(message_serializer.errors, safe=False)
|
||||
|
||||
class MessageSimpleView(APIView):
|
||||
def get(self, request, id):
|
||||
message=bdd.getObject(Messagerie, "id", id)
|
||||
message_serializer=MessageSerializer(message)
|
||||
return JsonResponse(message_serializer.data, safe=False)
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.conf import settings
|
||||
from GestionLogin.models import Profil
|
||||
from Auth.models import Profile
|
||||
|
||||
class TypeNotif(models.IntegerChoices):
|
||||
NOTIF_NONE = 0, _('Aucune notification')
|
||||
@ -10,7 +10,7 @@ class TypeNotif(models.IntegerChoices):
|
||||
NOTIF_DI = 2, _('Le dossier d\'inscription a été mis à jour')
|
||||
|
||||
class Notification(models.Model):
|
||||
user = models.ForeignKey(Profil, on_delete=models.PROTECT)
|
||||
user = models.ForeignKey(Profile, on_delete=models.PROTECT)
|
||||
message = models.CharField(max_length=255)
|
||||
is_read = models.BooleanField(default=False)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
@ -2,22 +2,22 @@ from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from .models import Notification, TypeNotif
|
||||
from GestionMessagerie.models import Messagerie
|
||||
from GestionInscriptions.models import FicheInscription
|
||||
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=FicheInscription)
|
||||
def notification_DI(sender, instance, created, **kwargs):
|
||||
for responsable in instance.eleve.responsables.all():
|
||||
Notification.objects.create(
|
||||
user=responsable.profilAssocie,
|
||||
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
|
||||
# )
|
||||
|
||||
@ -3,5 +3,5 @@ from django.urls import path, re_path
|
||||
from GestionNotification.views import NotificationView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^notification$', NotificationView.as_view(), name="notification"),
|
||||
re_path(r'^notifications$', NotificationView.as_view(), name="notifications"),
|
||||
]
|
||||
@ -3,7 +3,7 @@ from rest_framework.views import APIView
|
||||
|
||||
from .models import *
|
||||
|
||||
from GestionInscriptions.serializers import NotificationSerializer
|
||||
from Subscriptions.serializers import NotificationSerializer
|
||||
|
||||
from N3wtSchool import bdd
|
||||
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import logging
|
||||
from django.db.models import Q
|
||||
from GestionInscriptions.models import FicheInscription, Profil, Eleve
|
||||
from django.http import JsonResponse
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from Subscriptions.models import RegistrationForm, Student
|
||||
from Auth.models import Profile
|
||||
|
||||
logger = logging.getLogger('N3wtSchool')
|
||||
|
||||
@ -43,12 +46,12 @@ def getProfile(objectList, valueToCheck):
|
||||
return result
|
||||
|
||||
def getEleveByCodeFI(_codeFI):
|
||||
eleve = None
|
||||
ficheInscriptions_List=getAllObjects(FicheInscription)
|
||||
for fi in ficheInscriptions_List:
|
||||
if fi.codeLienInscription == _codeFI:
|
||||
eleve = fi.eleve
|
||||
return eleve
|
||||
student = None
|
||||
ficheInscriptions_List=getAllObjects(RegistrationForm)
|
||||
for rf in ficheInscriptions_List:
|
||||
if rf.codeLienInscription == _codeFI:
|
||||
student = rf.student
|
||||
return student
|
||||
|
||||
def getLastId(_object):
|
||||
result = 1
|
||||
@ -58,31 +61,47 @@ def getLastId(_object):
|
||||
logger.warning("Aucun résultat n'a été trouvé - ")
|
||||
return result
|
||||
|
||||
def searchObjects(_objectName, _searchTerm, _excludeState=None):
|
||||
def searchObjects(_objectName, _searchTerm=None, _excludeStates=None):
|
||||
"""
|
||||
Recherche générique sur les objets avec possibilité d'exclure certains états
|
||||
_objectName: Classe du modèle
|
||||
_objectName: SchoolClass du modèle
|
||||
_searchTerm: Terme de recherche
|
||||
_excludeState: État à exclure de la recherche (optionnel)
|
||||
_excludeStates: Liste d'état à exclure de la recherche (optionnel)
|
||||
"""
|
||||
try:
|
||||
query = _objectName.objects.all()
|
||||
|
||||
# Si on a un état à exclure
|
||||
if _excludeState is not None:
|
||||
query = query.filter(etat__lt=_excludeState)
|
||||
if _excludeStates is not None:
|
||||
query = query.exclude(status__in=_excludeStates)
|
||||
|
||||
# Si on a un terme de recherche
|
||||
if _searchTerm and _searchTerm.strip():
|
||||
terms = _searchTerm.lower().strip().split()
|
||||
for term in terms:
|
||||
query = query.filter(
|
||||
Q(eleve__nom__icontains=term) |
|
||||
Q(eleve__prenom__icontains=term)
|
||||
Q(student__last_name__icontains=term) |
|
||||
Q(student__first_name__icontains=term)
|
||||
)
|
||||
|
||||
return query.order_by('eleve__nom', 'eleve__prenom')
|
||||
return query.order_by('student__last_name', 'student__first_name')
|
||||
|
||||
except _objectName.DoesNotExist:
|
||||
logger.error(f"Aucun résultat n'a été trouvé - {_objectName.__name__} (recherche: {_searchTerm})")
|
||||
logging.error(f"Aucun résultat n'a été trouvé - {_objectName.__name__} (recherche: {_searchTerm})")
|
||||
return None
|
||||
|
||||
def delete_object(model_class, object_id, related_field=None):
|
||||
try:
|
||||
obj = model_class.objects.get(id=object_id)
|
||||
|
||||
if related_field and hasattr(obj, related_field):
|
||||
related_obj = getattr(obj, related_field)
|
||||
if related_obj:
|
||||
related_obj.delete()
|
||||
obj_name = str(obj) # Utiliser la méthode __str__
|
||||
obj.delete()
|
||||
return JsonResponse({'message': f'La suppression de l\'objet {obj_name} a été effectuée avec succès'}, safe=False)
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse({'error': f'L\'objet {model_class.__name__} n\'existe pas avec cet ID'}, status=404, safe=False)
|
||||
except Exception as e:
|
||||
return JsonResponse({'error': f'Une erreur est survenue : {str(e)}'}, status=500, safe=False)
|
||||
|
||||
8
Back-End/N3wtSchool/middleware.py
Normal file
8
Back-End/N3wtSchool/middleware.py
Normal file
@ -0,0 +1,8 @@
|
||||
class ContentSecurityPolicyMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
response['Content-Security-Policy'] = "frame-ancestors 'self' http://localhost:3000"
|
||||
return response
|
||||
@ -13,11 +13,16 @@ https://docs.djangoproject.com/en/5.0/ref/settings/
|
||||
from pathlib import Path
|
||||
import json
|
||||
import os
|
||||
from datetime import timedelta
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
MEDIA_URL = '/data/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'data')
|
||||
|
||||
LOGIN_REDIRECT_URL = '/GestionInscriptions/fichesInscriptions'
|
||||
BASE_URL = os.getenv('CORS_ALLOWED_ORIGINS', 'http://localhost:3000')
|
||||
|
||||
LOGIN_REDIRECT_URL = '/Subscriptions/registerForms'
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||
@ -33,11 +38,13 @@ ALLOWED_HOSTS = ['*']
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'GestionInscriptions.apps.GestioninscriptionsConfig',
|
||||
'GestionLogin.apps.GestionloginConfig',
|
||||
'Subscriptions.apps.GestioninscriptionsConfig',
|
||||
'Auth.apps.GestionloginConfig',
|
||||
'GestionMessagerie.apps.GestionMessagerieConfig',
|
||||
'GestionNotification.apps.GestionNotificationConfig',
|
||||
'GestionEnseignants.apps.GestionenseignantsConfig',
|
||||
'School.apps.SchoolConfig',
|
||||
'Planning.apps.PlanningConfig',
|
||||
'Establishment.apps.EstablishmentConfig',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
@ -49,6 +56,7 @@ INSTALLED_APPS = [
|
||||
'django_celery_beat',
|
||||
'N3wtSchool',
|
||||
'drf_yasg',
|
||||
'rest_framework_simplejwt'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@ -60,6 +68,7 @@ MIDDLEWARE = [
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'N3wtSchool.middleware.ContentSecurityPolicyMiddleware'
|
||||
]
|
||||
|
||||
|
||||
@ -131,12 +140,12 @@ LOGGING = {
|
||||
"level": os.getenv("GESTION_NOTIFICATION_LOG_LEVEL", "INFO"),
|
||||
"propagate": False,
|
||||
},
|
||||
"GestionLogin": {
|
||||
"Auth": {
|
||||
"handlers": ["console"],
|
||||
"level": os.getenv("GESTION_LOGIN_LOG_LEVEL", "INFO"),
|
||||
"propagate": False,
|
||||
},
|
||||
"GestionInscriptions": {
|
||||
"Subscriptions": {
|
||||
"handlers": ["console"],
|
||||
"level": os.getenv("GESTION_INSCRIPTIONS_LOG_LEVEL", "DEBUG"),
|
||||
"propagate": False,
|
||||
@ -146,7 +155,7 @@ LOGGING = {
|
||||
"level": os.getenv("GESTION_MESSAGERIE_LOG_LEVEL", "INFO"),
|
||||
"propagate": False,
|
||||
},
|
||||
"GestionEnseignants": {
|
||||
"School": {
|
||||
"handlers": ["console"],
|
||||
"level": os.getenv("GESTION_ENSEIGNANTS_LOG_LEVEL", "INFO"),
|
||||
"propagate": False,
|
||||
@ -209,7 +218,7 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
#################### Application Settings ##############################
|
||||
########################################################################
|
||||
|
||||
with open('GestionInscriptions/Configuration/application.json', 'r') as f:
|
||||
with open('Subscriptions/Configuration/application.json', 'r') as f:
|
||||
jsonObject = json.load(f)
|
||||
|
||||
DJANGO_SUPERUSER_PASSWORD='admin'
|
||||
@ -223,42 +232,32 @@ EMAIL_HOST_PASSWORD=jsonObject['password']
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_USE_TLS = True
|
||||
EMAIL_USE_SSL = False
|
||||
EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Dossier Inscription'
|
||||
EMAIL_INSCRIPTION_CORPUS = """Bonjour,
|
||||
|
||||
Afin de procéder à l'inscription de votre petit bout, vous trouverez ci-joint le lien vers la page d'authentification : http://localhost:3000/users/login
|
||||
|
||||
S'il s'agit de votre première connexion, veuillez procéder à l'activation de votre compte : http://localhost:3000/users/subscribe
|
||||
identifiant = %s
|
||||
|
||||
Cordialement,
|
||||
"""
|
||||
|
||||
EMAIL_RELANCE_SUBJECT = '[N3WT-SCHOOL] Relance - Dossier Inscription'
|
||||
EMAIL_RELANCE_CORPUS = 'Bonjour,\nN\'ayant pas eu de retour de votre part, nous vous renvoyons le lien vers le formulaire d\'inscription : http://localhost:3000/users/login\nCordialement'
|
||||
EMAIL_REINIT_SUBJECT = 'Réinitialisation du mot de passe'
|
||||
EMAIL_REINIT_CORPUS = 'Bonjour,\nVous trouverez ci-joint le lien pour réinitialiser votre mot de passe : http://localhost:3000/users/password/reset?uuid=%s\nCordialement'
|
||||
|
||||
DOCUMENT_DIR = 'documents'
|
||||
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
CORS_ALLOW_ALL_HEADERS = True
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
CORS_ALLOW_HEADERS = [
|
||||
'content-type',
|
||||
'authorization',
|
||||
'X-Auth-Token',
|
||||
'x-csrftoken'
|
||||
]
|
||||
|
||||
CORS_ALLOWED_ORIGINS = [
|
||||
"http://localhost:3000"
|
||||
os.getenv('CORS_ALLOWED_ORIGINS', 'http://localhost:3000')
|
||||
]
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
"http://localhost:3000", # Front Next.js
|
||||
"http://localhost:8080" # Insomnia
|
||||
]
|
||||
CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost:3000,http://localhost:8080').split(',')
|
||||
|
||||
CSRF_COOKIE_HTTPONLY = False
|
||||
CSRF_COOKIE_SECURE = False
|
||||
CSRF_COOKIE_NAME = 'csrftoken'
|
||||
|
||||
|
||||
USE_TZ = True
|
||||
TZ_APPLI = 'Europe/Paris'
|
||||
|
||||
@ -273,8 +272,8 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
AUTH_USER_MODEL = 'GestionLogin.Profil'
|
||||
AUTHENTICATION_BACKENDS = ('GestionLogin.backends.EmailBackend', )
|
||||
AUTH_USER_MODEL = 'Auth.Profile'
|
||||
AUTHENTICATION_BACKENDS = ('Auth.backends.EmailBackend', )
|
||||
SILENCED_SYSTEM_CHECKS = ["auth.W004"]
|
||||
|
||||
EXPIRATION_URL_NB_DAYS = 7
|
||||
@ -287,8 +286,11 @@ NB_RESULT_PER_PAGE = 8
|
||||
NB_MAX_PAGE = 100
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_PAGINATION_CLASS': 'GestionInscriptions.pagination.CustomPagination',
|
||||
'PAGE_SIZE': NB_RESULT_PER_PAGE
|
||||
'DEFAULT_PAGINATION_CLASS': 'Subscriptions.pagination.CustomPagination',
|
||||
'PAGE_SIZE': NB_RESULT_PER_PAGE,
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||||
),
|
||||
}
|
||||
|
||||
CELERY_BROKER_URL = 'redis://redis:6379/0'
|
||||
@ -307,3 +309,25 @@ REDIS_DB = 0
|
||||
REDIS_PASSWORD = None
|
||||
|
||||
SECRET_KEY = 'QWQ8bYlCz1NpQ9G0vR5kxMnvWszfH2y3'
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
|
||||
'ROTATE_REFRESH_TOKENS': False,
|
||||
'BLACKLIST_AFTER_ROTATION': True,
|
||||
'ALGORITHM': 'HS256',
|
||||
'SIGNING_KEY': SECRET_KEY,
|
||||
'VERIFYING_KEY': None,
|
||||
'AUTH_HEADER_TYPES': ('Bearer',),
|
||||
'USER_ID_FIELD': 'id',
|
||||
'USER_ID_CLAIM': 'user_id',
|
||||
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
|
||||
'TOKEN_TYPE_CLAIM': 'token_type',
|
||||
}
|
||||
|
||||
# Configuration for DocuSeal JWT
|
||||
DOCUSEAL_JWT = {
|
||||
'ALGORITHM': 'HS256',
|
||||
'SIGNING_KEY': SECRET_KEY,
|
||||
'EXPIRATION_DELTA': timedelta(hours=1),
|
||||
'API_KEY': os.getenv('DOCUSEAL_API_KEY')
|
||||
}
|
||||
@ -16,6 +16,6 @@ def setup_periodic_tasks(sender, **kwargs):
|
||||
PeriodicTask.objects.get_or_create(
|
||||
interval=schedule, # Utiliser l'intervalle défini ci-dessus
|
||||
name='Tâche périodique toutes les 5 secondes',
|
||||
task='GestionInscriptions.tasks.check_for_signature_deadlines', # Remplacer par le nom de ta tâche
|
||||
task='Subscriptions.tasks.check_for_signature_deadlines', # Remplacer par le nom de ta tâche
|
||||
kwargs=json.dumps({}) # Si nécessaire, ajoute
|
||||
)
|
||||
@ -7,7 +7,7 @@ Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
SchoolClass-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
@ -16,6 +16,8 @@ Including another URLconf
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path, re_path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from rest_framework import permissions
|
||||
from drf_yasg.views import get_schema_view
|
||||
from drf_yasg import openapi
|
||||
@ -37,13 +39,19 @@ schema_view = get_schema_view(
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path("GestionInscriptions/", include(("GestionInscriptions.urls", 'GestionInscriptions'), namespace='GestionInscriptions')),
|
||||
path("GestionLogin/", include(("GestionLogin.urls", 'GestionLogin'), namespace='GestionLogin')),
|
||||
path("Subscriptions/", include(("Subscriptions.urls", 'Subscriptions'), namespace='Subscriptions')),
|
||||
path("Auth/", include(("Auth.urls", 'Auth'), namespace='Auth')),
|
||||
path("GestionMessagerie/", include(("GestionMessagerie.urls", 'GestionMessagerie'), namespace='GestionMessagerie')),
|
||||
path("GestionNotification/", include(("GestionNotification.urls", 'GestionNotification'), namespace='GestionNotification')),
|
||||
path("GestionEnseignants/", include(("GestionEnseignants.urls", 'GestionEnseignants'), namespace='GestionEnseignants')),
|
||||
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'),
|
||||
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
1
Back-End/Planning/__init__.py
Normal file
1
Back-End/Planning/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
default_app_config = 'Planning.apps.PlanningConfig'
|
||||
3
Back-End/Planning/admin.py
Normal file
3
Back-End/Planning/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
7
Back-End/Planning/apps.py
Normal file
7
Back-End/Planning/apps.py
Normal file
@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class PlanningConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'Planning'
|
||||
|
||||
|
||||
38
Back-End/Planning/models.py
Normal file
38
Back-End/Planning/models.py
Normal file
@ -0,0 +1,38 @@
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from Establishment.models import Establishment
|
||||
|
||||
class RecursionType(models.IntegerChoices):
|
||||
RECURSION_NONE = 0, _('Aucune')
|
||||
RECURSION_DAILY = 1, _('Quotidienne')
|
||||
RECURSION_WEEKLY = 2, _('Hebdomadaire')
|
||||
RECURSION_MONTHLY = 3, _('Mensuel')
|
||||
RECURSION_CUSTOM = 4, _('Personnalisé')
|
||||
|
||||
class Planning(models.Model):
|
||||
establishment = models.ForeignKey(Establishment, on_delete=models.PROTECT)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(default="", blank=True, null=True)
|
||||
color= models.CharField(max_length=255, default="#000000")
|
||||
|
||||
def __str__(self):
|
||||
return f'Planning for {self.user.username}'
|
||||
|
||||
|
||||
class Events(models.Model):
|
||||
planning = models.ForeignKey(Planning, on_delete=models.PROTECT)
|
||||
title = models.CharField(max_length=255)
|
||||
description = models.TextField()
|
||||
start = models.DateTimeField()
|
||||
end = models.DateTimeField()
|
||||
recursionType = models.IntegerField(choices=RecursionType, default=0)
|
||||
color= models.CharField(max_length=255)
|
||||
location = models.CharField(max_length=255, default="", blank=True, null=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f'Event for {self.user.username}'
|
||||
13
Back-End/Planning/serializers.py
Normal file
13
Back-End/Planning/serializers.py
Normal file
@ -0,0 +1,13 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import Planning, Events
|
||||
|
||||
|
||||
class PlanningSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Planning
|
||||
fields = '__all__'
|
||||
class EventsSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Events
|
||||
fields = '__all__'
|
||||
11
Back-End/Planning/urls.py
Normal file
11
Back-End/Planning/urls.py
Normal file
@ -0,0 +1,11 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from Planning.views import PlanningView,PlanningWithIdView,EventsView,EventsWithIdView,UpcomingEventsView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^plannings$', PlanningView.as_view(), name="planning"),
|
||||
re_path(r'^plannings/(?P<id>[0-9]+)$', PlanningWithIdView.as_view(), name="planning"),
|
||||
re_path(r'^events$', EventsView.as_view(), name="events"),
|
||||
re_path(r'^events/(?P<id>[0-9]+)$', EventsWithIdView.as_view(), name="events"),
|
||||
re_path(r'^events/upcoming', UpcomingEventsView.as_view(), name="events"),
|
||||
]
|
||||
97
Back-End/Planning/views.py
Normal file
97
Back-End/Planning/views.py
Normal file
@ -0,0 +1,97 @@
|
||||
from django.http.response import JsonResponse
|
||||
from rest_framework.views import APIView
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import Planning, Events
|
||||
|
||||
from .serializers import PlanningSerializer, EventsSerializer
|
||||
|
||||
from N3wtSchool import bdd
|
||||
|
||||
class PlanningView(APIView):
|
||||
def get(self, request):
|
||||
plannings=bdd.getAllObjects(Planning)
|
||||
planning_serializer=PlanningSerializer(plannings, many=True)
|
||||
|
||||
return JsonResponse(planning_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
planning_serializer = PlanningSerializer(data=request.data)
|
||||
if planning_serializer.is_valid():
|
||||
planning_serializer.save()
|
||||
return JsonResponse(planning_serializer.data, status=201)
|
||||
return JsonResponse(planning_serializer.errors, status=400)
|
||||
|
||||
|
||||
|
||||
class PlanningWithIdView(APIView):
|
||||
def get(self, request,id):
|
||||
planning = Planning.objects.get(pk=id)
|
||||
if planning is None:
|
||||
return JsonResponse({"errorMessage":'Le dossier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
planning_serializer=PlanningSerializer(planning)
|
||||
|
||||
return JsonResponse(planning_serializer.data, safe=False)
|
||||
|
||||
def put(self, request, id):
|
||||
try:
|
||||
planning = Planning.objects.get(pk=id)
|
||||
except Planning.DoesNotExist:
|
||||
return JsonResponse({'error': 'Planning not found'}, status=404)
|
||||
|
||||
planning_serializer = PlanningSerializer(planning, data=request.data)
|
||||
if planning_serializer.is_valid():
|
||||
planning_serializer.save()
|
||||
return JsonResponse(planning_serializer.data)
|
||||
return JsonResponse(planning_serializer.errors, status=400)
|
||||
|
||||
def delete(self, request, id):
|
||||
try:
|
||||
planning = Planning.objects.get(pk=id)
|
||||
except Planning.DoesNotExist:
|
||||
return JsonResponse({'error': 'Planning not found'}, status=404)
|
||||
|
||||
planning.delete()
|
||||
return JsonResponse({'message': 'Planning deleted'}, status=204)
|
||||
|
||||
class EventsView(APIView):
|
||||
def get(self, request):
|
||||
events = bdd.getAllObjects(Events)
|
||||
events_serializer = EventsSerializer(events, many=True)
|
||||
return JsonResponse(events_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
events_serializer = EventsSerializer(data=request.data)
|
||||
if events_serializer.is_valid():
|
||||
events_serializer.save()
|
||||
return JsonResponse(events_serializer.data, status=201)
|
||||
return JsonResponse(events_serializer.errors, status=400)
|
||||
|
||||
class EventsWithIdView(APIView):
|
||||
def put(self, request, id):
|
||||
try:
|
||||
event = Events.objects.get(pk=id)
|
||||
except Events.DoesNotExist:
|
||||
return JsonResponse({'error': 'Event not found'}, status=404)
|
||||
|
||||
events_serializer = EventsSerializer(event, data=request.data)
|
||||
if events_serializer.is_valid():
|
||||
events_serializer.save()
|
||||
return JsonResponse(events_serializer.data)
|
||||
return JsonResponse(events_serializer.errors, status=400)
|
||||
|
||||
def delete(self, request, id):
|
||||
try:
|
||||
event = Events.objects.get(pk=id)
|
||||
except Events.DoesNotExist:
|
||||
return JsonResponse({'error': 'Event not found'}, status=404)
|
||||
|
||||
event.delete()
|
||||
return JsonResponse({'message': 'Event deleted'}, status=204)
|
||||
|
||||
class UpcomingEventsView(APIView):
|
||||
def get(self, request):
|
||||
current_date = timezone.now()
|
||||
upcoming_events = Events.objects.filter(start__gte=current_date)
|
||||
events_serializer = EventsSerializer(upcoming_events, many=True)
|
||||
return JsonResponse(events_serializer.data, safe=False)
|
||||
1
Back-End/School/__init__.py
Normal file
1
Back-End/School/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
default_app_config = 'School.apps.SchoolConfig'
|
||||
3
Back-End/School/admin.py
Normal file
3
Back-End/School/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
Back-End/School/apps.py
Normal file
5
Back-End/School/apps.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class SchoolConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'School'
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
427
Back-End/School/management/commands/init_mock_datas.py
Normal file
427
Back-End/School/management/commands/init_mock_datas.py
Normal file
@ -0,0 +1,427 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from Subscriptions.models import (
|
||||
RegistrationForm,
|
||||
Student,
|
||||
Guardian,
|
||||
Fee,
|
||||
Discount,
|
||||
RegistrationFileGroup,
|
||||
RegistrationTemplateMaster,
|
||||
RegistrationTemplate
|
||||
)
|
||||
from Auth.models import Profile, ProfileRole
|
||||
from School.models import (
|
||||
FeeType,
|
||||
Speciality,
|
||||
Teacher,
|
||||
SchoolClass,
|
||||
PaymentMode,
|
||||
PaymentModeType,
|
||||
PaymentPlan,
|
||||
PaymentPlanType,
|
||||
DiscountType
|
||||
)
|
||||
from django.utils import timezone
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django.core.files import File
|
||||
from django.core.exceptions import SuspiciousFileOperation
|
||||
import os
|
||||
from django.conf import settings
|
||||
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, StudentSerializer
|
||||
|
||||
# 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.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_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 init_establishments(self):
|
||||
establishments_data = self.load_data('establishments.json')
|
||||
|
||||
self.establishments = []
|
||||
for establishment_data in establishments_data:
|
||||
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.ERROR(f'Error in data for establishment: {serializer.errors}'))
|
||||
|
||||
def init_profiles(self):
|
||||
fake = Faker()
|
||||
|
||||
for _ in range(50):
|
||||
# Générer des données fictives pour le profil
|
||||
profile_data = {
|
||||
"username": fake.user_name(),
|
||||
"email": fake.email(),
|
||||
"password": "Provisoire01!",
|
||||
"code": "",
|
||||
"datePeremption": ""
|
||||
}
|
||||
|
||||
# Créer le profil
|
||||
profile_serializer = ProfileSerializer(data=profile_data)
|
||||
if profile_serializer.is_valid():
|
||||
profile = profile_serializer.save()
|
||||
profile.set_password(profile_data["password"])
|
||||
profile.save()
|
||||
self.stdout.write(self.style.SUCCESS(f'Profile {profile.email} created successfully'))
|
||||
|
||||
# Créer entre 1 et 3 ProfileRole pour chaque profil
|
||||
num_roles = random.randint(1, 3)
|
||||
created_roles = set()
|
||||
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])
|
||||
|
||||
# Vérifier si le rôle existe déjà pour cet établissement
|
||||
if (establishment.id, role_type) in created_roles:
|
||||
continue
|
||||
|
||||
profile_role_data = {
|
||||
"profile": profile.id,
|
||||
"establishment": establishment.id,
|
||||
"role_type": role_type,
|
||||
"is_active": random.choice([True, False])
|
||||
}
|
||||
|
||||
profile_role_serializer = ProfileRoleSerializer(data=profile_role_data)
|
||||
if profile_role_serializer.is_valid():
|
||||
profile_role_serializer.save()
|
||||
created_roles.add((establishment.id, role_type))
|
||||
self.stdout.write(self.style.SUCCESS(f'ProfileRole for {profile.email} created successfully with role type {role_type}'))
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(f'Error in data for profile role: {profile_role_serializer.errors}'))
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(f'Error in data for profile: {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"] = fee_data['name']
|
||||
fee_data["establishment"] = establishment.id
|
||||
fee_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])
|
||||
|
||||
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 init_discounts(self):
|
||||
discounts_data = self.load_data('discounts.json')
|
||||
|
||||
for discount_data in discounts_data:
|
||||
establishment = random.choice(self.establishments)
|
||||
discount_data["name"] = discount_data['name']
|
||||
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])
|
||||
|
||||
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 init_payment_modes(self):
|
||||
modes = [PaymentModeType.SEPA, PaymentModeType.TRANSFER, PaymentModeType.CHECK, PaymentModeType.CASH]
|
||||
types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 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 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)
|
||||
}
|
||||
|
||||
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}'))
|
||||
|
||||
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 init_specialities(self):
|
||||
specialities_data = self.load_data('specialities.json')
|
||||
|
||||
for speciality_data in specialities_data:
|
||||
establishment = random.choice(self.establishments)
|
||||
speciality_data["name"] = speciality_data['name']
|
||||
speciality_data["establishment"] = establishment.id
|
||||
|
||||
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 existants avec un rôle ECOLE ou ADMIN
|
||||
profiles_with_roles = Profile.objects.filter(roles__role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN]).distinct()
|
||||
|
||||
if not profiles_with_roles.exists():
|
||||
self.stdout.write(self.style.ERROR('No profiles with role_type ECOLE or ADMIN found'))
|
||||
return
|
||||
|
||||
used_profiles = set()
|
||||
|
||||
for _ in range(15):
|
||||
# Récupérer un profil aléatoire qui n'a pas encore été utilisé
|
||||
available_profiles = profiles_with_roles.exclude(id__in=used_profiles)
|
||||
if not available_profiles.exists():
|
||||
self.stdout.write(self.style.ERROR('Not enough profiles with role_type ECOLE or ADMIN available'))
|
||||
break
|
||||
|
||||
profile = random.choice(available_profiles)
|
||||
used_profiles.add(profile.id)
|
||||
|
||||
# Récupérer les ProfileRole associés au profil avec les rôles ECOLE ou ADMIN
|
||||
profile_roles = ProfileRole.objects.filter(profile=profile, role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN])
|
||||
|
||||
if not profile_roles.exists():
|
||||
self.stdout.write(self.style.ERROR(f'No ProfileRole with role_type ECOLE or ADMIN found for profile {profile.email}'))
|
||||
continue
|
||||
|
||||
profile_role = random.choice(profile_roles)
|
||||
|
||||
# Générer des données fictives pour l'enseignant
|
||||
teacher_data = {
|
||||
"last_name": fake.last_name(),
|
||||
"first_name": fake.first_name(),
|
||||
"profile_role": profile_role.id
|
||||
}
|
||||
|
||||
establishment_specialities = list(Speciality.objects.filter(establishment=profile_role.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 {profile_role.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 init_school_classes(self):
|
||||
school_classes_data = self.load_data('school_classes.json')
|
||||
|
||||
for index, class_data in enumerate(school_classes_data, start=1):
|
||||
# Randomize establishment
|
||||
establishment = random.choice(self.establishments)
|
||||
class_data["atmosphere_name"] = f"Classe {index}"
|
||||
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))
|
||||
if len(establishment_teachers) > 0:
|
||||
num_teachers = min(2, len(establishment_teachers))
|
||||
selected_teachers = random.sample(establishment_teachers, num_teachers)
|
||||
teachers_ids = [teacher.id for teacher in selected_teachers]
|
||||
else:
|
||||
teachers_ids = []
|
||||
|
||||
# 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 init_file_group(self):
|
||||
fake = Faker()
|
||||
|
||||
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()}"
|
||||
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'))
|
||||
|
||||
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()
|
||||
|
||||
# Récupérer tous les profils existants avec un ProfileRole Parent
|
||||
profiles_with_parent_role = Profile.objects.filter(roles__role_type=ProfileRole.RoleType.PROFIL_PARENT).distinct()
|
||||
|
||||
if not profiles_with_parent_role.exists():
|
||||
self.stdout.write(self.style.ERROR('No profiles with ProfileRole Parent found'))
|
||||
return
|
||||
|
||||
used_profiles = set()
|
||||
|
||||
for _ in range(50):
|
||||
# Récupérer un profil aléatoire qui n'a pas encore été utilisé
|
||||
available_profiles = profiles_with_parent_role.exclude(id__in=used_profiles)
|
||||
if not available_profiles.exists():
|
||||
self.stdout.write(self.style.ERROR('Not enough profiles with ProfileRole Parent available'))
|
||||
break
|
||||
|
||||
profile = random.choice(available_profiles)
|
||||
used_profiles.add(profile.id)
|
||||
|
||||
# Récupérer le ProfileRole Parent associé au profil
|
||||
profile_roles = ProfileRole.objects.filter(profile=profile, role_type=ProfileRole.RoleType.PROFIL_PARENT)
|
||||
profile_role = random.choice(profile_roles)
|
||||
|
||||
# Générer des données fictives pour le guardian
|
||||
guardian_data = {
|
||||
"profile_role": profile_role.id,
|
||||
"last_name": fake.last_name(),
|
||||
"first_name": fake.first_name(),
|
||||
"birth_date": fake.date_of_birth().strftime('%Y-%m-%d'),
|
||||
"address": fake.address(),
|
||||
"phone": fake.phone_number(),
|
||||
"profession": fake.job()
|
||||
}
|
||||
|
||||
# Générer des données fictives pour l'étudiant
|
||||
student_data = {
|
||||
"last_name": fake.last_name(),
|
||||
"first_name": fake.first_name(),
|
||||
"address": fake.address(),
|
||||
"birth_date": fake.date_of_birth(),
|
||||
"birth_place": fake.city(),
|
||||
"birth_postal_code": fake.postcode(),
|
||||
"nationality": fake.country(),
|
||||
"attending_physician": fake.name(),
|
||||
"level": fake.random_int(min=1, max=6),
|
||||
"guardians": [guardian_data],
|
||||
"sibling": []
|
||||
}
|
||||
|
||||
# Créer ou mettre à jour l'étudiant
|
||||
student_serializer = StudentSerializer(data=student_data)
|
||||
if student_serializer.is_valid():
|
||||
student = student_serializer.save()
|
||||
self.stdout.write(self.style.SUCCESS(f'Student {student.last_name} created successfully'))
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(f'Error in data for student: {student_serializer.errors}'))
|
||||
continue
|
||||
|
||||
# Récupérer les frais et les réductions
|
||||
fees = Fee.objects.filter(id__in=[1, 2, 3, 4])
|
||||
discounts = Discount.objects.filter(id__in=[1])
|
||||
|
||||
# Créer les données du formulaire d'inscription
|
||||
register_form_data = {
|
||||
"fileGroup": RegistrationFileGroup.objects.get(id=fake.random_int(min=1, max=file_group_count)),
|
||||
"establishment": profile_role.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,
|
||||
establishment=profile_role.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'))
|
||||
42
Back-End/School/management/mock_datas/discounts.json
Normal file
42
Back-End/School/management/mock_datas/discounts.json
Normal file
@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"name": "Parrainage",
|
||||
"amount": "10.00",
|
||||
"description": "Réduction pour parrainage"
|
||||
},
|
||||
{
|
||||
"name": "Réinscription",
|
||||
"amount": "100.00",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
23
Back-End/School/management/mock_datas/establishments.json
Normal file
23
Back-End/School/management/mock_datas/establishments.json
Normal file
@ -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": ""
|
||||
}
|
||||
]
|
||||
32
Back-End/School/management/mock_datas/fees.json
Normal file
32
Back-End/School/management/mock_datas/fees.json
Normal file
@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"name": "Frais d'inscription",
|
||||
"base_amount": "150.00",
|
||||
"description": "Montant de base",
|
||||
"is_active": true
|
||||
},
|
||||
{
|
||||
"name": "Matériel",
|
||||
"base_amount": "85.00",
|
||||
"description": "Livres / jouets",
|
||||
"is_active": true
|
||||
},
|
||||
{
|
||||
"name": "Sorties périscolaires",
|
||||
"base_amount": "120.00",
|
||||
"description": "Sorties",
|
||||
"is_active": true
|
||||
},
|
||||
{
|
||||
"name": "Les colibris",
|
||||
"base_amount": "4500.00",
|
||||
"description": "TPS / PS / MS / GS",
|
||||
"is_active": true
|
||||
},
|
||||
{
|
||||
"name": "Les butterflies",
|
||||
"base_amount": "5000.00",
|
||||
"description": "CP / CE1 / CE2 / CM1 / CM2",
|
||||
"is_active": true
|
||||
}
|
||||
]
|
||||
52
Back-End/School/management/mock_datas/school_classes.json
Normal file
52
Back-End/School/management/mock_datas/school_classes.json
Normal file
@ -0,0 +1,52 @@
|
||||
[
|
||||
{
|
||||
"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]
|
||||
},
|
||||
{
|
||||
"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]
|
||||
},
|
||||
{
|
||||
"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]
|
||||
},
|
||||
{
|
||||
"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]
|
||||
},
|
||||
{
|
||||
"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]
|
||||
}
|
||||
]
|
||||
42
Back-End/School/management/mock_datas/specialities.json
Normal file
42
Back-End/School/management/mock_datas/specialities.json
Normal file
@ -0,0 +1,42 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"name": "SCIENCES",
|
||||
"color_code": "#00FF00"
|
||||
},
|
||||
{
|
||||
"name": "MUSIQUE",
|
||||
"color_code": "#0000FF"
|
||||
},
|
||||
{
|
||||
"name": "ART",
|
||||
"color_code": "#FF00FF"
|
||||
},
|
||||
{
|
||||
"name": "INFORMATIQUE",
|
||||
"color_code": "#00FFFF"
|
||||
}
|
||||
]
|
||||
134
Back-End/School/models.py
Normal file
134
Back-End/School/models.py
Normal file
@ -0,0 +1,134 @@
|
||||
from django.db import models
|
||||
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
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
LEVEL_CHOICES = [
|
||||
(1, 'Très Petite Section (TPS)'),
|
||||
(2, 'Petite Section (PS)'),
|
||||
(3, 'Moyenne Section (MS)'),
|
||||
(4, 'Grande Section (GS)'),
|
||||
(5, 'Cours Préparatoire (CP)'),
|
||||
(6, 'Cours Élémentaire 1 (CE1)'),
|
||||
(7, 'Cours Élémentaire 2 (CE2)'),
|
||||
(8, 'Cours Moyen 1 (CM1)'),
|
||||
(9, 'Cours Moyen 2 (CM2)')
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
class Teacher(models.Model):
|
||||
last_name = models.CharField(max_length=100)
|
||||
first_name = models.CharField(max_length=100)
|
||||
specialities = models.ManyToManyField(Speciality, 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):
|
||||
return f"{self.last_name} {self.first_name}"
|
||||
|
||||
class SchoolClass(models.Model):
|
||||
PLANNING_TYPE_CHOICES = [
|
||||
(1, 'Annuel'),
|
||||
(2, 'Semestriel'),
|
||||
(3, 'Trimestriel')
|
||||
]
|
||||
|
||||
atmosphere_name = models.CharField(max_length=255, null=True, blank=True)
|
||||
age_range = models.JSONField(blank=True)
|
||||
number_of_students = models.PositiveIntegerField(blank=True)
|
||||
teaching_language = models.CharField(max_length=255, blank=True)
|
||||
school_year = models.CharField(max_length=9, blank=True)
|
||||
updated_date = models.DateTimeField(auto_now=True)
|
||||
teachers = models.ManyToManyField(Teacher, blank=True)
|
||||
levels = ArrayField(models.IntegerField(choices=LEVEL_CHOICES), default=list)
|
||||
type = models.IntegerField(choices=PLANNING_TYPE_CHOICES, default=1)
|
||||
time_range = models.JSONField(default=list)
|
||||
opening_days = ArrayField(models.IntegerField(), default=list)
|
||||
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='school_classes')
|
||||
|
||||
def __str__(self):
|
||||
return self.atmosphere_name
|
||||
|
||||
class Planning(models.Model):
|
||||
level = models.IntegerField(choices=LEVEL_CHOICES, null=True, blank=True)
|
||||
school_class = models.ForeignKey(SchoolClass, null=True, blank=True, related_name='plannings', on_delete=models.CASCADE)
|
||||
schedule = JSONField(default=dict)
|
||||
|
||||
def __str__(self):
|
||||
return f'Planning for {self.level} of {self.school_class.atmosphere_name}'
|
||||
|
||||
class PaymentPlanType(models.IntegerChoices):
|
||||
ONE_TIME = 1, '1 fois'
|
||||
THREE_TIMES = 3, '3 fois'
|
||||
TEN_TIMES = 10, '10 fois'
|
||||
TWELVE_TIMES = 12, '12 fois'
|
||||
|
||||
class DiscountType(models.IntegerChoices):
|
||||
CURRENCY = 0, 'Currency'
|
||||
PERCENT = 1, 'Percent'
|
||||
|
||||
class FeeType(models.IntegerChoices):
|
||||
REGISTRATION_FEE = 0, 'Registration Fee'
|
||||
TUITION_FEE = 1, 'Tuition Fee'
|
||||
|
||||
class PaymentModeType(models.IntegerChoices):
|
||||
SEPA = 1, 'Prélèvement SEPA'
|
||||
TRANSFER = 2, 'Virement'
|
||||
CHECK = 3, 'Chèque'
|
||||
CASH = 4, 'Espèce'
|
||||
|
||||
class Discount(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
||||
description = models.TextField(blank=True)
|
||||
discount_type = models.IntegerField(choices=DiscountType.choices, default=DiscountType.CURRENCY)
|
||||
type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='discounts')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Fee(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
base_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
||||
description = models.TextField(blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE)
|
||||
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='fees')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class PaymentPlan(models.Model):
|
||||
frequency = models.IntegerField(choices=PaymentPlanType.choices, default=PaymentPlanType.ONE_TIME)
|
||||
due_dates = ArrayField(models.DateField(), blank=True)
|
||||
type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE)
|
||||
is_active = models.BooleanField(default=False)
|
||||
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='payment_plans')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_frequency_display()} - {self.get_type_display()}"
|
||||
|
||||
class PaymentMode(models.Model):
|
||||
mode = models.IntegerField(choices=PaymentModeType.choices, default=PaymentModeType.SEPA)
|
||||
type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE)
|
||||
is_active = models.BooleanField(default=False)
|
||||
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='payment_modes')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_mode_display()} - {self.get_type_display()}"
|
||||
|
||||
257
Back-End/School/serializers.py
Normal file
257
Back-End/School/serializers.py
Normal file
@ -0,0 +1,257 @@
|
||||
from rest_framework import serializers
|
||||
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 Auth.serializers import ProfileRoleSerializer
|
||||
from N3wtSchool import settings, bdd
|
||||
from django.utils import timezone
|
||||
import pytz
|
||||
|
||||
class SpecialitySerializer(serializers.ModelSerializer):
|
||||
updated_date_formatted = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Speciality
|
||||
fields = '__all__'
|
||||
|
||||
def get_updated_date_formatted(self, obj):
|
||||
utc_time = timezone.localtime(obj.updated_date) # Convert to local time
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
local_time = utc_time.astimezone(local_tz)
|
||||
|
||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
class TeacherDetailSerializer(serializers.ModelSerializer):
|
||||
specialities = SpecialitySerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Teacher
|
||||
fields = ['id', 'last_name', 'first_name', 'email', 'specialities']
|
||||
|
||||
class TeacherSerializer(serializers.ModelSerializer):
|
||||
specialities = serializers.PrimaryKeyRelatedField(queryset=Speciality.objects.all(), many=True, required=False)
|
||||
specialities_details = serializers.SerializerMethodField()
|
||||
updated_date_formatted = serializers.SerializerMethodField()
|
||||
role_type_display = serializers.SerializerMethodField()
|
||||
role_type = serializers.IntegerField(write_only=True)
|
||||
associated_profile_email = serializers.EmailField(write_only=True)
|
||||
profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=False)
|
||||
associated_profile_email_display = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Teacher
|
||||
fields = '__all__'
|
||||
|
||||
def create_or_update_profile_role(self, profile, associated_profile_email, establishment_id, role_type):
|
||||
# Mettre à jour l'email du profil si nécessaire
|
||||
if profile.email != associated_profile_email:
|
||||
profile.email = associated_profile_email
|
||||
profile.username = associated_profile_email
|
||||
profile.save()
|
||||
|
||||
profile_role, created = ProfileRole.objects.update_or_create(
|
||||
profile=profile,
|
||||
establishment_id=establishment_id,
|
||||
defaults={'role_type': role_type, 'is_active': True}
|
||||
)
|
||||
|
||||
if not created:
|
||||
profile_role.role_type = role_type
|
||||
profile_role.establishment_id = establishment_id
|
||||
profile_role.save()
|
||||
|
||||
return profile_role
|
||||
|
||||
def create(self, validated_data):
|
||||
specialities_data = validated_data.pop('specialities', None)
|
||||
associated_profile_email = validated_data.pop('associated_profile_email')
|
||||
establishment_id = validated_data.pop('establishment')
|
||||
role_type = validated_data.pop('role_type')
|
||||
|
||||
profile, created = Profile.objects.get_or_create(
|
||||
email=associated_profile_email,
|
||||
defaults={'username': associated_profile_email}
|
||||
)
|
||||
|
||||
profile_role = self.create_or_update_profile_role(profile, associated_profile_email, establishment_id, role_type)
|
||||
|
||||
teacher = Teacher.objects.create(profile_role=profile_role, **validated_data)
|
||||
if specialities_data:
|
||||
teacher.specialities.set(specialities_data)
|
||||
teacher.save()
|
||||
return teacher
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
specialities_data = validated_data.pop('specialities', [])
|
||||
associated_profile_email = validated_data.pop('associated_profile_email', instance.profile_role.profile.email)
|
||||
establishment_id = validated_data.get('establishment', instance.profile_role.establishment.id)
|
||||
role_type = validated_data.get('role_type', instance.profile_role.role_type)
|
||||
|
||||
profile = instance.profile_role.profile
|
||||
|
||||
profile_role = self.create_or_update_profile_role(profile, associated_profile_email, establishment_id, role_type)
|
||||
instance.profile_role = profile_role
|
||||
|
||||
instance.last_name = validated_data.get('last_name', instance.last_name)
|
||||
instance.first_name = validated_data.get('first_name', instance.first_name)
|
||||
instance.save()
|
||||
if specialities_data:
|
||||
instance.specialities.set(specialities_data)
|
||||
return instance
|
||||
|
||||
def get_updated_date_formatted(self, obj):
|
||||
utc_time = timezone.localtime(obj.updated_date) # Convert to local time
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
local_time = utc_time.astimezone(local_tz)
|
||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
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):
|
||||
if obj.profile_role and obj.profile_role.profile:
|
||||
return obj.profile_role.profile.email
|
||||
return None
|
||||
|
||||
def get_role_type_display(self, obj):
|
||||
if obj.profile_role:
|
||||
return obj.profile_role.role_type
|
||||
return None
|
||||
|
||||
def get_associated_profile_email_display(self, obj):
|
||||
return self.get_associated_profile_email(obj)
|
||||
|
||||
class PlanningSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Planning
|
||||
fields = ['id', 'level', 'schedule']
|
||||
|
||||
def to_internal_value(self, data):
|
||||
internal_value = super().to_internal_value(data)
|
||||
internal_value['schedule'] = data.get('schedule', {})
|
||||
return internal_value
|
||||
|
||||
class SchoolClassSerializer(serializers.ModelSerializer):
|
||||
updated_date_formatted = serializers.SerializerMethodField()
|
||||
teachers = serializers.PrimaryKeyRelatedField(queryset=Teacher.objects.all(), many=True, required=False)
|
||||
establishment = serializers.PrimaryKeyRelatedField(queryset=Establishment.objects.all(), required=False)
|
||||
teachers_details = serializers.SerializerMethodField()
|
||||
students = serializers.PrimaryKeyRelatedField(queryset=Student.objects.all(), many=True, required=False)
|
||||
|
||||
class Meta:
|
||||
model = SchoolClass
|
||||
fields = '__all__'
|
||||
|
||||
def create(self, validated_data):
|
||||
teachers_data = validated_data.pop('teachers', [])
|
||||
levels_data = validated_data.pop('levels', [])
|
||||
plannings_data = validated_data.pop('plannings', [])
|
||||
|
||||
school_class = SchoolClass.objects.create(
|
||||
atmosphere_name=validated_data.get('atmosphere_name', ''),
|
||||
age_range=validated_data.get('age_range', []),
|
||||
number_of_students=validated_data.get('number_of_students', 0),
|
||||
teaching_language=validated_data.get('teaching_language', ''),
|
||||
school_year=validated_data.get('school_year', ''),
|
||||
levels=levels_data,
|
||||
type=validated_data.get('type', 1),
|
||||
time_range=validated_data.get('time_range', ['08:30', '17:30']),
|
||||
opening_days=validated_data.get('opening_days', [1, 2, 4, 5]),
|
||||
establishment=validated_data.get('establishment', None)
|
||||
)
|
||||
|
||||
school_class.teachers.set(teachers_data)
|
||||
|
||||
for planning_data in plannings_data:
|
||||
Planning.objects.create(
|
||||
school_class=school_class,
|
||||
level=planning_data['level'],
|
||||
schedule=planning_data.get('schedule', {})
|
||||
)
|
||||
|
||||
return school_class
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
teachers_data = validated_data.pop('teachers', [])
|
||||
levels_data = validated_data.pop('levels', [])
|
||||
plannings_data = validated_data.pop('plannings', [])
|
||||
|
||||
instance.atmosphere_name = validated_data.get('atmosphere_name', instance.atmosphere_name)
|
||||
instance.age_range = validated_data.get('age_range', instance.age_range)
|
||||
instance.number_of_students = validated_data.get('number_of_students', instance.number_of_students)
|
||||
instance.teaching_language = validated_data.get('teaching_language', instance.teaching_language)
|
||||
instance.school_year = validated_data.get('school_year', instance.school_year)
|
||||
instance.levels = levels_data
|
||||
instance.type = validated_data.get('type', instance.type)
|
||||
instance.time_range = validated_data.get('time_range', instance.time_range)
|
||||
instance.opening_days = validated_data.get('opening_days', instance.opening_days)
|
||||
instance.establishment = validated_data.get('establishment', instance.establishment)
|
||||
|
||||
instance.save()
|
||||
instance.teachers.set(teachers_data)
|
||||
|
||||
existing_plannings = {planning.level: planning for planning in instance.plannings.all()}
|
||||
|
||||
for planning_data in plannings_data:
|
||||
level = planning_data['level']
|
||||
if level in existing_plannings:
|
||||
# Update existing planning
|
||||
planning = existing_plannings[level]
|
||||
planning.schedule = planning_data.get('schedule', planning.schedule)
|
||||
planning.save()
|
||||
else:
|
||||
# Create new planning if level not existing
|
||||
Planning.objects.create(
|
||||
school_class=instance,
|
||||
level=level,
|
||||
schedule=planning_data.get('schedule', {})
|
||||
)
|
||||
|
||||
return instance
|
||||
|
||||
def get_teachers_details(self, obj):
|
||||
return [{'id': teacher.id, 'last_name': teacher.last_name, 'first_name': teacher.first_name} for teacher in obj.teachers.all()]
|
||||
|
||||
def get_updated_date_formatted(self, obj):
|
||||
utc_time = timezone.localtime(obj.updated_date)
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
local_time = utc_time.astimezone(local_tz)
|
||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
class DiscountSerializer(serializers.ModelSerializer):
|
||||
updated_at_formatted = serializers.SerializerMethodField()
|
||||
establishment = serializers.PrimaryKeyRelatedField(queryset=Establishment.objects.all(), required=False)
|
||||
class Meta:
|
||||
model = Discount
|
||||
fields = '__all__'
|
||||
|
||||
def get_updated_at_formatted(self, obj):
|
||||
utc_time = timezone.localtime(obj.updated_at)
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
local_time = utc_time.astimezone(local_tz)
|
||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
class FeeSerializer(serializers.ModelSerializer):
|
||||
updated_at_formatted = serializers.SerializerMethodField()
|
||||
establishment = serializers.PrimaryKeyRelatedField(queryset=Establishment.objects.all(), required=False)
|
||||
|
||||
class Meta:
|
||||
model = Fee
|
||||
fields = '__all__'
|
||||
|
||||
def get_updated_at_formatted(self, obj):
|
||||
utc_time = timezone.localtime(obj.updated_at)
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
local_time = utc_time.astimezone(local_tz)
|
||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||
|
||||
class PaymentPlanSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PaymentPlan
|
||||
fields = '__all__'
|
||||
|
||||
class PaymentModeSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = PaymentMode
|
||||
fields = '__all__'
|
||||
46
Back-End/School/urls.py
Normal file
46
Back-End/School/urls.py
Normal file
@ -0,0 +1,46 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from .views import (
|
||||
TeacherListCreateView,
|
||||
TeacherDetailView,
|
||||
SpecialityListCreateView,
|
||||
SpecialityDetailView,
|
||||
SchoolClassListCreateView,
|
||||
SchoolClassDetailView,
|
||||
PlanningListCreateView,
|
||||
PlanningDetailView,
|
||||
FeeListCreateView,
|
||||
FeeDetailView,
|
||||
DiscountListCreateView,
|
||||
DiscountDetailView,
|
||||
PaymentPlanListCreateView,
|
||||
PaymentPlanDetailView,
|
||||
PaymentModeListCreateView,
|
||||
PaymentModeDetailView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^specialities$', SpecialityListCreateView.as_view(), name="speciality_list_create"),
|
||||
re_path(r'^specialities/(?P<id>[0-9]+)$', SpecialityDetailView.as_view(), name="speciality_detail"),
|
||||
|
||||
re_path(r'^teachers$', TeacherListCreateView.as_view(), name="teacher_list_create"),
|
||||
re_path(r'^teachers/(?P<id>[0-9]+)', TeacherDetailView.as_view(), name="teacher_detail"),
|
||||
|
||||
re_path(r'^schoolClasses$', SchoolClassListCreateView.as_view(), name="school_class_list_create"),
|
||||
re_path(r'^schoolClasses/(?P<id>[0-9]+)', SchoolClassDetailView.as_view(), name="school_class_detail"),
|
||||
|
||||
re_path(r'^plannings$', PlanningListCreateView.as_view(), name="planninglist_create"),
|
||||
re_path(r'^plannings/(?P<id>[0-9]+)$', PlanningDetailView.as_view(), name="planning_detail"),
|
||||
|
||||
re_path(r'^fees$', FeeListCreateView.as_view(), name="fee_list_create"),
|
||||
re_path(r'^fees/(?P<id>[0-9]+)$', FeeDetailView.as_view(), name="fee_detail"),
|
||||
|
||||
re_path(r'^discounts$', DiscountListCreateView.as_view(), name="discount_list_create"),
|
||||
re_path(r'^discounts/(?P<id>[0-9]+)$', DiscountDetailView.as_view(), name="discount_detail"),
|
||||
|
||||
re_path(r'^paymentPlans$', PaymentPlanListCreateView.as_view(), name="payment_plan_list_create"),
|
||||
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")
|
||||
]
|
||||
413
Back-End/School/views.py
Normal file
413
Back-End/School/views.py
Normal file
@ -0,0 +1,413 @@
|
||||
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 (
|
||||
Teacher,
|
||||
Speciality,
|
||||
SchoolClass,
|
||||
Planning,
|
||||
Discount,
|
||||
Fee,
|
||||
PaymentPlan,
|
||||
PaymentMode
|
||||
)
|
||||
from .serializers import (
|
||||
TeacherSerializer,
|
||||
SpecialitySerializer,
|
||||
SchoolClassSerializer,
|
||||
PlanningSerializer,
|
||||
DiscountSerializer,
|
||||
FeeSerializer,
|
||||
PaymentPlanSerializer,
|
||||
PaymentModeSerializer
|
||||
)
|
||||
from N3wtSchool.bdd import delete_object, getAllObjects, getObject
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class SpecialityListCreateView(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)
|
||||
|
||||
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):
|
||||
speciality_data=JSONParser().parse(request)
|
||||
speciality_serializer = SpecialitySerializer(data=speciality_data)
|
||||
|
||||
if speciality_serializer.is_valid():
|
||||
speciality_serializer.save()
|
||||
return JsonResponse(speciality_serializer.data, safe=False, status=status.HTTP_201_CREATED)
|
||||
|
||||
return JsonResponse(speciality_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class SpecialityDetailView(APIView):
|
||||
def get(self, request, id):
|
||||
speciality = getObject(_objectName=Speciality, _columnName='id', _value=id)
|
||||
speciality_serializer=SpecialitySerializer(speciality)
|
||||
return JsonResponse(speciality_serializer.data, safe=False)
|
||||
|
||||
def put(self, request, id):
|
||||
speciality_data=JSONParser().parse(request)
|
||||
speciality = getObject(_objectName=Speciality, _columnName='id', _value=id)
|
||||
speciality_serializer = SpecialitySerializer(speciality, data=speciality_data)
|
||||
if speciality_serializer.is_valid():
|
||||
speciality_serializer.save()
|
||||
return JsonResponse(speciality_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(speciality_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, id):
|
||||
return delete_object(Speciality, id)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class TeacherListCreateView(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)
|
||||
|
||||
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):
|
||||
teacher_data=JSONParser().parse(request)
|
||||
teacher_serializer = TeacherSerializer(data=teacher_data)
|
||||
|
||||
if teacher_serializer.is_valid():
|
||||
teacher_serializer.save()
|
||||
|
||||
return JsonResponse(teacher_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(teacher_serializer.errors, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class TeacherDetailView(APIView):
|
||||
def get (self, request, id):
|
||||
teacher = getObject(_objectName=Teacher, _columnName='id', _value=id)
|
||||
teacher_serializer=TeacherSerializer(teacher)
|
||||
|
||||
return JsonResponse(teacher_serializer.data, safe=False)
|
||||
|
||||
def put(self, request, id):
|
||||
teacher_data=JSONParser().parse(request)
|
||||
teacher = getObject(_objectName=Teacher, _columnName='id', _value=id)
|
||||
teacher_serializer = TeacherSerializer(teacher, data=teacher_data)
|
||||
if teacher_serializer.is_valid():
|
||||
teacher_serializer.save()
|
||||
return JsonResponse(teacher_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(teacher_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, id):
|
||||
return 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):
|
||||
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):
|
||||
classe_data=JSONParser().parse(request)
|
||||
classe_serializer = SchoolClassSerializer(data=classe_data)
|
||||
|
||||
if classe_serializer.is_valid():
|
||||
classe_serializer.save()
|
||||
return JsonResponse(classe_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(classe_serializer.errors, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class SchoolClassDetailView(APIView):
|
||||
def get (self, request, id):
|
||||
schoolClass = getObject(_objectName=SchoolClass, _columnName='id', _value=id)
|
||||
classe_serializer=SchoolClassSerializer(schoolClass)
|
||||
|
||||
return JsonResponse(classe_serializer.data, safe=False)
|
||||
|
||||
def put(self, request, id):
|
||||
classe_data=JSONParser().parse(request)
|
||||
schoolClass = getObject(_objectName=SchoolClass, _columnName='id', _value=id)
|
||||
classe_serializer = SchoolClassSerializer(schoolClass, data=classe_data)
|
||||
if classe_serializer.is_valid():
|
||||
classe_serializer.save()
|
||||
return JsonResponse(classe_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(classe_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, id):
|
||||
return delete_object(SchoolClass, id)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class PlanningListCreateView(APIView):
|
||||
def get(self, request):
|
||||
schedulesList=getAllObjects(Planning)
|
||||
schedules_serializer=PlanningSerializer(schedulesList, many=True)
|
||||
return JsonResponse(schedules_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
planning_data=JSONParser().parse(request)
|
||||
planning_serializer = PlanningSerializer(data=planning_data)
|
||||
|
||||
if planning_serializer.is_valid():
|
||||
planning_serializer.save()
|
||||
return JsonResponse(planning_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(planning_serializer.errors, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class PlanningDetailView(APIView):
|
||||
def get (self, request, id):
|
||||
planning = getObject(_objectName=Planning, _columnName='classe_id', _value=id)
|
||||
planning_serializer=PlanningSerializer(planning)
|
||||
|
||||
return JsonResponse(planning_serializer.data, safe=False)
|
||||
|
||||
def put(self, request, id):
|
||||
planning_data = JSONParser().parse(request)
|
||||
|
||||
try:
|
||||
planning = Planning.objects.get(id=id)
|
||||
except Planning.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Planning.MultipleObjectsReturned:
|
||||
return JsonResponse({'error': 'Multiple objects found'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
planning_serializer = PlanningSerializer(planning, data=planning_data)
|
||||
|
||||
if planning_serializer.is_valid():
|
||||
planning_serializer.save()
|
||||
return JsonResponse(planning_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(planning_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, id):
|
||||
return delete_object(Planning, id)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@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, establishment_id=establishment_id).distinct()
|
||||
fee_serializer = FeeSerializer(fees, many=True)
|
||||
|
||||
return JsonResponse(fee_serializer.data, safe=False, status=status.HTTP_200_OK)
|
||||
|
||||
def post(self, request):
|
||||
fee_data = JSONParser().parse(request)
|
||||
fee_serializer = FeeSerializer(data=fee_data)
|
||||
if fee_serializer.is_valid():
|
||||
fee_serializer.save()
|
||||
return JsonResponse(fee_serializer.data, safe=False, status=status.HTTP_201_CREATED)
|
||||
return JsonResponse(fee_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class FeeDetailView(APIView):
|
||||
def get(self, request, id):
|
||||
try:
|
||||
fee = Fee.objects.get(id=id)
|
||||
fee_serializer = FeeSerializer(fee)
|
||||
return JsonResponse(fee_serializer.data, safe=False)
|
||||
except Fee.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def put(self, request, id):
|
||||
fee_data = JSONParser().parse(request)
|
||||
try:
|
||||
fee = Fee.objects.get(id=id)
|
||||
except Fee.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
fee_serializer = FeeSerializer(fee, data=fee_data, partial=True)
|
||||
if fee_serializer.is_valid():
|
||||
fee_serializer.save()
|
||||
return JsonResponse(fee_serializer.data, safe=False)
|
||||
return JsonResponse(fee_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, id):
|
||||
return delete_object(Fee, id)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
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, establishment_id=establishment_id).distinct()
|
||||
discounts_serializer = DiscountSerializer(discounts, many=True)
|
||||
|
||||
return JsonResponse(discounts_serializer.data, safe=False, status=status.HTTP_200_OK)
|
||||
|
||||
def post(self, request):
|
||||
discount_data = JSONParser().parse(request)
|
||||
discount_serializer = DiscountSerializer(data=discount_data)
|
||||
if discount_serializer.is_valid():
|
||||
discount_serializer.save()
|
||||
return JsonResponse(discount_serializer.data, safe=False, status=status.HTTP_201_CREATED)
|
||||
return JsonResponse(discount_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class DiscountDetailView(APIView):
|
||||
def get(self, request, id):
|
||||
try:
|
||||
discount = Discount.objects.get(id=id)
|
||||
discount_serializer = DiscountSerializer(discount)
|
||||
return JsonResponse(discount_serializer.data, safe=False)
|
||||
except Discount.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def put(self, request, id):
|
||||
discount_data = JSONParser().parse(request)
|
||||
try:
|
||||
discount = Discount.objects.get(id=id)
|
||||
except Discount.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
discount_serializer = DiscountSerializer(discount, data=discount_data, partial=True) # Utilisation de partial=True
|
||||
if discount_serializer.is_valid():
|
||||
discount_serializer.save()
|
||||
return JsonResponse(discount_serializer.data, safe=False)
|
||||
return JsonResponse(discount_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, id):
|
||||
return delete_object(Discount, id)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@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
|
||||
|
||||
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)
|
||||
|
||||
def post(self, request):
|
||||
payment_plan_data = JSONParser().parse(request)
|
||||
payment_plan_serializer = PaymentPlanSerializer(data=payment_plan_data)
|
||||
if payment_plan_serializer.is_valid():
|
||||
payment_plan_serializer.save()
|
||||
return JsonResponse(payment_plan_serializer.data, safe=False, status=status.HTTP_201_CREATED)
|
||||
return JsonResponse(payment_plan_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class PaymentPlanDetailView(APIView):
|
||||
def get(self, request, id):
|
||||
try:
|
||||
payment_plan = PaymentPlan.objects.get(id=id)
|
||||
payment_plan_serializer = PaymentPlanSerializer(payment_plan)
|
||||
return JsonResponse(payment_plan_serializer.data, safe=False)
|
||||
except PaymentPlan.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def put(self, request, id):
|
||||
payment_plan_data = JSONParser().parse(request)
|
||||
try:
|
||||
payment_plan = PaymentPlan.objects.get(id=id)
|
||||
except PaymentPlan.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
payment_plan_serializer = PaymentPlanSerializer(payment_plan, data=payment_plan_data, partial=True)
|
||||
if payment_plan_serializer.is_valid():
|
||||
payment_plan_serializer.save()
|
||||
return JsonResponse(payment_plan_serializer.data, safe=False)
|
||||
return JsonResponse(payment_plan_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, id):
|
||||
return delete_object(PaymentPlan, id)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@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
|
||||
|
||||
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)
|
||||
|
||||
def post(self, request):
|
||||
payment_mode_data = JSONParser().parse(request)
|
||||
payment_mode_serializer = PaymentModeSerializer(data=payment_mode_data)
|
||||
if payment_mode_serializer.is_valid():
|
||||
payment_mode_serializer.save()
|
||||
return JsonResponse(payment_mode_serializer.data, safe=False, status=status.HTTP_201_CREATED)
|
||||
return JsonResponse(payment_mode_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class PaymentModeDetailView(APIView):
|
||||
def get(self, request, id):
|
||||
try:
|
||||
payment_mode = PaymentMode.objects.get(id=id)
|
||||
payment_mode_serializer = PaymentModeSerializer(payment_mode)
|
||||
return JsonResponse(payment_mode_serializer.data, safe=False)
|
||||
except PaymentMode.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def put(self, request, id):
|
||||
payment_mode_data = JSONParser().parse(request)
|
||||
try:
|
||||
payment_mode = PaymentMode.objects.get(id=id)
|
||||
except PaymentMode.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
payment_mode_serializer = PaymentModeSerializer(payment_mode, data=payment_mode_data, partial=True)
|
||||
if payment_mode_serializer.is_valid():
|
||||
payment_mode_serializer.save()
|
||||
return JsonResponse(payment_mode_serializer.data, safe=False)
|
||||
return JsonResponse(payment_mode_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, id):
|
||||
return delete_object(PaymentMode, id)
|
||||
1
Back-End/Subscriptions/__init__.py
Normal file
1
Back-End/Subscriptions/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
default_app_config = 'Subscriptions.apps.GestionInscriptionsConfig'
|
||||
@ -2,8 +2,8 @@ from django.contrib import admin
|
||||
|
||||
from .models import *
|
||||
|
||||
admin.site.register(Eleve)
|
||||
admin.site.register(Responsable)
|
||||
admin.site.register(Student)
|
||||
admin.site.register(Guardian)
|
||||
|
||||
class EleveAdmin(admin.ModelAdmin):
|
||||
def save_model(self, request, obj, form, change):
|
||||
@ -3,8 +3,5 @@ from django.conf import settings
|
||||
|
||||
class GestioninscriptionsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'GestionInscriptions'
|
||||
name = 'Subscriptions'
|
||||
|
||||
def ready(self):
|
||||
from GestionInscriptions.signals import clear_cache
|
||||
clear_cache()
|
||||
43
Back-End/Subscriptions/automate.py
Normal file
43
Back-End/Subscriptions/automate.py
Normal file
@ -0,0 +1,43 @@
|
||||
# state_machine.py
|
||||
import json
|
||||
from Subscriptions.models import RegistrationForm
|
||||
|
||||
state_mapping = {
|
||||
"ABSENT": RegistrationForm.RegistrationFormStatus.RF_ABSENT,
|
||||
"CREE": RegistrationForm.RegistrationFormStatus.RF_CREATED,
|
||||
"ENVOYE": RegistrationForm.RegistrationFormStatus.RF_SENT,
|
||||
"EN_VALIDATION": RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW,
|
||||
"A_RELANCER": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP,
|
||||
"VALIDE": RegistrationForm.RegistrationFormStatus.RF_VALIDATED,
|
||||
"ARCHIVE": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED
|
||||
}
|
||||
|
||||
def load_config(config_file):
|
||||
with open(config_file, 'r') as file:
|
||||
config = json.load(file)
|
||||
return config
|
||||
|
||||
def getStateMachineObject(etat) :
|
||||
return Automate_RF_Register(etat)
|
||||
|
||||
def getStateMachineObjectState(etat):
|
||||
return Automate_RF_Register(etat).state
|
||||
|
||||
def updateStateMachine(rf, transition) :
|
||||
automateModel = load_config('Subscriptions/Configuration/automate.json')
|
||||
state_machine = getStateMachineObject(rf.status)
|
||||
print(f'etat DI : {state_machine.state}')
|
||||
if state_machine.trigger(transition, automateModel):
|
||||
rf.status = state_machine.state
|
||||
rf.save()
|
||||
|
||||
class Automate_RF_Register:
|
||||
def __init__(self, initial_state):
|
||||
self.state = initial_state
|
||||
|
||||
def trigger(self, transition_name, config):
|
||||
for transition in config["transitions"]:
|
||||
if transition["name"] == transition_name and self.state == state_mapping[transition["from"]]:
|
||||
self.state = state_mapping[transition["to"]]
|
||||
return True
|
||||
return False
|
||||
86
Back-End/Subscriptions/mailManager.py
Normal file
86
Back-End/Subscriptions/mailManager.py
Normal file
@ -0,0 +1,86 @@
|
||||
from django.core.mail import send_mail, EmailMultiAlternatives, EmailMessage
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import strip_tags
|
||||
import re
|
||||
from N3wtSchool import settings
|
||||
|
||||
def envoieReinitMotDePasse(recipients, code):
|
||||
errorMessage = ''
|
||||
try:
|
||||
EMAIL_REINIT_SUBJECT = 'Réinitialisation du mot de passe'
|
||||
context = {
|
||||
'BASE_URL': settings.BASE_URL,
|
||||
'code': str(code)
|
||||
}
|
||||
subject = EMAIL_REINIT_SUBJECT
|
||||
html_message = render_to_string('emails/resetPassword.html', context)
|
||||
plain_message = strip_tags(html_message)
|
||||
from_email = settings.EMAIL_HOST_USER
|
||||
send_mail(subject, plain_message, from_email, [recipients], html_message=html_message)
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
|
||||
return errorMessage
|
||||
|
||||
|
||||
def sendRegisterForm(recipients, establishment_id):
|
||||
errorMessage = ''
|
||||
try:
|
||||
print(f'{settings.EMAIL_HOST_USER}')
|
||||
# Préparation du contexte pour le template
|
||||
EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Dossier Inscription'
|
||||
context = {
|
||||
'BASE_URL': settings.BASE_URL,
|
||||
'email': recipients,
|
||||
'establishment': establishment_id
|
||||
}
|
||||
|
||||
subject = EMAIL_INSCRIPTION_SUBJECT
|
||||
html_message = render_to_string('emails/inscription.html', context)
|
||||
plain_message = strip_tags(html_message)
|
||||
from_email = settings.EMAIL_HOST_USER
|
||||
|
||||
send_mail(subject, plain_message, from_email, [recipients], html_message=html_message)
|
||||
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
|
||||
return errorMessage
|
||||
|
||||
def envoieRelanceDossierInscription(recipients, code):
|
||||
EMAIL_RELANCE_SUBJECT = '[N3WT-SCHOOL] Relance - Dossier Inscription'
|
||||
EMAIL_RELANCE_CORPUS = 'Bonjour,\nN\'ayant pas eu de retour de votre part, nous vous renvoyons le lien vers le formulaire d\'inscription : ' + BASE_URL + '/users/login\nCordialement'
|
||||
errorMessage = ''
|
||||
try:
|
||||
send_mail(
|
||||
EMAIL_RELANCE_SUBJECT,
|
||||
EMAIL_RELANCE_CORPUS%str(code),
|
||||
settings.EMAIL_HOST_USER,
|
||||
[recipients],
|
||||
fail_silently=False,
|
||||
)
|
||||
except Exception as e:
|
||||
errorMessage = str(e)
|
||||
|
||||
return errorMessage
|
||||
|
||||
def isValid(message, fiche_inscription):
|
||||
# Est-ce que la référence du dossier est VALIDE
|
||||
subject = message.subject
|
||||
print ("++++ " + subject)
|
||||
responsableMail = message.from_header
|
||||
result = re.search('<(.*)>', responsableMail)
|
||||
|
||||
if result:
|
||||
responsableMail = result.group(1)
|
||||
|
||||
result = re.search(r'.*\[Ref(.*)\].*', subject)
|
||||
idMail = -1
|
||||
if result:
|
||||
idMail = result.group(1).strip()
|
||||
|
||||
eleve = fiche_inscription.eleve
|
||||
responsable = eleve.getMainGuardian()
|
||||
mailReponsableAVerifier = responsable.mail
|
||||
|
||||
return responsableMail == mailReponsableAVerifier and str(idMail) == str(fiche_inscription.eleve.id)
|
||||
247
Back-End/Subscriptions/models.py
Normal file
247
Back-End/Subscriptions/models.py
Normal file
@ -0,0 +1,247 @@
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
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
|
||||
|
||||
class Language(models.Model):
|
||||
"""
|
||||
Représente une langue parlée par l’élève.
|
||||
"""
|
||||
id = models.AutoField(primary_key=True)
|
||||
label = models.CharField(max_length=200, default="")
|
||||
|
||||
def __str__(self):
|
||||
return "LANGUAGE"
|
||||
|
||||
class Guardian(models.Model):
|
||||
"""
|
||||
Représente un responsable légal (parent/tuteur) d’un élève.
|
||||
"""
|
||||
last_name = models.CharField(max_length=200, default="")
|
||||
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)
|
||||
phone = models.CharField(max_length=200, default="", blank=True)
|
||||
profession = models.CharField(max_length=200, default="", blank=True)
|
||||
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
|
||||
|
||||
class Sibling(models.Model):
|
||||
"""
|
||||
Représente un frère ou une sœur d’un élève.
|
||||
"""
|
||||
id = models.AutoField(primary_key=True)
|
||||
last_name = models.CharField(max_length=200, default="")
|
||||
first_name = models.CharField(max_length=200, default="")
|
||||
birth_date = models.CharField(max_length=200, default="", blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return "SIBLING"
|
||||
|
||||
class Student(models.Model):
|
||||
"""
|
||||
Représente l’élève inscrit ou en cours d’inscription.
|
||||
"""
|
||||
class StudentGender(models.IntegerChoices):
|
||||
NONE = 0, _('Sélection du genre')
|
||||
MALE = 1, _('Garçon')
|
||||
FEMALE = 2, _('Fille')
|
||||
|
||||
class StudentLevel(models.IntegerChoices):
|
||||
NONE = 0, _('Sélection du niveau')
|
||||
TPS = 1, _('TPS - Très Petite Section')
|
||||
PS = 2, _('PS - Petite Section')
|
||||
MS = 3, _('MS - Moyenne Section')
|
||||
GS = 4, _('GS - Grande Section')
|
||||
|
||||
class PaymentMethod(models.IntegerChoices):
|
||||
NONE = 0, _('Sélection du mode de paiement')
|
||||
SEPA_DIRECT_DEBIT = 1, _('Prélèvement SEPA')
|
||||
CHECK = 2, _('Chèques')
|
||||
|
||||
last_name = models.CharField(max_length=200, default="")
|
||||
first_name = models.CharField(max_length=200, default="")
|
||||
gender = models.IntegerField(choices=StudentGender, default=StudentGender.NONE, blank=True)
|
||||
level = models.IntegerField(choices=StudentLevel, default=StudentLevel.NONE, blank=True)
|
||||
nationality = models.CharField(max_length=200, default="", blank=True)
|
||||
address = models.CharField(max_length=200, default="", blank=True)
|
||||
birth_date = models.DateField(null=True, blank=True)
|
||||
birth_place = models.CharField(max_length=200, default="", blank=True)
|
||||
birth_postal_code = models.IntegerField(default=0, blank=True)
|
||||
attending_physician = models.CharField(max_length=200, default="", blank=True)
|
||||
payment_method = models.IntegerField(choices=PaymentMethod, default=PaymentMethod.NONE, blank=True)
|
||||
|
||||
# Many-to-Many Relationship
|
||||
profiles = models.ManyToManyField('Auth.Profile', blank=True)
|
||||
|
||||
# Many-to-Many Relationship
|
||||
guardians = models.ManyToManyField(Guardian, blank=True)
|
||||
|
||||
# Many-to-Many Relationship
|
||||
siblings = models.ManyToManyField(Sibling, blank=True)
|
||||
|
||||
# Many-to-Many Relationship
|
||||
registration_files = models.ManyToManyField('RegistrationTemplate', blank=True, related_name='students')
|
||||
|
||||
# Many-to-Many Relationship
|
||||
spoken_languages = models.ManyToManyField(Language, blank=True)
|
||||
|
||||
# One-to-Many Relationship
|
||||
associated_class = models.ForeignKey(SchoolClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='students')
|
||||
|
||||
def __str__(self):
|
||||
return self.last_name + "_" + self.first_name
|
||||
|
||||
def getSpokenLanguages(self):
|
||||
"""
|
||||
Retourne la liste des langues parlées par l’élève.
|
||||
"""
|
||||
return self.spoken_languages.all()
|
||||
|
||||
def getMainGuardian(self):
|
||||
"""
|
||||
Retourne le responsable légal principal de l’élève.
|
||||
"""
|
||||
return self.guardians.all()[0]
|
||||
|
||||
def getGuardians(self):
|
||||
"""
|
||||
Retourne tous les responsables légaux de l’élève.
|
||||
"""
|
||||
return self.guardians.all()
|
||||
|
||||
def getProfiles(self):
|
||||
"""
|
||||
Retourne les profils utilisateurs liés à l’élève.
|
||||
"""
|
||||
return self.profiles.all()
|
||||
|
||||
def getSiblings(self):
|
||||
"""
|
||||
Retourne les frères et sœurs de l’élève.
|
||||
"""
|
||||
return self.siblings.all()
|
||||
|
||||
def getNumberOfSiblings(self):
|
||||
"""
|
||||
Retourne le nombre de frères et sœurs.
|
||||
"""
|
||||
return self.siblings.count()
|
||||
|
||||
@property
|
||||
def age(self):
|
||||
if self.birth_date:
|
||||
today = datetime.today()
|
||||
years = today.year - self.birth_date.year
|
||||
months = today.month - self.birth_date.month
|
||||
if today.day < self.birth_date.day:
|
||||
months -= 1
|
||||
if months < 0:
|
||||
years -= 1
|
||||
months += 12
|
||||
|
||||
# Determine the age format
|
||||
if 6 <= months <= 12:
|
||||
return f"{years} years 1/2"
|
||||
else:
|
||||
return f"{years} years"
|
||||
return None
|
||||
|
||||
@property
|
||||
def formatted_birth_date(self):
|
||||
if self.birth_date:
|
||||
return self.birth_date.strftime('%d-%m-%Y')
|
||||
return None
|
||||
|
||||
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
|
||||
|
||||
def registration_file_path(instance, filename):
|
||||
# Génère le chemin : registration_files/dossier_rf_{student_id}/filename
|
||||
return f'registration_files/dossier_rf_{instance.student_id}/{filename}'
|
||||
|
||||
class RegistrationTemplateMaster(models.Model):
|
||||
groups = models.ManyToManyField(RegistrationFileGroup, related_name='template_masters', blank=True)
|
||||
id = models.IntegerField(primary_key=True)
|
||||
name = models.CharField(max_length=255, default="")
|
||||
is_required = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.group.name} - {self.id}'
|
||||
|
||||
class RegistrationForm(models.Model):
|
||||
class RegistrationFormStatus(models.IntegerChoices):
|
||||
RF_ABSENT = 0, _('Pas de dossier d\'inscription')
|
||||
RF_CREATED = 1, _('Dossier d\'inscription créé')
|
||||
RF_SENT = 2, _('Dossier d\'inscription envoyé')
|
||||
RF_UNDER_REVIEW = 3, _('Dossier d\'inscription en cours de validation')
|
||||
RF_TO_BE_FOLLOWED_UP = 4, _('Dossier d\'inscription à relancer')
|
||||
RF_VALIDATED = 5, _('Dossier d\'inscription validé')
|
||||
RF_ARCHIVED = 6, _('Dossier d\'inscription archivé')
|
||||
|
||||
# One-to-One Relationship
|
||||
student = models.OneToOneField(Student, on_delete=models.CASCADE, primary_key=True)
|
||||
status = models.IntegerField(choices=RegistrationFormStatus, default=RegistrationFormStatus.RF_ABSENT)
|
||||
last_update = models.DateTimeField(auto_now=True)
|
||||
notes = models.CharField(max_length=200, blank=True)
|
||||
registration_link_code = models.CharField(max_length=200, default="", blank=True)
|
||||
registration_file = models.FileField(
|
||||
upload_to=registration_file_path,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
associated_rf = models.CharField(max_length=200, default="", blank=True)
|
||||
|
||||
# Many-to-Many Relationship
|
||||
fees = models.ManyToManyField(Fee, blank=True, related_name='register_forms')
|
||||
|
||||
# Many-to-Many Relationship
|
||||
discounts = models.ManyToManyField(Discount, blank=True, related_name='register_forms')
|
||||
fileGroup = models.ForeignKey(RegistrationFileGroup,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='register_forms',
|
||||
null=True,
|
||||
blank=True)
|
||||
|
||||
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
|
||||
|
||||
def registration_file_upload_to(instance, filename):
|
||||
return f"registration_files/dossier_rf_{instance.registration_form.pk}/{filename}"
|
||||
|
||||
class RegistrationTemplate(models.Model):
|
||||
master = models.ForeignKey(RegistrationTemplateMaster, on_delete=models.CASCADE, related_name='templates', blank=True)
|
||||
id = models.IntegerField(primary_key=True)
|
||||
slug = models.CharField(max_length=255, default="")
|
||||
name = models.CharField(max_length=255, default="")
|
||||
registration_form = models.ForeignKey(RegistrationForm, on_delete=models.CASCADE, related_name='templates', blank=True)
|
||||
file = models.FileField(null=True,blank=True, upload_to=registration_file_upload_to)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@staticmethod
|
||||
def get_files_from_rf(register_form_id):
|
||||
"""
|
||||
Récupère tous les fichiers liés à un dossier d’inscription donné.
|
||||
"""
|
||||
registration_files = RegistrationTemplate.objects.filter(registration_form=register_form_id)
|
||||
filenames = []
|
||||
for reg_file in registration_files:
|
||||
filenames.append(reg_file.file.path)
|
||||
return filenames
|
||||
@ -16,5 +16,5 @@ class CustomPagination(PageNumberPagination):
|
||||
'count': self.page.paginator.count,
|
||||
'page_size': self.page_size,
|
||||
'max_page_size' : self.max_page_size,
|
||||
'fichesInscriptions': data }
|
||||
'registerForms': data }
|
||||
)
|
||||
314
Back-End/Subscriptions/serializers.py
Normal file
314
Back-End/Subscriptions/serializers.py
Normal file
@ -0,0 +1,314 @@
|
||||
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 ProfileRole, Profile
|
||||
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
|
||||
from GestionMessagerie.models import Messagerie
|
||||
from GestionNotification.models import Notification
|
||||
from N3wtSchool import settings
|
||||
from django.utils import timezone
|
||||
import pytz
|
||||
from datetime import datetime
|
||||
|
||||
class RegistrationTemplateMasterSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = RegistrationTemplateMaster
|
||||
fields = '__all__'
|
||||
|
||||
class RegistrationTemplateSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = RegistrationTemplate
|
||||
fields = '__all__'
|
||||
|
||||
class GuardianSimpleSerializer(serializers.ModelSerializer):
|
||||
associated_profile_email = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Guardian
|
||||
fields = ['id', 'associated_profile_email']
|
||||
|
||||
def get_associated_profile_email(self, obj):
|
||||
if obj.profile_role and obj.profile_role.profile:
|
||||
return obj.profile_role.profile.email
|
||||
return None
|
||||
|
||||
class RegistrationFormSimpleSerializer(serializers.ModelSerializer):
|
||||
guardians = GuardianSimpleSerializer(many=True, source='student.guardians')
|
||||
last_name = serializers.SerializerMethodField()
|
||||
first_name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = RegistrationForm
|
||||
fields = ['student_id', 'last_name', 'first_name', 'guardians']
|
||||
|
||||
def get_last_name(self, obj):
|
||||
return obj.student.last_name
|
||||
|
||||
def get_first_name(self, obj):
|
||||
return obj.student.first_name
|
||||
|
||||
class RegistrationFileGroupSerializer(serializers.ModelSerializer):
|
||||
registration_forms = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = RegistrationFileGroup
|
||||
fields = '__all__'
|
||||
|
||||
def get_registration_forms(self, obj):
|
||||
forms = RegistrationForm.objects.filter(fileGroup=obj)
|
||||
return RegistrationFormSimpleSerializer(forms, many=True).data
|
||||
|
||||
class LanguageSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = Language
|
||||
fields = '__all__'
|
||||
|
||||
class SiblingSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = Sibling
|
||||
fields = '__all__'
|
||||
|
||||
class GuardianSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=False)
|
||||
profile_role_data = ProfileRoleSerializer(write_only=True, required=False)
|
||||
associated_profile_email = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Guardian
|
||||
fields = '__all__'
|
||||
|
||||
def get_associated_profile_email(self, obj):
|
||||
if obj.profile_role and obj.profile_role.profile:
|
||||
return obj.profile_role.profile.email
|
||||
return None
|
||||
|
||||
class StudentSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
guardians = GuardianSerializer(many=True, required=False)
|
||||
siblings = SiblingSerializer(many=True, required=False)
|
||||
spoken_languages = LanguageSerializer(many=True, required=False)
|
||||
associated_class_id = serializers.PrimaryKeyRelatedField(queryset=SchoolClass.objects.all(), source='associated_class', required=False, write_only=False, read_only=False)
|
||||
age = serializers.SerializerMethodField()
|
||||
formatted_birth_date = serializers.SerializerMethodField()
|
||||
birth_date = serializers.DateField(input_formats=['%d-%m-%Y', '%Y-%m-%d'], required=False, allow_null=True)
|
||||
associated_class_name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Student
|
||||
fields = '__all__'
|
||||
|
||||
def create_or_update_guardians(self, guardians_data):
|
||||
guardians_ids = []
|
||||
for guardian_data in guardians_data:
|
||||
profile_role_data = guardian_data.pop('profile_role_data', None)
|
||||
profile_role = guardian_data.pop('profile_role', None)
|
||||
|
||||
if profile_role_data:
|
||||
# Vérifiez si 'profile' est un objet ou une clé primaire
|
||||
if isinstance(profile_role_data.get('profile'), Profile):
|
||||
profile_role_data['profile'] = profile_role_data['profile'].id
|
||||
establishment_id = profile_role_data.pop('establishment').id
|
||||
profile_role_data['establishment'] = establishment_id
|
||||
|
||||
profile_role_serializer = ProfileRoleSerializer(data=profile_role_data)
|
||||
profile_role_serializer.is_valid(raise_exception=True)
|
||||
profile_role = profile_role_serializer.save()
|
||||
elif profile_role:
|
||||
profile_role = ProfileRole.objects.get(id=profile_role.id)
|
||||
|
||||
if profile_role:
|
||||
guardian_data['profile_role'] = profile_role
|
||||
|
||||
guardian_instance, created = Guardian.objects.update_or_create(
|
||||
id=guardian_data.get('id'),
|
||||
defaults=guardian_data
|
||||
)
|
||||
guardians_ids.append(guardian_instance.id)
|
||||
return guardians_ids
|
||||
|
||||
def create_or_update_siblings(self, siblings_data):
|
||||
siblings_ids = []
|
||||
for sibling_data in siblings_data:
|
||||
sibling_instance, created = Sibling.objects.update_or_create(
|
||||
id=sibling_data.get('id'),
|
||||
defaults=sibling_data
|
||||
)
|
||||
siblings_ids.append(sibling_instance.id)
|
||||
return siblings_ids
|
||||
|
||||
def create_or_update_languages(self, languages_data):
|
||||
languages_ids = []
|
||||
for language_data in languages_data:
|
||||
language_instance, created = Language.objects.update_or_create(
|
||||
id=language_data.get('id'),
|
||||
defaults=language_data
|
||||
)
|
||||
languages_ids.append(language_instance.id)
|
||||
return languages_ids
|
||||
|
||||
def create(self, validated_data):
|
||||
guardians_data = validated_data.pop('guardians', [])
|
||||
siblings_data = validated_data.pop('siblings', [])
|
||||
languages_data = validated_data.pop('spoken_languages', [])
|
||||
student = Student.objects.create(**validated_data)
|
||||
student.guardians.set(self.create_or_update_guardians(guardians_data))
|
||||
student.siblings.set(self.create_or_update_siblings(siblings_data))
|
||||
student.spoken_languages.set(self.create_or_update_languages(languages_data))
|
||||
|
||||
return student
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
guardians_data = validated_data.pop('guardians', [])
|
||||
siblings_data = validated_data.pop('siblings', [])
|
||||
languages_data = validated_data.pop('spoken_languages', [])
|
||||
if guardians_data:
|
||||
instance.guardians.set(self.create_or_update_guardians(guardians_data))
|
||||
if siblings_data:
|
||||
instance.siblings.set(self.create_or_update_siblings(siblings_data))
|
||||
if languages_data:
|
||||
instance.spoken_languages.set(self.create_or_update_languages(languages_data))
|
||||
|
||||
for field in self.fields:
|
||||
try:
|
||||
setattr(instance, field, validated_data[field])
|
||||
except KeyError:
|
||||
pass
|
||||
instance.save()
|
||||
|
||||
return instance
|
||||
|
||||
def get_age(self, obj):
|
||||
return obj.age
|
||||
|
||||
def get_formatted_birth_date(self, obj):
|
||||
return obj.formatted_birth_date
|
||||
|
||||
def get_associated_class_name(self, obj):
|
||||
return obj.associated_class.atmosphereName if obj.associated_class else None
|
||||
|
||||
class RegistrationFormSerializer(serializers.ModelSerializer):
|
||||
student = StudentSerializer(many=False, required=False)
|
||||
registration_file = serializers.FileField(required=False)
|
||||
status_label = serializers.SerializerMethodField()
|
||||
formatted_last_update = serializers.SerializerMethodField()
|
||||
registration_files = RegistrationTemplateSerializer(many=True, required=False)
|
||||
fees = serializers.PrimaryKeyRelatedField(queryset=Fee.objects.all(), many=True, required=False)
|
||||
discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True, required=False)
|
||||
totalRegistrationFees = serializers.SerializerMethodField()
|
||||
totalTuitionFees = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = RegistrationForm
|
||||
fields = '__all__'
|
||||
|
||||
def create(self, validated_data):
|
||||
student_data = validated_data.pop('student')
|
||||
student = StudentSerializer.create(StudentSerializer(), student_data)
|
||||
fees_data = validated_data.pop('fees', [])
|
||||
discounts_data = validated_data.pop('discounts', [])
|
||||
registrationForm = RegistrationForm.objects.create(student=student, **validated_data)
|
||||
|
||||
# Associer les IDs des objets Fee et Discount au RegistrationForm
|
||||
registrationForm.fees.set([fee.id for fee in fees_data])
|
||||
registrationForm.discounts.set([discount.id for discount in discounts_data])
|
||||
return registrationForm
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
student_data = validated_data.pop('student', None)
|
||||
fees_data = validated_data.pop('fees', [])
|
||||
discounts_data = validated_data.pop('discounts', [])
|
||||
if student_data:
|
||||
student = instance.student
|
||||
StudentSerializer.update(StudentSerializer(), student, student_data)
|
||||
|
||||
for field in self.fields:
|
||||
try:
|
||||
setattr(instance, field, validated_data[field])
|
||||
except KeyError:
|
||||
pass
|
||||
instance.save()
|
||||
|
||||
# Associer les IDs des objets Fee et Discount au RegistrationForm
|
||||
instance.fees.set([fee.id for fee in fees_data])
|
||||
instance.discounts.set([discount.id for discount in discounts_data])
|
||||
|
||||
return instance
|
||||
|
||||
def get_status_label(self, obj):
|
||||
return obj.get_status_display()
|
||||
|
||||
def get_formatted_last_update(self, obj):
|
||||
utc_time = timezone.localtime(obj.last_update) # 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_totalRegistrationFees(self, obj):
|
||||
return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.REGISTRATION_FEE))
|
||||
|
||||
def get_totalTuitionFees(self, obj):
|
||||
return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.TUITION_FEE))
|
||||
|
||||
class StudentByParentSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Student
|
||||
fields = ['id', 'last_name', 'first_name']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(StudentByParentSerializer, self).__init__(*args, **kwargs)
|
||||
for field in self.fields:
|
||||
self.fields[field].required = False
|
||||
|
||||
class RegistrationFormByParentSerializer(serializers.ModelSerializer):
|
||||
student = StudentByParentSerializer(many=False, required=True)
|
||||
|
||||
class Meta:
|
||||
model = RegistrationForm
|
||||
fields = ['student', 'status']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RegistrationFormByParentSerializer, self).__init__(*args, **kwargs)
|
||||
for field in self.fields:
|
||||
self.fields[field].required = False
|
||||
|
||||
class GuardianByDICreationSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
associated_profile_email = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Guardian
|
||||
fields = ['id', 'last_name', 'first_name', 'associated_profile_email']
|
||||
|
||||
def get_associated_profile_email(self, obj):
|
||||
if obj.profile_role and obj.profile_role.profile:
|
||||
return obj.profile_role.profile.email
|
||||
return None
|
||||
|
||||
class StudentByRFCreationSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
guardians = GuardianByDICreationSerializer(many=True, required=False)
|
||||
|
||||
class Meta:
|
||||
model = Student
|
||||
fields = ['id', 'last_name', 'first_name', 'guardians']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(StudentByRFCreationSerializer, self).__init__(*args, **kwargs)
|
||||
for field in self.fields:
|
||||
self.fields[field].required = False
|
||||
|
||||
class NotificationSerializer(serializers.ModelSerializer):
|
||||
notification_type_label = serializers.ReadOnlyField()
|
||||
|
||||
class Meta:
|
||||
model = Notification
|
||||
fields = '__all__'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user