chore: Initial Commit

feat: Gestion des inscriptions [#1]
feat(frontend): Création des vues pour le paramétrage de l'école [#2]
feat: Gestion du login [#6]
fix: Correction lors de la migration des modèle [#8]
feat: Révision du menu principal [#9]
feat: Ajout d'un footer [#10]
feat: Création des dockers compose pour les environnements de
développement et de production [#12]
doc(ci): Mise en place de Husky et d'un suivi de version automatique [#14]
This commit is contained in:
Luc SORIGNET
2024-11-18 10:02:58 +01:00
committed by N3WT DE COMPET
commit af0cd1c840
228 changed files with 22694 additions and 0 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 = 'GestionInscriptions.apps.GestionInscriptionsConfig'

View File

@ -0,0 +1,11 @@
from django.contrib import admin
from .models import *
admin.site.register(Eleve)
admin.site.register(Responsable)
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 = 'GestionInscriptions'
def ready(self):
from GestionInscriptions.signals import clear_cache
clear_cache()

View File

@ -0,0 +1,45 @@
# state_machine.py
import json
from GestionInscriptions.models import FicheInscription
from GestionInscriptions.signals import clear_cache
state_mapping = {
"ABSENT": FicheInscription.EtatDossierInscription.DI_ABSENT,
"CREE": FicheInscription.EtatDossierInscription.DI_CREE,
"ENVOYE": FicheInscription.EtatDossierInscription.DI_ENVOYE,
"EN_VALIDATION": FicheInscription.EtatDossierInscription.DI_EN_VALIDATION,
"A_RELANCER": FicheInscription.EtatDossierInscription.DI_A_RELANCER,
"VALIDE": FicheInscription.EtatDossierInscription.DI_VALIDE,
"ARCHIVE": FicheInscription.EtatDossierInscription.DI_ARCHIVE
}
def load_config(config_file):
with open(config_file, 'r') as file:
config = json.load(file)
return config
def getStateMachineObject(etat) :
return Automate_DI_Inscription(etat)
def getStateMachineObjectState(etat):
return Automate_DI_Inscription(etat).state
def updateStateMachine(di, transition) :
automateModel = load_config('GestionInscriptions/Configuration/automate.json')
state_machine = getStateMachineObject(di.etat)
print(f'etat DI : {state_machine.state}')
if state_machine.trigger(transition, automateModel):
di.etat = state_machine.state
di.save()
clear_cache()
class Automate_DI_Inscription:
def __init__(self, initial_state):
self.state = initial_state
def trigger(self, transition_name, config):
for transition in config["transitions"]:
if transition["name"] == transition_name and self.state == state_mapping[transition["from"]]:
self.state = state_mapping[transition["to"]]
return True
return False

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 envoieDossierInscription(recipients):
errorMessage = ''
try:
print(f'{settings.EMAIL_HOST_USER}')
send_mail(
settings.EMAIL_INSCRIPTION_SUBJECT,
settings.EMAIL_INSCRIPTION_CORPUS%[recipients],
settings.EMAIL_HOST_USER,
[recipients],
fail_silently=False,
)
except Exception as e:
errorMessage = str(e)
return errorMessage
def envoieRelanceDossierInscription(recipients, code):
errorMessage = ''
try:
send_mail(
settings.EMAIL_RELANCE_SUBJECT,
settings.EMAIL_RELANCE_CORPUS%str(code),
settings.EMAIL_HOST_USER,
[recipients],
fail_silently=False,
)
except Exception as e:
errorMessage = str(e)
return errorMessage
def envoieSEPA(recipients, ref):
send_mail(
settings.EMAIL_SEPA_SUBJECT%str(ref),
settings.EMAIL_SEPA_CORPUS,
settings.EMAIL_HOST_USER,
[recipients],
fail_silently=False,
)
def isValid(message, fiche_inscription):
# Est-ce que la référence du dossier est VALIDE
subject = message.subject
print ("++++ " + subject)
responsableMail = message.from_header
result = re.search('<(.*)>', responsableMail)
if result:
responsableMail = result.group(1)
result = re.search(r'.*\[Ref(.*)\].*', subject)
idMail = -1
if result:
idMail = result.group(1).strip()
eleve = fiche_inscription.eleve
responsable = eleve.getResponsablePrincipal()
mailReponsableAVerifier = responsable.mail
return responsableMail == mailReponsableAVerifier and str(idMail) == str(fiche_inscription.eleve.id)

View File

@ -0,0 +1,123 @@
from django.db import models
from django.utils.timezone import now
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from GestionLogin.models import Profil
class Langue(models.Model):
id = models.AutoField(primary_key=True)
libelle = models.CharField(max_length=200, default="")
def __str__(self):
return "LANGUE"
class Responsable(models.Model):
nom = models.CharField(max_length=200, default="")
prenom = models.CharField(max_length=200, default="")
dateNaissance = models.CharField(max_length=200, default="", blank=True)
adresse = models.CharField(max_length=200, default="", blank=True)
mail = models.CharField(max_length=200, default="", blank=True)
telephone = models.CharField(max_length=200, default="", blank=True)
profession = models.CharField(max_length=200, default="", blank=True)
profilAssocie = models.ForeignKey(Profil, on_delete=models.CASCADE)
def __str__(self):
return self.nom + "_" + self.prenom
class Frere(models.Model):
id = models.AutoField(primary_key=True)
nom = models.CharField(max_length=200, default="")
prenom = models.CharField(max_length=200, default="")
dateNaissance = models.CharField(max_length=200, default="", blank=True)
def __str__(self):
return "FRERE"
class Eleve(models.Model):
class GenreEleve(models.IntegerChoices):
NONE = 0, _('Sélection du genre')
MALE = 1, _('Garçon')
FEMALE = 2, _('Fille')
class NiveauEleve(models.IntegerChoices):
NONE = 0, _('Sélection du niveau')
TPS = 1, _('TPS - Très Petite Section')
PS = 2, _('PS - Petite Section')
MS = 3, _('MS - Moyenne Section')
GS = 4, _('GS - Grande Section')
class ModePaiement(models.IntegerChoices):
NONE = 0, _('Sélection du mode de paiement')
PRELEVEMENT_SEPA = 1, _('Prélèvement SEPA')
CHEQUE = 2, _('Chèque')
nom = models.CharField(max_length=200, default="")
prenom = models.CharField(max_length=200, default="")
genre = models.IntegerField(choices=GenreEleve, default=GenreEleve.NONE, blank=True)
niveau = models.IntegerField(choices=NiveauEleve, default=NiveauEleve.NONE, blank=True)
nationalite = models.CharField(max_length=200, default="", blank=True)
adresse = models.CharField(max_length=200, default="", blank=True)
dateNaissance = models.CharField(max_length=200, default="", blank=True)
lieuNaissance = models.CharField(max_length=200, default="", blank=True)
codePostalNaissance = models.IntegerField(default=0, blank=True)
medecinTraitant = models.CharField(max_length=200, default="", blank=True)
modePaiement = models.IntegerField(choices=ModePaiement, default=ModePaiement.NONE, blank=True)
# Relation N-N
profils = models.ManyToManyField(Profil, blank=True)
# Relation N-N
responsables = models.ManyToManyField(Responsable, blank=True)
# Relation N-N
freres = models.ManyToManyField(Frere, blank=True)
# Relation N-N
languesParlees = models.ManyToManyField(Langue, blank=True)
def __str__(self):
return self.nom + "_" + self.prenom
def getLanguesParlees(self):
return self.languesParlees.all()
def getResponsablePrincipal(self):
return self.responsables.all()[0]
def getResponsables(self):
return self.responsables.all()
def getProfils(self):
return self.profils.all()
def getFreres(self):
return self.freres.all()
def getNbFreres(self):
return self.freres.count()
class FicheInscription(models.Model):
class EtatDossierInscription(models.IntegerChoices):
DI_ABSENT = 0, _('Pas de dossier d\'inscription')
DI_CREE = 1, _('Dossier d\'inscription créé')
DI_ENVOYE = 2, _('Dossier d\'inscription envoyé')
DI_EN_VALIDATION = 3, _('Dossier d\'inscription en cours de validation')
DI_A_RELANCER = 4, _('Dossier d\'inscription à relancer')
DI_VALIDE = 5, _('Dossier d\'inscription validé')
DI_ARCHIVE = 6, _('Dossier d\'inscription archivé')
# Relation 1-1
eleve = models.OneToOneField(Eleve, on_delete=models.CASCADE, primary_key=True)
etat = models.IntegerField(choices=EtatDossierInscription, default=EtatDossierInscription.DI_ABSENT)
dateMAJ = models.DateTimeField(auto_now=True)
notes = models.CharField(max_length=200, blank=True)
codeLienInscription = models.CharField(max_length=200, default="", blank=True)
fichierInscription = models.FileField(upload_to=settings.DOCUMENT_DIR, default="", blank=True)
di_associe = models.CharField(max_length=200, default="", blank=True)
def __str__(self):
return "FI_" + self.eleve.nom + "_" + self.eleve.prenom

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,
'fichesInscriptions': data }
)

View File

@ -0,0 +1,176 @@
from rest_framework import serializers
from GestionInscriptions.models import FicheInscription, Eleve, Responsable, Frere, Langue
from GestionLogin.models import Profil
from GestionLogin.serializers import ProfilSerializer
from GestionMessagerie.models import Messagerie
from GestionNotification.models import Notification
from N3wtSchool import settings
from django.utils import timezone
import pytz
class LanguesSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
model = Langue
fields = '__all__'
class FrereSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
model = Frere
fields = '__all__'
class ResponsableSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
profil_associe = serializers.SerializerMethodField()
class Meta:
model = Responsable
fields = '__all__'
def get_profil_associe(self, obj):
return obj.profilAssocie.email
class EleveSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
responsables = ResponsableSerializer(many=True, required=False)
freres = FrereSerializer(many=True, required=False)
langues = LanguesSerializer(many=True, required=False)
class Meta:
model = Eleve
fields = '__all__'
def get_or_create_packages(self, responsables_data):
responsables_ids = []
for responsable_data in responsables_data:
responsable_instance, created = Responsable.objects.get_or_create( id=responsable_data.get('id'),
defaults=responsable_data)
responsables_ids.append(responsable_instance.id)
return responsables_ids
def create(self, validated_data):
responsables_data = validated_data.pop('responsables', [])
freres_data = validated_data.pop('freres', [])
langues_data = validated_data.pop('languesParlees', [])
eleve = Eleve.objects.create(**validated_data)
eleve.responsables.set(self.get_or_create_packages(responsables_data))
eleve.freres.set(self.get_or_create_packages(freres_data))
eleve.languesParlees.set(self.get_or_create_packages(langues_data))
return eleve
def create_or_update_packages(self, responsables_data):
responsables_ids = []
for responsable_data in responsables_data:
responsable_instance, created = Responsable.objects.update_or_create( id=responsable_data.get('id'),
defaults=responsable_data)
responsables_ids.append(responsable_instance.id)
return responsables_ids
def update(self, instance, validated_data):
responsables_data = validated_data.pop('responsables', [])
freres_data = validated_data.pop('freres', [])
langues_data = validated_data.pop('languesParlees', [])
if responsables_data:
instance.responsables.set(self.create_or_update_packages(responsables_data))
if freres_data:
instance.freres.set(self.create_or_update_packages(freres_data))
if langues_data:
instance.freres.set(self.create_or_update_packages(langues_data))
for field in self.fields:
try:
setattr(instance, field, validated_data[field])
except KeyError:
pass
instance.save()
return instance
class FicheInscriptionSerializer(serializers.ModelSerializer):
eleve = EleveSerializer(many=False, required=True)
fichierInscription = serializers.FileField(required=False)
etat_label = serializers.SerializerMethodField()
dateMAJ_formattee = serializers.SerializerMethodField()
class Meta:
model = FicheInscription
fields = '__all__'
def create(self, validated_data):
eleve_data = validated_data.pop('eleve')
eleve = EleveSerializer.create(EleveSerializer(), eleve_data)
ficheEleve = FicheInscription.objects.create(eleve=eleve, **validated_data)
return ficheEleve
def update(self, instance, validated_data):
eleve_data = validated_data.pop('eleve')
eleve = instance.eleve
eleve_serializer = EleveSerializer.update(EleveSerializer(), eleve, eleve_data)
for field in self.fields:
try:
setattr(instance, field, validated_data[field])
except KeyError:
pass
instance.save()
return instance
def get_etat_label(self, obj):
return obj.get_etat_display()
def get_dateMAJ_formattee(self, obj):
utc_time = timezone.localtime(obj.dateMAJ) # Convertir en heure locale
local_tz = pytz.timezone(settings.TZ_APPLI)
local_time = utc_time.astimezone(local_tz)
return local_time.strftime("%d-%m-%Y %H:%M")
class EleveByParentSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
model = Eleve
fields = ['id', 'nom', 'prenom']
def __init__(self, *args, **kwargs):
super(EleveByParentSerializer , self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].required = False
class FicheInscriptionByParentSerializer(serializers.ModelSerializer):
eleve = EleveByParentSerializer(many=False, required=True)
class Meta:
model = FicheInscription
fields = ['eleve', 'etat']
def __init__(self, *args, **kwargs):
super(FicheInscriptionByParentSerializer, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].required = False
class ResponsableByDICreationSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
model = Responsable
fields = ['id', 'nom', 'prenom', 'mail', 'profilAssocie']
class EleveByDICreationSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
responsables = ResponsableByDICreationSerializer(many=True, required=False)
class Meta:
model = Eleve
fields = ['id', 'nom', 'prenom', 'responsables']
def __init__(self, *args, **kwargs):
super(EleveByDICreationSerializer , self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].required = False
class NotificationSerializer(serializers.ModelSerializer):
typeNotification_label = serializers.ReadOnlyField()
class Meta:
model = Notification
fields = '__all__'

View File

@ -0,0 +1,43 @@
from django.db.models.signals import post_save, post_delete, m2m_changed
from django.dispatch import receiver
from django.core.cache import cache
from GestionInscriptions.models import FicheInscription, Eleve, Responsable
from GestionLogin.models import Profil
from N3wtSchool import settings
from N3wtSchool.redis_client import redis_client
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}*'
print(f'pattern : {pattern}')
for key in redis_client.scan_iter(pattern):
redis_client.delete(key)
print(f'deleting : {key}')
@receiver(post_save, sender=FicheInscription)
@receiver(post_delete, sender=FicheInscription)
def clear_cache_after_change(sender, instance, **kwargs):
clear_cache()
@receiver(m2m_changed, sender=Eleve.responsables.through)
def check_orphan_reponsables(sender, **kwargs):
action = kwargs.pop('action', None)
instance = kwargs.pop('instance', None)
# pre_clear : lors de la suppression d'une FI (on fait un "clear" sur chaque relation)
if action in ('post_remove', 'post_clear'):
if instance.responsables.all():
Responsable.objects.filter(eleve=None).delete()
@receiver(m2m_changed, sender=Eleve.profils.through)
def check_orphan_profils(sender, **kwargs):
action = kwargs.pop('action', None)
instance = kwargs.pop('instance', None)
# pre_clear : lors de la suppression d'une FI (on fait un "clear" sur chaque relation)
if action in ('post_remove', 'post_clear'):
if instance.profils.all():
Profil.objects.filter(eleve=None).delete()

View File

@ -0,0 +1,44 @@
# tasks.py
from celery import shared_task
from django.utils import timezone
from GestionInscriptions.automate import Automate_DI_Inscription, updateStateMachine
from .models import FicheInscription
from GestionMessagerie.models import Messagerie
from N3wtSchool import settings, bdd
import requests
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.WARNING)
@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 = FicheInscription.objects.filter(etat=FicheInscription.EtatDossierInscription.DI_ENVOYE, dateMAJ__lt=deadline)
for dossier in dossiers_en_attente:
send_notification(dossier)
def send_notification(dossier):
print(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.profils.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 'GestionInscriptions: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 'GestionInscriptions: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 'GestionInscriptions:validate' eleve.id %}' method="post" enctype="multipart/form-data">
{% csrf_token %}
{% with responsable=eleve.getResponsablePrincipal %}
<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 'GestionInscriptions:index' %}' method="post">
{% csrf_token %}
{% with responsable=eleve.getResponsablePrincipal %}
<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 ficheInscription in ficheInscriptions_list %}
{% with eleve=ficheInscription.eleve %}
{% with responsable=eleve.getResponsablePrincipal %}
{% with fichiers=ficheInscription|recupereFichiersDossierInscription %}
<tr>
<td>{{ eleve.nom }}</td>
<td>{{ eleve.prenom }}</td>
<td>{{ responsable.mail }}</td>
<td>{{ responsable.telephone }}</td>
<td>{{ ficheInscription.dateMAJ }}</td>
<td>
{% if ficheInscription.etat == 0 %}
<span class="tag blue"> Créé</span>
{% elif ficheInscription.etat == 1 %}
<span class="tag orange"> Envoyé</span>
{% elif ficheInscription.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 'GestionInscriptions: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>{{ dateSignature }}</b> <br/>
A : <b>{{ heureSignature }}</b>
<hr class='hrItem' />
<h1>ELEVE</h1>
{% with niveau=eleve|recupereNiveauEleve %}
{% with genre=eleve|recupereGenreEleve %}
NOM : <b>{{ eleve.nom }}</b> <br/>
PRENOM : <b>{{ eleve.prenom }}</b> <br/>
ADRESSE : <b>{{ eleve.adresse }}</b> <br/>
GENRE : <b>{{ genre }}</b> <br/>
NE(E) LE : <b>{{ eleve.dateNaissance }}</b> <br/>
A : <b>{{ eleve.lieuNaissance }} ({{ eleve.codePostalNaissance }})</b> <br/>
NATIONALITE : <b>{{ eleve.nationalite }}</b> <br/>
NIVEAU : <b>{{ niveau }}</b> <br/>
MEDECIN TRAITANT : <b>{{ eleve.medecinTraitant }}</b> <br/>
{% endwith %}
{% endwith %}
<hr class='hrItem' />
<h1>RESPONSABLES</h1>
{% with responsables_List=eleve.getResponsables %}
{% with freres_List=eleve.getFreres %}
{% for responsable in responsables_List%}
<h2>Responsable {{ forloop.counter }}</h2>
NOM : <b>{{ responsable.nom }}</b> <br/>
PRENOM : <b>{{ responsable.prenom }}</b> <br/>
ADRESSE : <b>{{ responsable.adresse }}</b> <br/>
NE(E) LE : <b>{{ responsable.dateNaissance }}</b> <br/>
MAIL : <b>{{ responsable.mail }}</b> <br/>
TEL : <b>{{ responsable.telephone }}</b> <br/>
PROFESSION : <b>{{ responsable.profession }}</b> <br/>
{% endfor %}
<hr class='hrItem' />
<h1>FRATRIE</h1>
{% for frere in freres_List%}
<h2>Frère - Soeur {{ forloop.counter }}</h2>
NOM : <b>{{ frere.nom }}</b> <br/>
PRENOM : <b>{{ frere.prenom }}</b> <br/>
NE(E) LE : <b>{{ frere.dateNaissance }}</b> <br/>
{% endfor %}
<hr class='hrItem' />
<h1>MODALITES DE PAIEMENT</h1>
{% with modePaiement=eleve|recupereModePaiement %}
<b>{{ modePaiement }}</b> <br/>
{% endwith %}
{% endwith %}
{% endwith %}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,23 @@
from GestionInscriptions.models import FicheInscription, Eleve
from django import template
register = template.Library()
# @register.filter
# def recupereFichiersDossierInscription(pk):
# fichiers_list = FicheInscription.objects.filter(fiche_inscription=pk)
# return fichiers_list
@register.filter
def recupereModePaiement(pk):
ficheInscription = FicheInscription.objects.get(eleve=pk)
return Eleve.ModePaiement(int(ficheInscription.eleve.modePaiement)).label
@register.filter
def recupereNiveauEleve(pk):
ficheInscription = FicheInscription.objects.get(eleve=pk)
return Eleve.NiveauEleve(int(ficheInscription.eleve.niveau)).label
@register.filter
def recupereGenreEleve(pk):
ficheInscription = FicheInscription.objects.get(eleve=pk)
return Eleve.GenreEleve(int(ficheInscription.eleve.genre)).label

View File

@ -0,0 +1,31 @@
from django.urls import path, re_path
from . import views
from GestionInscriptions.views import ListFichesInscriptionView, FicheInscriptionView, EleveView, ResponsableView, ListeEnfantsView, ListeElevesView
urlpatterns = [
re_path(r'^fichesInscription/([a-zA-z]+)$', ListFichesInscriptionView.as_view(), name="listefichesInscriptions"),
re_path(r'^ficheInscription$', FicheInscriptionView.as_view(), name="fichesInscriptions"),
re_path(r'^ficheInscription/([0-9]+)$', FicheInscriptionView.as_view(), name="fichesInscriptions"),
# Page de formulaire d'inscription - ELEVE
re_path(r'^eleve/([0-9]+)$', EleveView.as_view(), name="eleves"),
# Page de formulaire d'inscription - RESPONSABLE
re_path(r'^recupereDernierResponsable$', ResponsableView.as_view(), name="recupereDernierResponsable"),
# Envoi d'un dossier d'inscription
re_path(r'^send/([0-9]+)$', views.send, name="send"),
# Archivage d'un dossier d'inscription
re_path(r'^archive/([0-9]+)$', views.archive, name="archive"),
# Envoi d'une relance de dossier d'inscription
re_path(r'^sendRelance/([0-9]+)$', views.relance, name="relance"),
# Page PARENT - Liste des enfants
re_path(r'^enfants/([0-9]+)$', ListeEnfantsView.as_view(), name="enfants"),
# Page INSCRIPTION - Liste des élèves
re_path(r'^eleves$', ListeElevesView.as_view(), name="enfants"),
]

View File

@ -0,0 +1,181 @@
from django.shortcuts import render,get_object_or_404,get_list_or_404
from .models import FicheInscription, Eleve, Responsable, Frere
import time
from datetime import date, datetime, timedelta
from zoneinfo import ZoneInfo
from django.conf import settings
from N3wtSchool import renderers
from N3wtSchool import bdd
from io import BytesIO
from django.core.files import File
from pathlib import Path
import os
from enum import Enum
import random
import string
from rest_framework.parsers import JSONParser
def recupereListeFichesInscription():
context = {
"ficheInscriptions_list": bdd.getAllObjects(FicheInscription),
}
return context
def recupereListeFichesInscriptionEnAttenteSEPA():
ficheInscriptionsSEPA_list = FicheInscription.objects.filter(modePaiement="Prélèvement SEPA").filter(etat=FicheInscription.EtatDossierInscription['SEPA_ENVOYE'])
return ficheInscriptionsSEPA_list
def updateEleve(eleve, inputs, erase=False):
eleve.nom = inputs["nomEleve"]
eleve.prenom = inputs["prenomEleve"]
eleve.ambiance = inputs["ambiance"]
eleve.genre = inputs["genre"]
eleve.adresse = inputs["adresseEleve"]
eleve.dateNaissance = inputs["dateNaissanceEleve"]
eleve.lieuNaissance = inputs["lieuNaissanceEleve"]
eleve.codePostalNaissance = inputs["codePostalNaissanceEleve"]
eleve.nationalite = inputs["nationaliteEleve"]
eleve.medecinTraitant = inputs["medecinTraitantEleve"]
responsable=eleve.getResponsablePrincipal()
responsable.adresse = inputs["adresseResponsable1"]
responsable.dateNaissance = inputs["dateNaissanceResponsable1"]
responsable.profession = inputs["professionResponsable1"]
responsable.save()
# Création du 2ème responsable
if inputs["nomResponsable2"] != "" and inputs["prenomResponsable2"] != "":
responsable2 = Responsable.objects.create(nom=inputs["nomResponsable2"],
prenom=inputs["prenomResponsable2"],
dateNaissance=inputs["dateNaissanceResponsable2"],
adresse=inputs["adresseResponsable2"],
mail=inputs["mailResponsable2"],
telephone=inputs["telephoneResponsable2"],
profession=inputs["professionResponsable2"])
responsable2.save()
eleve.responsables.add(responsable2)
# Création du 1er frère
if inputs["nomFrere1"] != "" and inputs["prenomFrere1"] != "":
frere1 = Frere.objects.create(nom=inputs["nomFrere1"],
prenom=inputs["prenomFrere1"],
dateNaissance=inputs["dateNaissanceFrere1"])
frere1.save()
eleve.freres.add(frere1)
# Création du 2ème frère
if inputs["nomFrere2"] != "" and inputs["prenomFrere2"] != "":
frere2 = Frere.objects.create(nom=inputs["nomFrere2"],
prenom=inputs["prenomFrere2"],
dateNaissance=inputs["dateNaissanceFrere2"])
frere2.save()
eleve.freres.add(frere2)
eleve.save()
def _now():
return datetime.now(ZoneInfo(settings.TZ_APPLI))
def convertToStr(dateValue, dateFormat):
return dateValue.strftime(dateFormat)
def convertToDate(date_time):
format = '%d-%m-%Y %H:%M'
datetime_str = datetime.strptime(date_time, format)
return datetime_str
def convertTelephone(telephoneValue, separator='-'):
return f"{telephoneValue[:2]}{separator}{telephoneValue[2:4]}{separator}{telephoneValue[4:6]}{separator}{telephoneValue[6:8]}{separator}{telephoneValue[8:10]}"
def generePDF(ficheEleve):
data = {
'pdf_title': "Dossier d'inscription de %s"%ficheEleve.eleve.prenom,
'dateSignature': convertToStr(_now(), '%d-%m-%Y'),
'heureSignature': convertToStr(_now(), '%H:%M'),
'eleve':ficheEleve.eleve,
}
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
nomFichierPDF = "Dossier_Inscription_%s_%s.pdf"%(ficheEleve.eleve.nom, ficheEleve.eleve.prenom)
pathFichier = Path(settings.DOCUMENT_DIR + "/" + nomFichierPDF)
if os.path.exists(str(pathFichier)):
os.remove(str(pathFichier))
receipt_file = BytesIO(pdf.content)
# fichier = Fichier.objects.create(fiche_inscription=ficheEleve)
# fichier.document = File(receipt_file, nomFichierPDF)
# fichier.save()
def genereRandomCode(length):
return ''.join(random.choice(string.ascii_letters) for i in range(length))
def calculeDatePeremption(_start, nbDays):
return convertToStr(_start + timedelta(days=nbDays), settings.DATE_FORMAT)
# Fonction permettant de retourner la valeur du QueryDict
# QueryDict [ index ] -> Dernière valeur d'une liste
# dict (QueryDict [ index ]) -> Toutes les valeurs de la liste
def _(liste):
return liste[0]
def toNewEleveJSONRequest(jsonOrigin):
etat=FicheInscription.EtatDossierInscription.DI_CREE
telephone = convertTelephone(_(jsonOrigin['telephoneResponsable']))
finalJSON = {
"eleve":
{
"nom" : _(jsonOrigin['nomEleve']),
"prenom" : _(jsonOrigin['prenomEleve']),
"responsables" : [
{
"nom" : _(jsonOrigin['nomResponsable']),
"prenom" : _(jsonOrigin['prenomResponsable']),
"mail" : _(jsonOrigin['mailResponsable']),
"telephone" : telephone
}
],
"profils" : [
],
},
"etat": str(etat),
"dateMAJ": str(convertToStr(_now(), '%d-%m-%Y %H:%M')),
}
print(finalJSON)
return finalJSON
def toEditEleveJSONRequest(jsonOrigin):
telephone = convertTelephone(_(jsonOrigin['telephoneResponsable']), '.')
finalJSON = {
"eleve":
{
"id" : _(jsonOrigin['fiche_id']),
"nom" : _(jsonOrigin['nomEleve']),
"prenom" : _(jsonOrigin['prenomEleve']),
"responsables" : [
{
"id" : _(jsonOrigin['responsable_id']),
"nom" : _(jsonOrigin['nomResponsable']),
"prenom" : _(jsonOrigin['prenomResponsable']),
"mail" : _(jsonOrigin['mailResponsable']),
"telephone" : telephone
}
],
"profils" : [
],
},
"dateMAJ": str(convertToStr(_now(), '%d-%m-%Y %H:%M')),
}
print(finalJSON)
return finalJSON
def getArgFromRequest(_argument, _request):
resultat = None
data=JSONParser().parse(_request)
resultat = data[_argument]
return resultat

View File

@ -0,0 +1,289 @@
from django.http.response import JsonResponse
from django.contrib.auth import login, authenticate, get_user_model
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
from django.utils.decorators import method_decorator
from django.core.cache import cache
from django.core.paginator import Paginator
from django.core.files import File
from django.db.models import Q # Ajout de cet import
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from rest_framework import status
import json
from pathlib import Path
import os
from io import BytesIO
import GestionInscriptions.mailManager as mailer
import GestionInscriptions.util as util
from GestionInscriptions.serializers import FicheInscriptionSerializer, EleveSerializer, FicheInscriptionByParentSerializer, EleveByDICreationSerializer
from GestionInscriptions.pagination import CustomPagination
from GestionInscriptions.signals import clear_cache
from .models import Eleve, Responsable, FicheInscription
from GestionInscriptions.automate import Automate_DI_Inscription, load_config, getStateMachineObjectState, updateStateMachine
from GestionLogin.models import Profil
from N3wtSchool import settings, renderers, bdd
class ListFichesInscriptionView(APIView):
pagination_class = CustomPagination
def get(self, request, _filter):
if _filter == 'all':
# Récupération des paramètres
search = request.GET.get('search', '').strip()
page_size = request.GET.get('page_size', None)
# Gestion du page_size
if page_size is not None:
try:
page_size = int(page_size)
except ValueError:
page_size = settings.NB_RESULT_PER_PAGE
cached_page_size = cache.get('N3WT_page_size')
if cached_page_size != page_size:
clear_cache()
cache.set('N3WT_page_size', page_size)
# Gestion du cache
page_number = request.GET.get('page', 1)
cache_key = f'N3WT_ficheInscriptions_page_{page_number}_search_{search}'
cached_page = cache.get(cache_key)
if cached_page:
return JsonResponse(cached_page, safe=False)
# Filtrage des résultats
if search:
# Utiliser la nouvelle fonction de recherche
ficheInscriptions_List = bdd.searchObjects(
FicheInscription,
search,
_excludeState=6 # Exclure les fiches archivées
)
else:
# Récupère toutes les fiches non archivées
ficheInscriptions_List = bdd.getObjects(FicheInscription, 'etat', 6, _reverseCondition=True)
# Pagination
paginator = self.pagination_class()
page = paginator.paginate_queryset(ficheInscriptions_List, request)
if page is not None:
ficheInscriptions_serializer = FicheInscriptionSerializer(page, many=True)
response_data = paginator.get_paginated_response(ficheInscriptions_serializer.data)
cache.set(cache_key, response_data, timeout=60*15)
return JsonResponse(response_data, safe=False)
elif _filter == 'archived' :
page_size = request.GET.get('page_size', None)
if page_size is not None:
try:
page_size = int(page_size)
except ValueError:
page_size = settings.NB_RESULT_PER_PAGE
cached_page_size = cache.get('N3WT_archived_page_size')
# Comparer avec le nouveau page_size
if cached_page_size != page_size:
# Appeler cached_page() et mettre à jour le cache
clear_cache()
cache.set('N3WT_archived_page_size',page_size)
page_number = request.GET.get('page', 1)
cache_key_page = f'N3WT_ficheInscriptions_archives_page_{page_number}'
cached_page = cache.get(cache_key_page)
if cached_page:
return JsonResponse(cached_page, safe=False)
ficheInscriptions_List=bdd.getObjects(FicheInscription, 'etat', 6)
paginator = self.pagination_class()
page = paginator.paginate_queryset(ficheInscriptions_List, request)
if page is not None:
ficheInscriptions_serializer = FicheInscriptionSerializer(page, many=True)
response_data = paginator.get_paginated_response(ficheInscriptions_serializer.data)
cache.set(cache_key_page, response_data, timeout=60*15)
return JsonResponse(response_data, safe=False)
return JsonResponse(status=status.HTTP_404_NOT_FOUND)
def post(self, request):
fichesEleve_data=JSONParser().parse(request)
for ficheEleve_data in fichesEleve_data:
# Ajout de la date de mise à jour
ficheEleve_data["dateMAJ"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
json.dumps(ficheEleve_data)
# Ajout du code d'inscription
code = util.genereRandomCode(12)
ficheEleve_data["codeLienInscription"] = code
ficheEleve_serializer = FicheInscriptionSerializer(data=ficheEleve_data)
if ficheEleve_serializer.is_valid():
ficheEleve_serializer.save()
return JsonResponse(ficheEleve_serializer.errors, safe=False)
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(ensure_csrf_cookie, name='dispatch')
class FicheInscriptionView(APIView):
pagination_class = CustomPagination
def get(self, request, _id):
ficheInscription=bdd.getObject(FicheInscription, "eleve__id", _id)
fiche_serializer=FicheInscriptionSerializer(ficheInscription)
return JsonResponse(fiche_serializer.data, safe=False)
def post(self, request):
ficheEleve_data=JSONParser().parse(request)
# Ajout de la date de mise à jour
ficheEleve_data["dateMAJ"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
json.dumps(ficheEleve_data)
# Ajout du code d'inscription
code = util.genereRandomCode(12)
ficheEleve_data["codeLienInscription"] = code
responsablesId = ficheEleve_data.pop('idResponsables', [])
ficheEleve_serializer = FicheInscriptionSerializer(data=ficheEleve_data)
if ficheEleve_serializer.is_valid():
di = ficheEleve_serializer.save()
# Mise à jour de l'automate
updateStateMachine(di, 'creationDI')
# Récupération du reponsable associé
for responsableId in responsablesId:
responsable = Responsable.objects.get(id=responsableId)
di.eleve.responsables.add(responsable)
di.save()
ficheInscriptions_List=bdd.getAllObjects(FicheInscription)
return JsonResponse({'totalInscrits':len(ficheInscriptions_List)}, safe=False)
return JsonResponse(ficheEleve_serializer.errors, safe=False)
def put(self, request, id):
ficheEleve_data=JSONParser().parse(request)
admin = ficheEleve_data.pop('admin', 1)
ficheEleve_data["dateMAJ"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'))
ficheEleve = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
currentState = getStateMachineObjectState(ficheEleve.etat)
if admin == 0 and currentState == FicheInscription.EtatDossierInscription.DI_ENVOYE:
json.dumps(ficheEleve_data)
# Ajout du fichier d'inscriptions
data = {
'pdf_title': "Dossier d'inscription de %s"%ficheEleve.eleve.prenom,
'dateSignature': util.convertToStr(util._now(), '%d-%m-%Y'),
'heureSignature': util.convertToStr(util._now(), '%H:%M'),
'eleve':ficheEleve.eleve,
}
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
nomFichierPDF = "Dossier_Inscription_%s_%s.pdf"%(ficheEleve.eleve.nom, ficheEleve.eleve.prenom)
pathFichier = Path(settings.DOCUMENT_DIR + "/" + nomFichierPDF)
if os.path.exists(str(pathFichier)):
print(f'File exists : {str(pathFichier)}')
os.remove(str(pathFichier))
receipt_file = BytesIO(pdf.content)
ficheEleve.fichierInscription = File(receipt_file, nomFichierPDF)
# Mise à jour de l'automate
updateStateMachine(di, 'saisiDI')
ficheEleve_serializer = FicheInscriptionSerializer(ficheEleve, data=ficheEleve_data)
if ficheEleve_serializer.is_valid():
di = ficheEleve_serializer.save()
return JsonResponse("Updated Successfully", safe=False)
return JsonResponse(ficheEleve_serializer.errors, safe=False)
def delete(self, request, id):
fiche_inscription = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
if fiche_inscription != None:
eleve = fiche_inscription.eleve
eleve.responsables.clear()
eleve.profils.clear()
eleve.delete()
clear_cache()
return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False)
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
class EleveView(APIView):
def get(self, request, _id):
eleve = bdd.getObject(_objectName=Eleve, _columnName='id', _value=_id)
eleve_serializer = EleveSerializer(eleve)
return JsonResponse(eleve_serializer.data, safe=False)
class ResponsableView(APIView):
def get(self, request):
lastResponsable = bdd.getLastId(Responsable)
return JsonResponse({"lastid":lastResponsable}, safe=False)
def send(request, id):
fiche_inscription = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
if fiche_inscription != None:
eleve = fiche_inscription.eleve
responsable = eleve.getResponsablePrincipal()
mail = responsable.mail
errorMessage = mailer.envoieDossierInscription(mail)
if errorMessage == '':
fiche_inscription.dateMAJ=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
# Mise à jour de l'automate
updateStateMachine(fiche_inscription, 'envoiDI')
return JsonResponse({"errorMessage":errorMessage}, safe=False)
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
def archive(request, id):
fiche_inscription = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
if fiche_inscription != None:
fiche_inscription.dateMAJ=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
# Mise à jour de l'automate
updateStateMachine(fiche_inscription, 'archiveDI')
return JsonResponse({"errorMessage":''}, safe=False)
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
def relance(request, id):
fiche_inscription = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
if fiche_inscription != None:
eleve = fiche_inscription.eleve
responsable = eleve.getResponsablePrincipal()
mail = responsable.mail
errorMessage = mailer.envoieRelanceDossierInscription(mail, fiche_inscription.codeLienInscription)
if errorMessage == '':
fiche_inscription.etat=FicheInscription.EtatDossierInscription.DI_ENVOYE
fiche_inscription.dateMAJ=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
fiche_inscription.save()
return JsonResponse({"errorMessage":errorMessage}, safe=False)
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False)
# API utilisée pour la vue parent
class ListeEnfantsView(APIView):
# Récupération des élèves d'un parent
# idProfile : identifiant du profil connecté rattaché aux fiches d'élèves
def get(self, request, _idProfile):
students = bdd.getObjects(_objectName=FicheInscription, _columnName='eleve__responsables__profilAssocie__id', _value=_idProfile)
students_serializer = FicheInscriptionByParentSerializer(students, many=True)
return JsonResponse(students_serializer.data, safe=False)
# API utilisée pour la vue de création d'un DI
class ListeElevesView(APIView):
# Récupération de la liste des élèves inscrits ou en cours d'inscriptions
def get(self, request):
students = bdd.getAllObjects(_objectName=Eleve)
students_serializer = EleveByDICreationSerializer(students, many=True)
return JsonResponse(students_serializer.data, safe=False)