feat: Précablage du formulaire dynamique [N3WTS-17]

This commit is contained in:
Luc SORIGNET
2025-11-30 17:24:25 +01:00
parent 7486f6c5ce
commit dd00cba385
41 changed files with 2637 additions and 606 deletions

View File

@ -1,18 +0,0 @@
{
"activationMailRelance": "Oui",
"delaiRelance": "30",
"ambiances": [
"2-3 ans",
"3-6 ans",
"6-12 ans"
],
"genres": [
"Fille",
"Garçon"
],
"modesPaiement": [
"Chèque",
"Virement",
"Prélèvement SEPA"
]
}

View File

@ -0,0 +1,43 @@
"""
Management command pour tester la configuration email Django
"""
from django.core.management.base import BaseCommand
from django.core.mail import send_mail
from django.conf import settings
from N3wtSchool.mailManager import getConnection, sendMail
import logging
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = 'Test de la configuration email'
def add_arguments(self, parser):
parser.add_argument('--establishment-id', type=int, help='ID de l\'établissement pour test')
parser.add_argument('--email', type=str, default='test@example.com', help='Email de destination')
def handle(self, *args, **options):
self.stdout.write("=== Test de configuration email ===")
# Affichage de la configuration
self.stdout.write(f"EMAIL_HOST: {settings.EMAIL_HOST}")
self.stdout.write(f"EMAIL_PORT: {settings.EMAIL_PORT}")
self.stdout.write(f"EMAIL_HOST_USER: {settings.EMAIL_HOST_USER}")
self.stdout.write(f"EMAIL_HOST_PASSWORD: {settings.EMAIL_HOST_PASSWORD}")
self.stdout.write(f"EMAIL_USE_TLS: {settings.EMAIL_USE_TLS}")
self.stdout.write(f"EMAIL_USE_SSL: {settings.EMAIL_USE_SSL}")
self.stdout.write(f"EMAIL_BACKEND: {settings.EMAIL_BACKEND}")
# Test 1: Configuration par défaut Django
self.stdout.write("\n--- Test : Configuration EMAIL par défaut ---")
try:
result = send_mail(
'Test Django Email',
'Ceci est un test de la configuration email par défaut.',
settings.EMAIL_HOST_USER,
[options['email']],
fail_silently=False,
)
self.stdout.write(self.style.SUCCESS(f"✅ Email envoyé avec succès (résultat: {result})"))
except Exception as e:
self.stdout.write(self.style.ERROR(f"❌ Erreur: {e}"))

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.3 on 2025-05-28 11:14
# Generated by Django 5.1.3 on 2025-11-30 11:02
import Subscriptions.models
import django.db.models.deletion
@ -46,10 +46,11 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='RegistrationSchoolFileTemplate',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('slug', models.CharField(default='', max_length=255)),
('name', models.CharField(default='', max_length=255)),
('file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_school_file_upload_to)),
('formTemplateData', models.JSONField(blank=True, default=list, null=True)),
],
),
migrations.CreateModel(
@ -153,7 +154,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='', max_length=255)),
('description', models.CharField(blank=True, null=True)),
('description', models.CharField(blank=True, max_length=500, null=True)),
('is_required', models.BooleanField(default=False)),
('groups', models.ManyToManyField(blank=True, related_name='parent_file_masters', to='Subscriptions.registrationfilegroup')),
],
@ -161,9 +162,10 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='RegistrationSchoolFileMaster',
fields=[
('id', models.IntegerField(primary_key=True, serialize=False)),
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='', max_length=255)),
('is_required', models.BooleanField(default=False)),
('formMasterData', models.JSONField(blank=True, default=list, null=True)),
('groups', models.ManyToManyField(blank=True, related_name='school_file_masters', to='Subscriptions.registrationfilegroup')),
],
),

View File

@ -1,18 +0,0 @@
# Generated by Django 5.1.3 on 2025-05-30 07:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('Subscriptions', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='registrationparentfilemaster',
name='description',
field=models.CharField(blank=True, max_length=500, null=True),
),
]

View File

@ -1,15 +1,15 @@
from rest_framework import serializers
from .models import (
RegistrationFileGroup,
RegistrationForm,
Student,
Guardian,
Sibling,
RegistrationFileGroup,
RegistrationForm,
Student,
Guardian,
Sibling,
Language,
RegistrationSchoolFileMaster,
RegistrationSchoolFileTemplate,
RegistrationParentFileMaster,
RegistrationParentFileTemplate,
RegistrationSchoolFileMaster,
RegistrationSchoolFileTemplate,
RegistrationParentFileMaster,
RegistrationParentFileTemplate,
AbsenceManagement,
BilanCompetence
)
@ -95,7 +95,7 @@ class RegistrationFormSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = RegistrationForm
fields = ['student_id', 'last_name', 'first_name', 'guardians']
def get_last_name(self, obj):
return obj.student.last_name
@ -164,12 +164,20 @@ class StudentSerializer(serializers.ModelSerializer):
if guardian_id:
# Si un ID est fourni, récupérer ou mettre à jour le Guardian existant
guardian_instance, created = Guardian.objects.update_or_create(
id=guardian_id,
defaults=guardian_data
)
guardians_ids.append(guardian_instance.id)
continue
try:
guardian_instance = Guardian.objects.get(id=guardian_id)
# Mettre à jour explicitement tous les champs y compris birth_date, profession, address
for field, value in guardian_data.items():
if field != 'id': # Ne pas mettre à jour l'ID
setattr(guardian_instance, field, value)
guardian_instance.save()
guardians_ids.append(guardian_instance.id)
continue
except Guardian.DoesNotExist:
# Si le guardian n'existe pas, créer un nouveau
guardian_instance = Guardian.objects.create(**guardian_data)
guardians_ids.append(guardian_instance.id)
continue
if profile_role_data:
# Vérifiez si 'profile_data' est fourni pour créer un nouveau profil

View File

@ -5,6 +5,8 @@ from Subscriptions.automate import Automate_RF_Register, updateStateMachine
from .models import RegistrationForm
from GestionMessagerie.models import Messagerie
from N3wtSchool import settings, bdd
from N3wtSchool.mailManager import sendMail, getConnection
from django.template.loader import render_to_string
import requests
import logging
logger = logging.getLogger(__name__)
@ -26,17 +28,82 @@ def send_notification(dossier):
# Changer l'état de l'automate
updateStateMachine(dossier, 'EVENT_FOLLOW_UP')
url = settings.URL_DJANGO + 'GestionMessagerie/message'
# Envoyer un email de relance aux responsables
try:
# Récupérer l'établissement du dossier
establishment_id = dossier.establishment.id
destinataires = dossier.eleve.profiles.all()
for destinataire in destinataires:
message = {
"objet": "[RELANCE]",
"destinataire" : destinataire.id,
"corpus": "RELANCE pour le dossier d'inscription"
# Obtenir la connexion SMTP pour cet établissement
connection = getConnection(establishment_id)
# Préparer le contenu de l'email
subject = f"[RELANCE] Dossier d'inscription en attente - {dossier.eleve.first_name} {dossier.eleve.last_name}"
context = {
'student_name': f"{dossier.eleve.first_name} {dossier.eleve.last_name}",
'deadline_date': (timezone.now() - timezone.timedelta(days=settings.EXPIRATION_DI_NB_DAYS)).strftime('%d/%m/%Y'),
'establishment_name': dossier.establishment.name,
'base_url': settings.BASE_URL
}
response = requests.post(url, json=message)
# Utiliser un template HTML pour l'email (si disponible)
try:
html_message = render_to_string('emails/relance_signature.html', context)
except:
# Si pas de template, message simple
html_message = f"""
<html>
<body>
<h2>Relance - Dossier d'inscription en attente</h2>
<p>Bonjour,</p>
<p>Le dossier d'inscription de <strong>{context['student_name']}</strong> est en attente de signature depuis plus de {settings.EXPIRATION_DI_NB_DAYS} jours.</p>
<p>Merci de vous connecter à votre espace pour finaliser l'inscription.</p>
<p>Cordialement,<br>L'équipe {context['establishment_name']}</p>
</body>
</html>
"""
# Récupérer les emails des responsables
destinataires = []
profiles = dossier.eleve.profiles.all()
for profile in profiles:
if profile.email:
destinataires.append(profile.email)
if destinataires:
# Envoyer l'email
result = sendMail(
subject=subject,
message=html_message,
recipients=destinataires,
connection=connection
)
logger.info(f"Email de relance envoyé pour le dossier {dossier.id} à {destinataires}")
else:
logger.warning(f"Aucun email trouvé pour les responsables du dossier {dossier.id}")
except Exception as e:
logger.error(f"Erreur lors de l'envoi de l'email de relance pour le dossier {dossier.id}: {str(e)}")
# En cas d'erreur email, utiliser la messagerie interne comme fallback
try:
url = settings.URL_DJANGO + 'GestionMessagerie/send-message/'
# Créer ou récupérer une conversation avec chaque responsable
destinataires = dossier.eleve.profiles.all()
for destinataire in destinataires:
message_data = {
"conversation_id": None, # Sera géré par l'API
"sender_id": 1, # ID du système ou admin
"content": f"RELANCE pour le dossier d'inscription de {dossier.eleve.first_name} {dossier.eleve.last_name}"
}
response = requests.post(url, json=message_data)
if response.status_code != 201:
logger.error(f"Erreur lors de l'envoi du message interne: {response.text}")
except Exception as inner_e:
logger.error(f"Erreur lors de l'envoi du message interne de fallback: {str(inner_e)}")
# subject = f"Dossier d'inscription non signé - {dossier.objet}"
# message = f"Le dossier d'inscription avec l'objet '{dossier.objet}' n'a pas été signé depuis {dossier.created_at}."

View File

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Relance - Dossier d'inscription</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 20px;
background-color: #f4f4f4;
}
.container {
max-width: 600px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.header {
text-align: center;
border-bottom: 3px solid #007bff;
padding-bottom: 20px;
margin-bottom: 30px;
}
.header h1 {
color: #007bff;
margin: 0;
}
.alert {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 5px;
padding: 15px;
margin: 20px 0;
}
.alert-icon {
font-size: 20px;
margin-right: 10px;
}
.content {
margin: 20px 0;
}
.student-info {
background-color: #f8f9fa;
border-left: 4px solid #007bff;
padding: 15px;
margin: 20px 0;
}
.cta-button {
display: inline-block;
background-color: #007bff;
color: white;
padding: 12px 25px;
text-decoration: none;
border-radius: 5px;
margin: 20px 0;
}
.footer {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #dee2e6;
font-size: 14px;
color: #6c757d;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{{ establishment_name }}</h1>
<p>Relance - Dossier d'inscription</p>
</div>
<div class="alert">
<span class="alert-icon">⚠️</span>
<strong>Attention :</strong> Votre dossier d'inscription nécessite votre attention
</div>
<div class="content">
<p>Bonjour,</p>
<div class="student-info">
<h3>Dossier d'inscription de : <strong>{{ student_name }}</strong></h3>
<p>En attente depuis le : <strong>{{ deadline_date }}</strong></p>
</div>
<p>Nous vous informons que le dossier d'inscription mentionné ci-dessus est en attente de finalisation depuis plus de {{ deadline_date }}.</p>
<p><strong>Action requise :</strong></p>
<ul>
<li>Connectez-vous à votre espace personnel</li>
<li>Vérifiez les documents manquants</li>
<li>Complétez et signez les formulaires en attente</li>
</ul>
<div style="text-align: center;">
<a href="{{ base_url }}" class="cta-button">Accéder à mon espace</a>
</div>
<p>Si vous rencontrez des difficultés ou avez des questions concernant ce dossier, n'hésitez pas à nous contacter.</p>
</div>
<div class="footer">
<p>Cordialement,<br>
L'équipe {{ establishment_name }}</p>
<hr style="margin: 20px 0;">
<p style="font-size: 12px;">
Cet email a été envoyé automatiquement. Si vous pensez avoir reçu ce message par erreur,
veuillez contacter l'établissement directement.
</p>
</div>
</div>
</body>
</html>

View File

@ -35,6 +35,18 @@ def build_payload_from_request(request):
- supporte application/json ou form-data simple
Retour: (payload_dict, None) ou (None, Response erreur)
"""
# Si c'est du JSON pur (Content-Type: application/json)
if hasattr(request, 'content_type') and 'application/json' in request.content_type:
try:
# request.data contient déjà le JSON parsé par Django REST
payload = dict(request.data) if hasattr(request.data, 'items') else request.data
logger.info(f"JSON payload extracted: {payload}")
return payload, None
except Exception as e:
logger.error(f'Error processing JSON: {e}')
return None, Response({'error': "Invalid JSON", 'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
# Cas multipart/form-data avec champ 'data'
data_field = request.data.get('data') if hasattr(request.data, 'get') else None
if data_field:
try:

View File

@ -17,10 +17,10 @@ import Subscriptions.util as util
from Subscriptions.serializers import RegistrationFormSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileTemplateSerializer
from Subscriptions.pagination import CustomSubscriptionPagination
from Subscriptions.models import (
Guardian,
RegistrationForm,
RegistrationSchoolFileTemplate,
RegistrationFileGroup,
Guardian,
RegistrationForm,
RegistrationSchoolFileTemplate,
RegistrationFileGroup,
RegistrationParentFileTemplate,
StudentCompetency
)
@ -431,6 +431,262 @@ class RegisterFormWithIdView(APIView):
# Retourner les données mises à jour
return JsonResponse(studentForm_serializer.data, safe=False)
@swagger_auto_schema(
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'student_data': openapi.Schema(type=openapi.TYPE_STRING, description='JSON string des données étudiant'),
'guardians_data': openapi.Schema(type=openapi.TYPE_STRING, description='JSON string des données responsables'),
'siblings_data': openapi.Schema(type=openapi.TYPE_STRING, description='JSON string des données fratrie'),
'payment_data': openapi.Schema(type=openapi.TYPE_STRING, description='JSON string des données de paiement'),
'current_page': openapi.Schema(type=openapi.TYPE_INTEGER, description='Page actuelle du formulaire'),
'auto_save': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='Indicateur auto-save'),
}
),
responses={200: RegistrationFormSerializer()},
operation_description="Auto-sauvegarde partielle d'un dossier d'inscription.",
operation_summary="Auto-sauvegarder un dossier d'inscription"
)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
def patch(self, request, id):
"""
Auto-sauvegarde partielle d'un dossier d'inscription.
Cette méthode est optimisée pour les sauvegardes automatiques périodiques.
"""
try:
# Récupérer le dossier d'inscription
registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
if not registerForm:
return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND)
# Préparer les données à mettre à jour
update_data = {}
# Traiter les données étudiant si présentes
if 'student_data' in request.data:
try:
student_data = json.loads(request.data['student_data'])
# Extraire les données de paiement des données étudiant
payment_fields = ['registration_payment', 'tuition_payment', 'registration_payment_plan', 'tuition_payment_plan']
payment_data = {}
for field in payment_fields:
if field in student_data:
payment_data[field] = student_data.pop(field)
# Si nous avons des données de paiement, les traiter
if payment_data:
logger.debug(f"Auto-save: extracted payment_data from student_data = {payment_data}")
# Traiter les données de paiement
payment_updates = {}
# Gestion du mode de paiement d'inscription
if 'registration_payment' in payment_data and payment_data['registration_payment']:
try:
from School.models import PaymentMode
payment_mode = PaymentMode.objects.get(id=payment_data['registration_payment'])
registerForm.registration_payment = payment_mode
payment_updates['registration_payment'] = payment_mode.id
except PaymentMode.DoesNotExist:
logger.warning(f"Auto-save: PaymentMode with id {payment_data['registration_payment']} not found")
# Gestion du mode de paiement de scolarité
if 'tuition_payment' in payment_data and payment_data['tuition_payment']:
try:
from School.models import PaymentMode
payment_mode = PaymentMode.objects.get(id=payment_data['tuition_payment'])
registerForm.tuition_payment = payment_mode
payment_updates['tuition_payment'] = payment_mode.id
except PaymentMode.DoesNotExist:
logger.warning(f"Auto-save: PaymentMode with id {payment_data['tuition_payment']} not found")
# Gestion du plan de paiement d'inscription
if 'registration_payment_plan' in payment_data and payment_data['registration_payment_plan']:
try:
from School.models import PaymentPlan
payment_plan = PaymentPlan.objects.get(id=payment_data['registration_payment_plan'])
registerForm.registration_payment_plan = payment_plan
payment_updates['registration_payment_plan'] = payment_plan.id
except PaymentPlan.DoesNotExist:
logger.warning(f"Auto-save: PaymentPlan with id {payment_data['registration_payment_plan']} not found")
# Gestion du plan de paiement de scolarité
if 'tuition_payment_plan' in payment_data and payment_data['tuition_payment_plan']:
try:
from School.models import PaymentPlan
payment_plan = PaymentPlan.objects.get(id=payment_data['tuition_payment_plan'])
registerForm.tuition_payment_plan = payment_plan
payment_updates['tuition_payment_plan'] = payment_plan.id
except PaymentPlan.DoesNotExist:
logger.warning(f"Auto-save: PaymentPlan with id {payment_data['tuition_payment_plan']} not found")
# Sauvegarder les modifications de paiement
if payment_updates:
registerForm.save()
logger.debug(f"Auto-save: Payment data updated - {payment_updates}")
update_data['student'] = student_data
except json.JSONDecodeError:
logger.warning("Auto-save: Invalid JSON in student_data")
# Traiter les données des responsables si présentes
if 'guardians_data' in request.data:
try:
guardians_data = json.loads(request.data['guardians_data'])
logger.debug(f"Auto-save: guardians_data = {guardians_data}")
# Enregistrer directement chaque guardian avec le modèle
for i, guardian_data in enumerate(guardians_data):
guardian_id = guardian_data.get('id')
if guardian_id:
try:
# Récupérer le guardian existant et mettre à jour ses champs
guardian = Guardian.objects.get(id=guardian_id)
# Mettre à jour les champs si ils sont présents
if 'birth_date' in guardian_data and guardian_data['birth_date']:
guardian.birth_date = guardian_data['birth_date']
if 'profession' in guardian_data:
guardian.profession = guardian_data['profession']
if 'address' in guardian_data:
guardian.address = guardian_data['address']
if 'phone' in guardian_data:
guardian.phone = guardian_data['phone']
if 'first_name' in guardian_data:
guardian.first_name = guardian_data['first_name']
if 'last_name' in guardian_data:
guardian.last_name = guardian_data['last_name']
guardian.save()
logger.debug(f"Guardian {i}: Updated birth_date={guardian.birth_date}, profession={guardian.profession}, address={guardian.address}")
except Guardian.DoesNotExist:
logger.warning(f"Auto-save: Guardian with id {guardian_id} not found")
except json.JSONDecodeError:
logger.warning("Auto-save: Invalid JSON in guardians_data")
# Traiter les données de la fratrie si présentes
if 'siblings_data' in request.data:
try:
siblings_data = json.loads(request.data['siblings_data'])
logger.debug(f"Auto-save: siblings_data = {siblings_data}")
# Enregistrer directement chaque sibling avec le modèle
for i, sibling_data in enumerate(siblings_data):
sibling_id = sibling_data.get('id')
if sibling_id:
try:
# Récupérer le sibling existant et mettre à jour ses champs
from Subscriptions.models import Sibling
sibling = Sibling.objects.get(id=sibling_id)
# Mettre à jour les champs si ils sont présents
if 'first_name' in sibling_data:
sibling.first_name = sibling_data['first_name']
if 'last_name' in sibling_data:
sibling.last_name = sibling_data['last_name']
if 'birth_date' in sibling_data and sibling_data['birth_date']:
sibling.birth_date = sibling_data['birth_date']
sibling.save()
logger.debug(f"Sibling {i}: Updated first_name={sibling.first_name}, last_name={sibling.last_name}, birth_date={sibling.birth_date}")
except Sibling.DoesNotExist:
logger.warning(f"Auto-save: Sibling with id {sibling_id} not found")
except json.JSONDecodeError:
logger.warning("Auto-save: Invalid JSON in siblings_data")
# Traiter les données de paiement si présentes
if 'payment_data' in request.data:
try:
payment_data = json.loads(request.data['payment_data'])
logger.debug(f"Auto-save: payment_data = {payment_data}")
# Mettre à jour directement les champs de paiement du formulaire
payment_updates = {}
# Gestion du mode de paiement d'inscription
if 'registration_payment' in payment_data and payment_data['registration_payment']:
try:
from School.models import PaymentMode
payment_mode = PaymentMode.objects.get(id=payment_data['registration_payment'])
registerForm.registration_payment = payment_mode
payment_updates['registration_payment'] = payment_mode.id
except PaymentMode.DoesNotExist:
logger.warning(f"Auto-save: PaymentMode with id {payment_data['registration_payment']} not found")
# Gestion du mode de paiement de scolarité
if 'tuition_payment' in payment_data and payment_data['tuition_payment']:
try:
from School.models import PaymentMode
payment_mode = PaymentMode.objects.get(id=payment_data['tuition_payment'])
registerForm.tuition_payment = payment_mode
payment_updates['tuition_payment'] = payment_mode.id
except PaymentMode.DoesNotExist:
logger.warning(f"Auto-save: PaymentMode with id {payment_data['tuition_payment']} not found")
# Gestion du plan de paiement d'inscription
if 'registration_payment_plan' in payment_data and payment_data['registration_payment_plan']:
try:
from School.models import PaymentPlan
payment_plan = PaymentPlan.objects.get(id=payment_data['registration_payment_plan'])
registerForm.registration_payment_plan = payment_plan
payment_updates['registration_payment_plan'] = payment_plan.id
except PaymentPlan.DoesNotExist:
logger.warning(f"Auto-save: PaymentPlan with id {payment_data['registration_payment_plan']} not found")
# Gestion du plan de paiement de scolarité
if 'tuition_payment_plan' in payment_data and payment_data['tuition_payment_plan']:
try:
from School.models import PaymentPlan
payment_plan = PaymentPlan.objects.get(id=payment_data['tuition_payment_plan'])
registerForm.tuition_payment_plan = payment_plan
payment_updates['tuition_payment_plan'] = payment_plan.id
except PaymentPlan.DoesNotExist:
logger.warning(f"Auto-save: PaymentPlan with id {payment_data['tuition_payment_plan']} not found")
# Sauvegarder les modifications de paiement
if payment_updates:
registerForm.save()
logger.debug(f"Auto-save: Payment data updated - {payment_updates}")
except json.JSONDecodeError:
logger.warning("Auto-save: Invalid JSON in payment_data")
# Mettre à jour la page actuelle si présente
if 'current_page' in request.data:
try:
current_page = int(request.data['current_page'])
# Vous pouvez sauvegarder cette info dans un champ du modèle si nécessaire
logger.debug(f"Auto-save: current_page = {current_page}")
except (ValueError, TypeError):
logger.warning("Auto-save: Invalid current_page value")
# Effectuer la mise à jour partielle seulement si nous avons des données
if update_data:
serializer = RegistrationFormSerializer(registerForm, data=update_data, partial=True)
if serializer.is_valid():
serializer.save()
logger.debug(f"Auto-save successful for student {id}")
return JsonResponse({"status": "auto_save_success", "timestamp": util._now().isoformat()}, safe=False)
else:
logger.warning(f"Auto-save validation errors: {serializer.errors}")
# Pour l'auto-save, on retourne un succès même en cas d'erreur de validation
return JsonResponse({"status": "auto_save_partial", "errors": serializer.errors}, safe=False)
else:
# Pas de données à sauvegarder, mais on retourne un succès
return JsonResponse({"status": "auto_save_no_data"}, safe=False)
except Exception as e:
logger.error(f"Auto-save error for student {id}: {str(e)}")
# Pour l'auto-save, on ne retourne pas d'erreur HTTP pour éviter d'interrompre l'UX
return JsonResponse({"status": "auto_save_failed", "error": str(e)}, safe=False)
@swagger_auto_schema(
responses={204: 'No Content'},
operation_description="Supprime un dossier d'inscription donné.",

View File

@ -1,19 +1,18 @@
from django.http.response import JsonResponse
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
import json
from django.http import QueryDict
from Subscriptions.serializers import RegistrationSchoolFileMasterSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileMasterSerializer, RegistrationParentFileTemplateSerializer
from Subscriptions.serializers import RegistrationSchoolFileMasterSerializer, RegistrationParentFileMasterSerializer, RegistrationParentFileTemplateSerializer
from Subscriptions.models import (
RegistrationForm,
RegistrationSchoolFileMaster,
RegistrationSchoolFileTemplate,
RegistrationParentFileMaster,
RegistrationSchoolFileMaster,
RegistrationParentFileMaster,
RegistrationParentFileTemplate
)
from N3wtSchool import bdd
@ -23,7 +22,8 @@ import Subscriptions.util as util
logger = logging.getLogger(__name__)
class RegistrationSchoolFileMasterView(APIView):
parser_classes = [MultiPartParser, FormParser]
parser_classes = [MultiPartParser, FormParser, JSONParser]
@swagger_auto_schema(
operation_description="Récupère tous les masters de templates d'inscription pour un établissement donné",
manual_parameters=[
@ -64,6 +64,7 @@ class RegistrationSchoolFileMasterView(APIView):
if resp:
return resp
logger.info(f"payload for serializer: {payload}")
serializer = RegistrationSchoolFileMasterSerializer(data=payload, partial=True)
if serializer.is_valid():
@ -90,6 +91,7 @@ class RegistrationSchoolFileMasterView(APIView):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RegistrationSchoolFileMasterSimpleView(APIView):
parser_classes = [MultiPartParser, FormParser, JSONParser]
@swagger_auto_schema(
operation_description="Récupère un master de template d'inscription spécifique",
responses={
@ -126,6 +128,7 @@ class RegistrationSchoolFileMasterSimpleView(APIView):
if resp:
return resp
logger.info(f"payload for update serializer: {payload}")
serializer = RegistrationSchoolFileMasterSerializer(master, data=payload, partial=True)
if serializer.is_valid():