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,MultiPartParser, FormParser from rest_framework.response import Response from rest_framework.views import APIView from rest_framework import status from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi import json from pathlib import Path import os from io import BytesIO import Subscriptions.mailManager as mailer import Subscriptions.util as util from Subscriptions.automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine from .serializers import RegistrationFormSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFileSerializer, RegistrationFileTemplateSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFeeSerializer from .pagination import CustomPagination from .signals import clear_cache from .models import Student, Guardian, RegistrationForm, RegistrationFee, RegistrationFileTemplate, RegistrationFile from .automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine from Auth.models import Profile from N3wtSchool import settings, renderers, bdd class RegisterFormListView(APIView): """ Gère la liste des dossiers d’inscription, lecture et création. """ pagination_class = CustomPagination def get_register_form(self, _filter, search=None): """ Récupère les fiches d'inscriptions en fonction du filtre passé. _filter: Filtre pour déterminer l'état des fiches ('pending', 'archived', 'subscribed') search: Terme de recherche (optionnel) """ if _filter == 'pending': exclude_states = [RegistrationForm.RegistrationFormStatus.RF_VALIDATED, RegistrationForm.RegistrationFormStatus.RF_ARCHIVED] return bdd.searchObjects(RegistrationForm, search, _excludeStates=exclude_states) elif _filter == 'archived': return bdd.getObjects(RegistrationForm, 'status', RegistrationForm.RegistrationFormStatus.RF_ARCHIVED) elif _filter == 'subscribed': return bdd.getObjects(RegistrationForm, 'status', RegistrationForm.RegistrationFormStatus.RF_VALIDATED) return None @swagger_auto_schema( manual_parameters=[ openapi.Parameter('_filter', openapi.IN_PATH, description="filtre", type=openapi.TYPE_STRING, enum=['pending', 'archived', 'subscribed'], required=True), openapi.Parameter('search', openapi.IN_QUERY, description="search", type=openapi.TYPE_STRING, required=False), openapi.Parameter('page_size', openapi.IN_QUERY, description="limite de page lors de la pagination", type=openapi.TYPE_INTEGER, required=False), ], responses={200: RegistrationFormSerializer(many=True)} ) def get(self, request, _filter): """ Récupère les fiches d'inscriptions en fonction du filtre passé. """ # 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 # Définir le cache_key en fonction du filtre page_number = request.GET.get('page', 1) cache_key = f'N3WT_ficheInscriptions_{_filter}_page_{page_number}_search_{search if _filter == "pending" else ""}' cached_page = cache.get(cache_key) if cached_page: return JsonResponse(cached_page, safe=False) # Récupérer les fiches d'inscriptions en fonction du filtre registerForms_List = self.get_register_form(_filter, search) if not registerForms_List: return JsonResponse({'error' : 'aucune donnée trouvée', 'count' :0}, safe=False) # Pagination paginator = self.pagination_class() page = paginator.paginate_queryset(registerForms_List, request) if page is not None: registerForms_serializer = RegistrationFormSerializer(page, many=True) response_data = paginator.get_paginated_response(registerForms_serializer.data) cache.set(cache_key, response_data, timeout=60*15) return JsonResponse(response_data, safe=False) return JsonResponse({'error' : 'aucune donnée trouvée', 'count' :0}, safe=False) @swagger_auto_schema( manual_parameters=[ ], responses={200: RegistrationFormSerializer(many=True)} ) def post(self, request): studentFormList_serializer=JSONParser().parse(request) for studentForm_data in studentFormList_serializer: # Ajout de la date de mise à jour studentForm_data["last_update"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M') json.dumps(studentForm_data) # Ajout du code d'inscription code = util.genereRandomCode(12) studentForm_data["codeLienInscription"] = code studentForm_serializer = RegistrationFormSerializer(data=studentForm_data) if studentForm_serializer.is_valid(): studentForm_serializer.save() return JsonResponse(studentForm_serializer.errors, safe=False) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') class RegisterFormView(APIView): """ Gère la lecture, création, modification et suppression d’un dossier d’inscription. """ pagination_class = CustomPagination def get(self, request, _id): """ Récupère un dossier d'inscription donné. """ registerForm=bdd.getObject(RegistrationForm, "student__id", _id) registerForm_serializer=RegistrationFormSerializer(registerForm) return JsonResponse(registerForm_serializer.data, safe=False) def post(self, request): """ Crée un dossier d'inscription. """ studentForm_data=JSONParser().parse(request) # Ajout de la date de mise à jour studentForm_data["last_update"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M') json.dumps(studentForm_data) # Ajout du code d'inscription code = util.genereRandomCode(12) studentForm_data["codeLienInscription"] = code guardiansId = studentForm_data.pop('idGuardians', []) studentForm_serializer = RegistrationFormSerializer(data=studentForm_data) if studentForm_serializer.is_valid(): di = studentForm_serializer.save() # Mise à jour de l'automate updateStateMachine(di, 'creationDI') # Récupération du reponsable associé for guardianId in guardiansId: guardian = Guardian.objects.get(id=guardianId) di.student.guardians.add(guardian) di.save() return JsonResponse(studentForm_serializer.data, safe=False) return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) def put(self, request, _id): """ Modifie un dossier d'inscription donné. """ studentForm_data=JSONParser().parse(request) _status = studentForm_data.pop('status', 0) studentForm_data["last_update"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M')) registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id) if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW: try: # Génération de la fiche d'inscription au format PDF base_dir = f"registration_files/dossier_rf_{registerForm.pk}" os.makedirs(base_dir, exist_ok=True) # Fichier PDF initial initial_pdf = f"{base_dir}/rf_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf" registerForm.registration_file = util.rfToPDF(registerForm, initial_pdf) registerForm.save() # Récupération des fichiers d'inscription fileNames = RegistrationFile.get_files_from_rf(registerForm.pk) if registerForm.registration_file: fileNames.insert(0, registerForm.registration_file.path) # Création du fichier PDF Fusionné merged_pdf = f"{base_dir}/dossier_complet_{registerForm.pk}.pdf" util.merge_files_pdf(fileNames, merged_pdf) # Mise à jour du champ registration_file avec le fichier fusionné with open(merged_pdf, 'rb') as f: registerForm.registration_file.save( os.path.basename(merged_pdf), File(f), save=True ) # Mise à jour de l'automate updateStateMachine(registerForm, 'saisiDI') except Exception as e: return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED: # L'école a validé le dossier d'inscription # Mise à jour de l'automate updateStateMachine(registerForm, 'valideDI') studentForm_serializer = RegistrationFormSerializer(registerForm, data=studentForm_data) if studentForm_serializer.is_valid(): studentForm_serializer.save() return JsonResponse(studentForm_serializer.data, safe=False) return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, id): """ Supprime un dossier d'inscription donné. """ register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) if register_form != None: student = register_form.student student.guardians.clear() student.profiles.clear() student.registration_files.clear() student.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, status=status.HTTP_404_NOT_FOUND) class StudentView(APIView): """ Gère la lecture d’un élève donné. """ def get(self, request, _id): student = bdd.getObject(_objectName=Student, _columnName='id', _value=_id) if student is None: return JsonResponse({"errorMessage":'Aucun élève trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) student_serializer = StudentSerializer(student) return JsonResponse(student_serializer.data, safe=False) class GuardianView(APIView): """ Récupère le dernier ID de responsable légal créé. """ def get(self, request): lastGuardian = bdd.getLastId(Guardian) return JsonResponse({"lastid":lastGuardian}, safe=False) def send(request, _id): """ Envoie le dossier d’inscription par e-mail. """ register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id) if register_form != None: student = register_form.student guardian = student.getMainGuardian() email = guardian.email errorMessage = mailer.sendRegisterForm(email) if errorMessage == '': register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') # Mise à jour de l'automate updateStateMachine(register_form, 'envoiDI') return JsonResponse({"message": f"Le dossier d'inscription a bien été envoyé à l'addresse {email}"}, safe=False) return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST) return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND) def archive(request, _id): """ Archive le dossier d’inscription visé. """ register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id) if register_form != None: register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') # Mise à jour de l'automate updateStateMachine(register_form, 'archiveDI') return JsonResponse({"errorMessage":''}, safe=False, status=status.HTTP_400_BAD_REQUEST) return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND) def relance(request, _id): """ Relance un dossier d’inscription par e-mail. """ register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id) if register_form != None: student = register_form.student guardian = student.getMainGuardian() email = guardian.email errorMessage = mailer.envoieRelanceDossierInscription(email, register_form.codeLienInscription) if errorMessage == '': register_form.status=RegistrationForm.RegistrationFormStatus.RF_SENT register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') register_form.save() return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST) return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND) # API utilisée pour la vue parent class ChildrenListView(APIView): """ Pour la vue parent : liste les élèves rattachés à un profil donné. """ # Récupération des élèves d'un parent # idProfile : identifiant du profil connecté rattaché aux fiches d'élèves def get(self, request, _id): students = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__associated_profile__id', _value=_id) students_serializer = RegistrationFormByParentSerializer(students, many=True) return JsonResponse(students_serializer.data, safe=False) # API utilisée pour la vue de création d'un DI class StudentListView(APIView): """ Pour la vue de création d’un dossier d’inscription : liste les élèves disponibles. """ # Récupération de la liste des élèves inscrits ou en cours d'inscriptions def get(self, request): students = bdd.getAllObjects(_objectName=Student) students_serializer = StudentByRFCreationSerializer(students, many=True) return JsonResponse(students_serializer.data, safe=False) # API utilisée pour la vue de personnalisation des frais d'inscription pour la structure class RegistrationFeeView(APIView): """ Liste les frais d’inscription. """ def get(self, request): tarifs = bdd.getAllObjects(RegistrationFee) tarifs_serializer = RegistrationFeeSerializer(tarifs, many=True) return JsonResponse(tarifs_serializer.data, safe=False) class RegistrationFileTemplateView(APIView): """ Gère les fichiers templates pour les dossiers d’inscription. """ parser_classes = (MultiPartParser, FormParser) def get(self, request, _id=None): """ Récupère les fichiers templates pour les dossiers d’inscription. """ if _id is None: files = RegistrationFileTemplate.objects.all() serializer = RegistrationFileTemplateSerializer(files, many=True) return Response(serializer.data) else : registationFileTemplate = bdd.getObject(_objectName=RegistrationFileTemplate, _columnName='id', _value=_id) if registationFileTemplate is None: return JsonResponse({"errorMessage":'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) serializer = RegistrationFileTemplateSerializer(registationFileTemplate) return JsonResponse(serializer.data, safe=False) def put(self, request, _id): """ Met à jour un fichier template existant. """ registationFileTemplate = bdd.getObject(_objectName=RegistrationFileTemplate, _columnName='id', _value=_id) if registationFileTemplate is None: return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) serializer = RegistrationFileTemplateSerializer(registationFileTemplate,data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def post(self, request): """ Crée un fichier template pour les dossiers d’inscription. """ serializer = RegistrationFileTemplateSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, _id): """ Supprime un fichier template existant. """ registrationFileTemplate = bdd.getObject(_objectName=RegistrationFileTemplate, _columnName='id', _value=_id) if registrationFileTemplate is not None: registrationFileTemplate.file.delete() # Supprimer le fichier uploadé registrationFileTemplate.delete() return JsonResponse({'message': 'La suppression du fichier d\'inscription a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT) else: return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) class RegistrationFileView(APIView): """ Gère la création, mise à jour et suppression de fichiers liés à un dossier d’inscription. """ parser_classes = (MultiPartParser, FormParser) def get(self, request, _id=None): """ Récupère les fichiers liés à un dossier d’inscription donné. """ if (_id is None): files = RegistrationFile.objects.all() serializer = RegistrationFileSerializer(files, many=True) return Response(serializer.data) else: registationFile = bdd.getObject(_objectName=RegistrationFile, _columnName='id', _value=_id) if registationFile is None: return JsonResponse({"errorMessage":'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) serializer = RegistrationFileSerializer(registationFile) return JsonResponse(serializer.data, safe=False) def post(self, request): """ Crée un RegistrationFile pour le RegistrationForm associé. """ serializer = RegistrationFileSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def put(self, request, fileId): """ Met à jour un RegistrationFile existant. """ registrationFile = bdd.getObject(_objectName=RegistrationFile, _columnName='id', _value=fileId) if registrationFile is None: return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) serializer = RegistrationFileSerializer(registrationFile, data=request.data) if serializer.is_valid(): serializer.save() return Response({'message': 'Fichier mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, _id): """ Supprime un RegistrationFile existant. """ registrationFile = bdd.getObject(_objectName=RegistrationFile, _columnName='id', _value=_id) if registrationFile is not None: registrationFile.file.delete() # Supprimer le fichier uploadé registrationFile.delete() return JsonResponse({'message': 'La suppression du fichier a été effectuée avec succès'}, safe=False) else: return JsonResponse({'erreur': 'Le fichier n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)