refactor: Traduction en anglais des modules "GestionInscription" et

"GestionLogin"
This commit is contained in:
N3WT DE COMPET
2025-01-12 10:07:06 +01:00
parent 830d9a48c0
commit 2b414b8391
69 changed files with 1393 additions and 1551 deletions

View File

@ -0,0 +1,4 @@
{
"mailFrom":"",
"password":""
}

View File

@ -0,0 +1,63 @@
{
"states": [
"ABSENT",
"CREE",
"ENVOYE",
"EN_VALIDATION",
"A_RELANCER",
"VALIDE",
"ARCHIVE"
],
"transitions": [
{
"name": "creationDI",
"from": "ABSENT",
"to": "CREE"
},
{
"name": "envoiDI",
"from": "CREE",
"to": "ENVOYE"
},
{
"name": "archiveDI",
"from": "CREE",
"to": "ARCHIVE"
},
{
"name": "saisiDI",
"from": "ENVOYE",
"to": "EN_VALIDATION"
},
{
"name": "relanceDI",
"from": "ENVOYE",
"to": "A_RELANCER"
},
{
"name": "archiveDI",
"from": "A_RELANCER",
"to": "ARCHIVE"
},
{
"name": "archiveDI",
"from": "ENVOYE",
"to": "ARCHIVE"
},
{
"name": "valideDI",
"from": "EN_VALIDATION",
"to": "VALIDE"
},
{
"name": "archiveDI",
"from": "EN_VALIDATION",
"to": "ARCHIVE"
},
{
"name": "archiveDI",
"from": "VALIDE",
"to": "ARCHIVE"
}
]
}

View File

@ -0,0 +1,18 @@
{
"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 @@
default_app_config = 'Subscriptions.apps.GestionInscriptionsConfig'

View File

@ -0,0 +1,11 @@
from django.contrib import admin
from .models import *
admin.site.register(Student)
admin.site.register(Guardian)
class EleveAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super().save_model(request, obj, form, change)

View File

@ -0,0 +1,10 @@
from django.apps import AppConfig
from django.conf import settings
class GestioninscriptionsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Subscriptions'
def ready(self):
from Subscriptions.signals import clear_cache
clear_cache()

View File

@ -0,0 +1,45 @@
# state_machine.py
import json
from Subscriptions.models import RegistrationForm
from Subscriptions.signals import clear_cache
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()
clear_cache()
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

View File

@ -0,0 +1,74 @@
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 sendRegisterForm(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.getMainGuardian()
mailReponsableAVerifier = responsable.mail
return responsableMail == mailReponsableAVerifier and str(idMail) == str(fiche_inscription.eleve.id)

View File

@ -0,0 +1,179 @@
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 Auth.models import Profile
from School.models import SchoolClass
from datetime import datetime
class RegistrationFee(models.Model):
class PaymentOptions(models.IntegerChoices):
SINGLE_PAYMENT = 0, _('Paiement en une seule fois')
MONTHLY_PAYMENT = 1, _('Paiement mensuel')
QUARTERLY_PAYMENT = 2, _('Paiement trimestriel')
name = models.CharField(max_length=255, unique=True)
description = models.TextField(blank=True)
base_amount = models.DecimalField(max_digits=10, decimal_places=2)
discounts = models.JSONField(blank=True, null=True)
supplements = models.JSONField(blank=True, null=True)
validity_start_date = models.DateField()
validity_end_date = models.DateField()
payment_option = models.IntegerField(choices=PaymentOptions, default=PaymentOptions.SINGLE_PAYMENT)
def __str__(self):
return self.name
class Language(models.Model):
id = models.AutoField(primary_key=True)
label = models.CharField(max_length=200, default="")
def __str__(self):
return "LANGUAGE"
class Guardian(models.Model):
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)
email = models.CharField(max_length=200, default="", blank=True)
phone = models.CharField(max_length=200, default="", blank=True)
profession = models.CharField(max_length=200, default="", blank=True)
associated_profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
def __str__(self):
return self.last_name + "_" + self.first_name
class Sibling(models.Model):
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):
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(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
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):
return self.spoken_languages.all()
def getMainGuardian(self):
return self.guardians.all()[0]
def getGuardians(self):
return self.guardians.all()
def getProfiles(self):
return self.profiles.all()
def getSiblings(self):
return self.siblings.all()
def getNumberOfSiblings(self):
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 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=settings.DOCUMENT_DIR, default="", blank=True)
associated_rf = models.CharField(max_length=200, default="", blank=True)
def __str__(self):
return "RF_" + self.student.last_name + "_" + self.student.first_name
class RegistrationFile(models.Model):
name = models.CharField(max_length=255)
file = models.FileField(upload_to='registration_files/')
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name

View File

@ -0,0 +1,20 @@
from rest_framework.pagination import PageNumberPagination
from N3wtSchool import settings
class CustomPagination(PageNumberPagination):
page_size_query_param = 'page_size'
max_page_size = settings.NB_MAX_PAGE
page_size = settings.NB_RESULT_PER_PAGE
def get_paginated_response(self, data):
return ({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'count': self.page.paginator.count,
'page_size': self.page_size,
'max_page_size' : self.max_page_size,
'registerForms': data }
)

View File

@ -0,0 +1,214 @@
from rest_framework import serializers
from .models import RegistrationFile, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationFee
from School.models import SchoolClass
from Auth.models import Profile
from Auth.serializers import ProfileSerializer
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 RegistrationFileSerializer(serializers.ModelSerializer):
class Meta:
model = RegistrationFile
fields = '__all__'
class RegistrationFeeSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
model = RegistrationFee
fields = '__all__'
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)
associated_profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=True)
associated_profile_email = serializers.SerializerMethodField()
class Meta:
model = Guardian
fields = '__all__'
def get_associated_profile_email(self, obj):
return obj.associated_profile.email
class StudentSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
guardians = GuardianSerializer(many=True, required=False)
siblings = SiblingSerializer(many=True, required=False)
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 get_or_create_packages(self, guardians_data):
guardians_ids = []
for guardian_data in guardians_data:
guardian_instance, created = Guardian.objects.get_or_create(
id=guardian_data.get('id'),
defaults=guardian_data
)
guardians_ids.append(guardian_instance.id)
return guardians_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.get_or_create_packages(guardians_data))
student.siblings.set(self.get_or_create_packages(siblings_data))
student.spoken_languages.set(self.get_or_create_packages(languages_data))
return student
def create_or_update_packages(self, guardians_data):
guardians_ids = []
for guardian_data in guardians_data:
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 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_packages(guardians_data))
if siblings_data:
instance.siblings.set(self.create_or_update_packages(siblings_data))
if languages_data:
instance.spoken_languages.set(self.create_or_update_packages(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()
class Meta:
model = RegistrationForm
fields = '__all__'
def create(self, validated_data):
student_data = validated_data.pop('student')
student = StudentSerializer.create(StudentSerializer(), student_data)
registrationForm = RegistrationForm.objects.create(student=student, **validated_data)
return registrationForm
def update(self, instance, validated_data):
student_data = validated_data.pop('student', None)
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()
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")
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)
class Meta:
model = Guardian
fields = ['id', 'last_name', 'first_name', 'email', 'associated_profile']
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__'

View File

@ -0,0 +1,44 @@
from django.db.models.signals import post_save, post_delete, m2m_changed
from django.dispatch import receiver
from django.core.cache import cache
from .models import RegistrationForm, Student, Guardian
from Auth.models import Profile
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=RegistrationForm)
@receiver(post_delete, sender=RegistrationForm)
def clear_cache_after_change(sender, instance, **kwargs):
clear_cache()
@receiver(m2m_changed, sender=Student.guardians.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.guardians.all():
Guardian.objects.filter(eleve=None).delete()
@receiver(m2m_changed, sender=Student.profiles.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.profiles.all():
Profile.objects.filter(eleve=None).delete()

View File

@ -0,0 +1,43 @@
# tasks.py
from celery import shared_task
from django.utils import timezone
from Subscriptions.automate import Automate_RF_Register, updateStateMachine
from .models import RegistrationForm
from GestionMessagerie.models import Messagerie
from N3wtSchool import settings, bdd
import requests
import logging
logger = logging.getLogger(__name__)
@shared_task
def check_for_signature_deadlines():
now = timezone.now()
deadline = now - timezone.timedelta(days=settings.EXPIRATION_DI_NB_DAYS)
# deadline = now - timezone.timedelta(seconds=settings.EXPIRATION_DI_NB_DAYS)
dossiers_en_attente = RegistrationForm.objects.filter(etat=RegistrationForm.RegistrationFormStatus.DI_ENVOYE, dateMAJ__lt=deadline)
for dossier in dossiers_en_attente:
send_notification(dossier)
def send_notification(dossier):
logger.debug(f'Dossier en attente.... {dossier} - Positionnement à l\'état A_RELANCER')
# Changer l'état de l'automate
updateStateMachine(dossier, 'relanceDI')
url = settings.URL_DJANGO + 'GestionMessagerie/message'
destinataires = dossier.eleve.profiles.all()
for destinataire in destinataires:
message = {
"objet": "[RELANCE]",
"destinataire" : destinataire.id,
"corpus": "RELANCE pour le dossier d'inscription"
}
response = requests.post(url, json=message)
# 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}."
# send_mail(subject, message, settings.EMAIL_HOST_USER, [dossier.destinataire.email])

View File

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% load rest_framework %}
{% block content %}
<h1>Création d'une nouvelle fiche d'inscription</h1>
<br>
<form action='{% url 'Subscriptions:nouvelEleve' %}' method="post">
{% csrf_token %}
<ul>
<li style="margin-bottom: 15px"><strong>ELEVE</strong></li>
<div class="input-group">
<label for="nomEleve">Nom</label>
<div class="input-wrapper">
<input type="text" id="nomEleve" name="nomEleve">
</div>
</div>
<div class="input-group">
<label for="prenomEleve">Prénom</label>
<div class="input-wrapper">
<input type="text" id="prenomEleve" name="prenomEleve">
</div>
</div>
<li style="margin-bottom: 15px"><strong>LISTE DES CONTACTS</strong></li>
<div class="input-group">
<label for="mail">Adresse e-mail </label>
<div class="input-wrapper">
<input type="text" id="mail" name="mailResponsable">
</div>
</div>
<div class="input-group">
<label for="telephone">Numéro de téléphone</label>
<div class="input-wrapper">
<input type="text" id="telephone" name="telephoneResponsable">
</div>
</div>
<div class="input-group">
<label for="nomContact">Nom</label>
<div class="input-wrapper">
<input type="text" id="nomContact" name="nomResponsable">
</div>
</div>
<div class="input-group">
<label for="prenomContact">Prénom</label>
<div class="input-wrapper">
<input type="text" id="prenomContact" name="prenomResponsable">
</div>
</div>
</ul>
<input class="btn primary" type="submit" value="Créer" name="valider">
<br>
<input class="btn" type="button" value="Annuler" name="cancel">
</form>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block content %}
<h1>Configuration des dossiers d'inscriptions</h1>
<br>
<form action='{% url 'Subscriptions:index' %}' method="post">
{% csrf_token %}
<ul>
<div style="margin-bottom: 15px">
<label>Relance automatique :</label>
<input type="text" name="delaiRelance" value="{{ delaiRelance }}">
<label>secondes</label>
</div>
</ul>
<input class="btn" type="submit" value="Configurer" name="valider">
</form>
{% endblock %}

View File

@ -0,0 +1,162 @@
{% extends "base.html" %}
{% block content %}
<h1>Création du dossier d'inscription</h1>
<br>
<form action='{% url 'Subscriptions:validate' eleve.id %}' method="post" enctype="multipart/form-data">
{% csrf_token %}
{% with responsable=eleve.getMainGuardian %}
<ul>
<li style="margin-bottom: 15px"><strong>ELEVE</strong></li>
<div style="margin-bottom: 15px">
<label>Nom :</label>
<input type="text" name="nomEleve" value="{{ eleve.nom }}">
</div>
<div style="margin-bottom: 15px">
<label>Prénom :</label>
<input type="text" name="prenomEleve" value="{{ eleve.prenom }}">
</div>
<div style="margin-bottom: 15px">
<label>Genre :<br></label>
{% for genre in genres %}
<label>{{ genre }}</label>
<input type="radio" id="{{ genre }}" name="genre" value="{{ genre }}">
{% endfor %}
</div>
<div style="margin-bottom: 15px">
<label>Adresse :</label>
<input type="text" name="adresseEleve">
</div>
<div style="margin-bottom: 15px">
<label>Date de naissance :</label>
<input type="text" name="dateNaissanceEleve">
</div>
<div style="margin-bottom: 15px">
<label>Lieu de naissance :</label>
<input type="text" name="lieuNaissanceEleve">
</div>
<div style="margin-bottom: 15px">
<label>Code postal de naissance :</label>
<input type="text" name="codePostalNaissanceEleve">
</div>
<div style="margin-bottom: 15px">
<label>Nationalité :</label>
<input type="text" name="nationaliteEleve">
</div>
<div style="margin-bottom: 15px">
<label>Langue parlée :</label>
<input type="text" name="langueEleve">
</div>
<div style="margin-bottom: 15px">
<label>Ambiance :<br></label>
{% for ambiance in ambiances %}
<label>{{ ambiance }}</label>
<input type="radio" id="{{ ambiance }}" name="ambiance" value="{{ ambiance }}">
{% endfor %}
</div>
<div style="margin-bottom: 15px">
<label>Médecin traitant :</label>
<input type="text" name="medecinTraitantEleve">
</div>
<li style="margin-bottom: 15px"><strong>RESPONSABLES</strong></li>
<ul>
<li style="margin-bottom: 15px"><strong>RESPONSABLE 1</strong></li>
<div style="margin-bottom: 15px">
<label>Nom :</label>
<input type="text" name="nomResponsable1" value="{{ responsable.nom }}">
</div>
<div style="margin-bottom: 15px">
<label>Prénom :</label>
<input type="text" name="prenomResponsable1" value="{{ responsable.prenom }}">
</div>
<div style="margin-bottom: 15px">
<label>Adresse :</label>
<input type="text" name="adresseResponsable1" value="{{ responsable.adresse }}">
</div>
<div style="margin-bottom: 15px">
<label>Date de naissance :</label>
<input type="text" name="dateNaissanceResponsable1" value="{{ responsable.dateNaissance }}">
</div>
<div style="margin-bottom: 15px">
<label>Mail :</label>
<input type="text" name="mailResponsable1" value="{{ responsable.mail }}">
</div>
<div style="margin-bottom: 15px">
<label>Téléphone :</label>
<input type="text" name="telephoneResponsable1" value="{{ responsable.telephone }}">
</div>
<div style="margin-bottom: 15px">
<label>Profession :</label>
<input type="text" name="professionResponsable1" value="{{ responsable.profession }}">
</div>
<li style="margin-bottom: 15px"><strong>RESPONSABLE 2</strong></li>
<div style="margin-bottom: 15px">
<label>Nom :</label>
<input type="text" name="nomResponsable2">
</div>
<div style="margin-bottom: 15px">
<label>Prénom :</label>
<input type="text" name="prenomResponsable2">
</div>
<div style="margin-bottom: 15px">
<label>Adresse :</label>
<input type="text" name="adresseResponsable2">
</div>
<div style="margin-bottom: 15px">
<label>Date de naissance :</label>
<input type="text" name="dateNaissanceResponsable2">
</div>
<div style="margin-bottom: 15px">
<label>Mail :</label>
<input type="text" name="mailResponsable2">
</div>
<div style="margin-bottom: 15px">
<label>Téléphone :</label>
<input type="text" name="telephoneResponsable2">
</div>
<div style="margin-bottom: 15px">
<label>Profession :</label>
<input type="text" name="professionResponsable2">
</div>
</ul>
<li style="margin-bottom: 15px"><strong>FRATRIE</strong></li>
<ul>
<li style="margin-bottom: 15px"><strong>FRERE - SOEUR 1 :</strong></li>
<div style="margin-bottom: 15px">
<label>Nom :</label>
<input type="text" name="nomFrere1">
</div>
<div style="margin-bottom: 15px">
<label>Prénom :</label>
<input type="text" name="prenomFrere1">
</div>
<div style="margin-bottom: 15px">
<label>Date de naissance :</label>
<input type="text" name="dateNaissanceFrere1">
</div>
<li style="margin-bottom: 15px"><strong>FRERE - SOEUR 2 :</strong></li>
<div style="margin-bottom: 15px">
<label>Nom :</label>
<input type="text" name="nomFrere2">
</div>
<div style="margin-bottom: 15px">
<label>Prénom :</label>
<input type="text" name="prenomFrere2">
</div>
<div style="margin-bottom: 15px">
<label>Date de naissance :</label>
<input type="text" name="dateNaissanceFrere2">
</div>
</ul>
<li style="margin-bottom: 15px"><strong>PAIEMENT</strong></li>
<div style="margin-bottom: 15px">
<label>Mode Paiement :<br></label>
{% for modePaiement in modesPaiement %}
<label>{{ modePaiement }}</label>
<input type="radio" id="{{ modePaiement }}" name="modePaiement" value="{{ modePaiement }}">
{% endfor %}
</div>
</ul>
<input class="btn" type="submit" value="Valider" name="valider">
{% endwith %}
</form>
{% endblock %}

View File

@ -0,0 +1,42 @@
{% extends "base.html" %}
{% block content %}
<h1>Edition d'une fiche d'inscription</h1>
<br>
<form action='{% url 'Subscriptions:index' %}' method="post">
{% csrf_token %}
{% with responsable=eleve.getMainGuardian %}
<ul>
<div style="margin-bottom: 15px">
<input type="hidden" name="fiche_id" value="{{ eleve.id }}">
</div>
<li style="margin-bottom: 15px"><strong>ELEVE</strong></li>
<div style="margin-bottom: 15px">
<label>Nom :</label>
<input type="text" name="nomEleve" value="{{ eleve.nom }}">
</div>
<div style="margin-bottom: 15px">
<label>Prénom :</label>
<input type="text" name="prenomEleve" value="{{ eleve.prenom }}">
</div>
<li style="margin-bottom: 15px"><strong>LISTE DES CONTACTS</strong></li>
<div style="margin-bottom: 15px">
<label>Adresse e-mail :</label>
<input type="text" name="mail" value="{{ responsable.mail }}">
</div>
<div style="margin-bottom: 15px">
<label>Numéro de téléphone :</label>
<input type="text" name="telephone" value="{{ responsable.telephone }}">
</div>
<div style="margin-bottom: 15px">
<label>Nom :</label>
<input type="text" name="nomContact" value="{{ responsable.nom }}">
</div>
<div style="margin-bottom: 15px">
<label>Prénom :</label>
<input type="text" name="prenomContact" value="{{ responsable.prenom }}">
</div>
</ul>
<input class="btn" type="submit" value="Modifier" name="valider">
{% endwith %}
</form>
{% endblock %}

View File

@ -0,0 +1,126 @@
{% extends "base.html" %}
{% load myTemplateTag %}
{% block content %}
<h1>Inscriptions 2024/2025</h1>
<br>
<div>
<br>
<br>
</div>
<section class="heading-section">
<!-- Search bar -->
<div class="input-group max-80">
<div class="input-wrapper max">
<span class="icon-ctn">
<i class="icon user-search"></i>
</span>
<input type="text" id="username" placeholder="Rechercher">
</div>
</div>
<a class="btn primary" href="nouvelEleve">
Ajouter <i class="icon profile-add"></i>
</a>
</section>
<section class="heading-section">
<div class="alphabet-filter">
{% for letter in "*ABCDEFGHIJKLMNOPQRSTUVWXYZ" %}
<a class="item" href="?letter={{ letter }}">{{ letter }}</a>
{% endfor %}
</div>
</section>
<table class="table">
<thead>
<tr>
<th>Nom</th>
<th>Prenom</th>
<th>Mail</th>
<th>Téléphone</th>
<th>MàJ Le</th>
<th>Statut</th>
<th>Fichiers</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for registerForm in ficheInscriptions_list %}
{% with eleve=registerForm.eleve %}
{% with responsable=eleve.getMainGuardian %}
{% with fichiers=registerForm|recupereFichiersDossierInscription %}
<tr>
<td>{{ eleve.nom }}</td>
<td>{{ eleve.prenom }}</td>
<td>{{ responsable.mail }}</td>
<td>{{ responsable.telephone }}</td>
<td>{{ registerForm.dateMAJ }}</td>
<td>
{% if registerForm.etat == 0 %}
<span class="tag blue"> Créé</span>
{% elif registerForm.etat == 1 %}
<span class="tag orange"> Envoyé</span>
{% elif registerForm.etat == 2 %}
<span class="tag purple"> En Validation</span>
{% else %}
<span class="tag green"> Validé</span>
{% endif %}
</td>
<td>
{% for fichier in fichiers %}
<a href="{{ fichier.url }}">{{ fichier.nom }}</a>
{% endfor %}
</td>
<td class="actions">
<button class="icon-btn" onclick="location.href='{% url 'Subscriptions:send' eleve.id %}'" type="submit"> <i class="icon directbox-send"></i></button>
<button class="icon-btn"> <i class="icon edit"></i></button>
<button class="icon-btn red"> <i class="icon user-minus"></i></button>
</td>
</tr>
{% endwith %}
{% endwith %}
{% endwith %}
{% endfor %}
</tbody>
<tfoot>
<tr>
<td></td>
<td colspan="6">
<div class="pagination">
{% if ficheInscriptions_list.has_previous %}
{% if ficheInscriptions_list.previous_page_number == 1 %}
<a class="item" href="?page={{ ficheInscriptions_list.previous_page_number }}">&lt;</a>
<a class="item" href="?page=1">1</a>
{% else %}
<a class="item" href="?page={{ ficheInscriptions_list.previous_page_number }}">&lt;</a>
<a class="item" href="?page=1">1</a>
<a class="item" >...</a>
{% endif %}
{% endif %}
{% if ficheInscriptions_list %}
<a class="item active">{{ ficheInscriptions_list.number }}</a>
{% else %}
<a class="item">{{ ficheInscriptions_list.number }}</a>
{% endif %}
{% if ficheInscriptions_list.has_next %}
{% if ficheInscriptions_list.next_page_number == ficheInscriptions_list.paginator.num_pages %}
<a class="item" href="?page={{ ficheInscriptions_list.next_page_number }}">{{ ficheInscriptions_list.next_page_number }}</a>
<a class="item" href="?page={{ ficheInscriptions_list.next_page_number }}">&gt;</a>
{% else %}
<a class="item" >...</a>
<a class="item" href="?page={{ ficheInscriptions_list.paginator.num_pages }}">{{ ficheInscriptions_list.paginator.num_pages }}</a>
<a class="item" href="?page={{ ficheInscriptions_list.next_page_number }}">&gt;</a>
{% endif %}
{% endif %}
</div>
</td>
<td></td>
</tr>
</tfoot>
</table>
{% endblock %}

View File

@ -0,0 +1,31 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static '/css/main.css' %}">
<title>Monteschool</title>
</head>
<body>
<div class="sidebar">
<div class="itemlogo">
<div class="circle"></div>
</div>
<a class="item active">
<i class="icon receipt-edit"></i> Administration
</a>
<a class="item">
<i class="icon user-line"></i> Statistiques
</a>
<a class="item">
<i class="icon book"></i> Paramétrage
</a>
</div>
<div class="container">
{% block content %}
{% endblock %}
</div>
</body>
</html>

View File

@ -0,0 +1,97 @@
<!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>{{ signatureDate }}</b> <br/>
A : <b>{{ signatureTime }}</b>
<hr class='hrItem' />
<h1>ELEVE</h1>
{% with level=student|getStudentLevel %}
{% with gender=student|getStudentGender %}
NOM : <b>{{ student.last_name }}</b> <br/>
PRENOM : <b>{{ student.first_name }}</b> <br/>
ADRESSE : <b>{{ student.address }}</b> <br/>
GENRE : <b>{{ gender }}</b> <br/>
NE(E) LE : <b>{{ student.birth_date }}</b> <br/>
A : <b>{{ student.birth_place }} ({{ student.birth_postal_code }})</b> <br/>
NATIONALITE : <b>{{ student.nationality }}</b> <br/>
NIVEAU : <b>{{ level }}</b> <br/>
MEDECIN TRAITANT : <b>{{ student.attending_physician }}</b> <br/>
{% endwith %}
{% endwith %}
<hr class='hrItem' />
<h1>RESPONSABLES</h1>
{% with guardians=student.getGuardians %}
{% with siblings=student.getGuardians %}
{% for guardian in guardians%}
<h2>Guardian {{ forloop.counter }}</h2>
NOM : <b>{{ guardian.last_name }}</b> <br/>
PRENOM : <b>{{ guardian.first_name }}</b> <br/>
ADRESSE : <b>{{ guardian.address }}</b> <br/>
NE(E) LE : <b>{{ guardian.birth_date }}</b> <br/>
MAIL : <b>{{ guardian.email }}</b> <br/>
TEL : <b>{{ guardian.phone }}</b> <br/>
PROFESSION : <b>{{ guardian.profession }}</b> <br/>
{% endfor %}
<hr class='hrItem' />
<h1>FRATRIE</h1>
{% for sibling in siblings%}
<h2>Frère - Soeur {{ forloop.counter }}</h2>
NOM : <b>{{ sibling.last_name }}</b> <br/>
PRENOM : <b>{{ sibling.first_name }}</b> <br/>
NE(E) LE : <b>{{ sibling.birth_date }}</b> <br/>
{% endfor %}
<hr class='hrItem' />
<h1>MODALITES DE PAIEMENT</h1>
{% with paymentMethod=student|getStudentPaymentMethod %}
<b>{{ paymentMethod }}</b> <br/>
{% endwith %}
{% endwith %}
{% endwith %}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,18 @@
from Subscriptions.models import RegistrationForm, Student
from django import template
register = template.Library()
@register.filter
def getStudentPaymentMethod(pk):
registerForm = RegistrationForm.objects.get(student=pk)
return Student.PaymentMethod(int(registerForm.student.payment_method)).label
@register.filter
def getStudentLevel(pk):
registerForm = RegistrationForm.objects.get(student=pk)
return Student.StudentLevel(int(registerForm.student.level)).label
@register.filter
def getStudentGender(pk):
registerForm = RegistrationForm.objects.get(student=pk)
return Student.StudentGender(int(registerForm.student.gender)).label

View File

@ -0,0 +1,36 @@
from django.urls import path, re_path
from . import views
from Subscriptions.views import RegisterFileTemplateView, RegisterFormListView, RegisterFormView, StudentView, GuardianView, ChildrenListView, StudentListView, RegisterFeeView
urlpatterns = [
re_path(r'^registerForms/([a-zA-z]+)$', RegisterFormListView.as_view(), name="listefichesInscriptions"),
re_path(r'^registerForm$', RegisterFormView.as_view(), name="registerForms"),
re_path(r'^registerForm/([0-9]+)$', RegisterFormView.as_view(), name="registerForms"),
# Page de formulaire d'inscription - ELEVE
re_path(r'^student/([0-9]+)$', StudentView.as_view(), name="students"),
# Page de formulaire d'inscription - RESPONSABLE
re_path(r'^fetchLastGuardian$', GuardianView.as_view(), name="fetchLastGuardian"),
# 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 children
re_path(r'^children/([0-9]+)$', ChildrenListView.as_view(), name="children"),
# Page INSCRIPTION - Liste des élèves
re_path(r'^students$', StudentListView.as_view(), name="students"),
# Frais d'inscription
re_path(r'^registerFees$', RegisterFeeView.as_view(), name="registerFees"),
re_path(r'^registerFilesTemplates$', RegisterFileTemplateView.as_view(), name='registerFilesTemplates'),
re_path(r'^registerFilesTemplates/([0-9]+)$', RegisterFileTemplateView.as_view(), name="registerFilesTemplates"),
]

View File

@ -0,0 +1,82 @@
from django.shortcuts import render,get_object_or_404,get_list_or_404
from .models import RegistrationForm, Student, Guardian, Sibling
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(RegistrationForm),
}
return context
def recupereListeFichesInscriptionEnAttenteSEPA():
ficheInscriptionsSEPA_list = RegistrationForm.objects.filter(modePaiement="Prélèvement SEPA").filter(etat=RegistrationForm.RegistrationFormStatus['SEPA_ENVOYE'])
return ficheInscriptionsSEPA_list
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 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 getArgFromRequest(_argument, _request):
resultat = None
data=JSONParser().parse(_request)
resultat = data[_argument]
return resultat
def rfToPDF(registerForm):
# Ajout du fichier d'inscriptions
data = {
'pdf_title': "Dossier d'inscription de %s"%registerForm.student.first_name,
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
'signatureTime': convertToStr(_now(), '%H:%M'),
'student':registerForm.student,
}
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
PDFFileName = "Dossier_Inscription_%s_%s.pdf"%(registerForm.student.last_name, registerForm.student.first_name)
pathFichier = Path(settings.DOCUMENT_DIR + "/" + PDFFileName)
if os.path.exists(str(pathFichier)):
print(f'File exists : {str(pathFichier)}')
os.remove(str(pathFichier))
receipt_file = BytesIO(pdf.content)
registerForm.fichierInscription = File(receipt_file, PDFFileName)

View File

@ -0,0 +1,280 @@
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
import json
from pathlib import Path
import os
from io import BytesIO
import Subscriptions.mailManager as mailer
import Subscriptions.util as util
from Subscriptions.serializers import RegistrationFormSerializer, RegistrationFileSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFeeSerializer
from Subscriptions.pagination import CustomPagination
from Subscriptions.signals import clear_cache
from .models import Student, Guardian, RegistrationForm, RegistrationFee, RegistrationFile
from Subscriptions.automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine
from Auth.models import Profile
from N3wtSchool import settings, renderers, bdd
class RegisterFormListView(APIView):
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
def get(self, request, _filter):
# 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)
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):
pagination_class = CustomPagination
def get(self, request, _id):
registerForm=bdd.getObject(RegistrationForm, "student__id", _id)
registerForm_serializer=RegistrationFormSerializer(registerForm)
return JsonResponse(registerForm_serializer.data, safe=False)
def post(self, request):
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)
def put(self, request, id):
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:
# Le parent a complété le dossier d'inscription, il est soumis à validation par l'école
json.dumps(studentForm_data)
util.rfToPDF(registerForm)
# Mise à jour de l'automate
updateStateMachine(registerForm, 'saisiDI')
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)
def delete(self, request, id):
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.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 StudentView(APIView):
def get(self, request, _id):
student = bdd.getObject(_objectName=Student, _columnName='id', _value=_id)
student_serializer = StudentSerializer(student)
return JsonResponse(student_serializer.data, safe=False)
class GuardianView(APIView):
def get(self, request):
lastGuardian = bdd.getLastId(Guardian)
return JsonResponse({"lastid":lastGuardian}, safe=False)
def send(request, id):
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({"errorMessage":errorMessage}, safe=False)
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
def archive(request, id):
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)
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
def relance(request, id):
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)
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
# API utilisée pour la vue parent
class ChildrenListView(APIView):
# Récupération des élèves d'un parent
# idProfile : identifiant du profil connecté rattaché aux fiches d'élèves
def get(self, request, _idProfile):
students = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__profilAssocie__id', _value=_idProfile)
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):
# 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 RegisterFeeView(APIView):
def get(self, request):
tarifs = bdd.getAllObjects(RegistrationFee)
tarifs_serializer = RegistrationFeeSerializer(tarifs, many=True)
return JsonResponse(tarifs_serializer.data, safe=False)
class RegisterFileTemplateView(APIView):
parser_classes = (MultiPartParser, FormParser)
def get(self, request):
fichiers = RegistrationFile.objects.all()
serializer = RegistrationFormSerializer(fichiers, many=True)
return Response(serializer.data)
def post(self, request):
serializer = RegistrationFormSerializer(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):
fichierInscription = bdd.getObject(_objectName=RegistrationFile, _columnName='id', _value=_id)
if fichierInscription is not None:
fichierInscription.file.delete() # Supprimer le fichier uploadé
fichierInscription.delete()
return JsonResponse({'message': 'La suppression du fichier d\'inscription a été effectuée avec succès'}, safe=False)
else:
return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False)