feat: Envoi mail d'inscription aux enseignants [N3WTS-1]

This commit is contained in:
N3WT DE COMPET
2026-02-15 16:37:43 +01:00
parent 176edc5c45
commit bd7dc2b0c2
5 changed files with 106 additions and 38 deletions

View File

@ -25,7 +25,7 @@ class ProfileRole(models.Model):
profile = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name='roles') profile = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name='roles')
role_type = models.IntegerField(choices=RoleType.choices, default=RoleType.PROFIL_UNDEFINED) role_type = models.IntegerField(choices=RoleType.choices, default=RoleType.PROFIL_UNDEFINED)
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='profile_roles') establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='profile_roles')
is_active = models.BooleanField(default=False) is_active = models.BooleanField(default=False, blank=True)
updated_date = models.DateTimeField(auto_now=True) updated_date = models.DateTimeField(auto_now=True)
def __str__(self): def __str__(self):

View File

@ -1,4 +1,4 @@
from django.core.mail import send_mail, get_connection, EmailMultiAlternatives, EmailMessage from django.core.mail import get_connection, EmailMultiAlternatives, EmailMessage
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.conf import settings from django.conf import settings
@ -207,4 +207,22 @@ def isValid(message, fiche_inscription):
responsable = eleve.getMainGuardian() responsable = eleve.getMainGuardian()
mailReponsableAVerifier = responsable.mail mailReponsableAVerifier = responsable.mail
return responsableMail == mailReponsableAVerifier and str(idMail) == str(fiche_inscription.eleve.id) return responsableMail == mailReponsableAVerifier and str(idMail) == str(fiche_inscription.eleve.id)
def sendRegisterTeacher(recipients, establishment_id):
errorMessage = ''
try:
EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Bienvenue sur N3wt School (Enseignant)'
context = {
'BASE_URL': settings.BASE_URL,
'URL_DJANGO': settings.URL_DJANGO,
'email': recipients,
'establishment': establishment_id
}
connection = getConnection(establishment_id)
subject = EMAIL_INSCRIPTION_SUBJECT
html_message = render_to_string('emails/inscription_teacher.html', context)
sendMail(subject=subject, message=html_message, recipients=recipients, connection=connection)
except Exception as e:
errorMessage = str(e)
return errorMessage

View File

@ -35,6 +35,7 @@ from collections import defaultdict
from Subscriptions.models import Student, StudentCompetency from Subscriptions.models import Student, StudentCompetency
from Subscriptions.util import getCurrentSchoolYear from Subscriptions.util import getCurrentSchoolYear
import logging import logging
from N3wtSchool.mailManager import sendRegisterForm, sendRegisterTeacher
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -102,8 +103,17 @@ class TeacherListCreateView(APIView):
teacher_serializer = TeacherSerializer(data=teacher_data) teacher_serializer = TeacherSerializer(data=teacher_data)
if teacher_serializer.is_valid(): if teacher_serializer.is_valid():
teacher_serializer.save() teacher_instance = teacher_serializer.save()
# Envoi du mail d'inscription enseignant uniquement à la création
email = None
establishment_id = None
if hasattr(teacher_instance, "profile_role") and teacher_instance.profile_role:
if hasattr(teacher_instance.profile_role, "profile") and teacher_instance.profile_role.profile:
email = teacher_instance.profile_role.profile.email
if hasattr(teacher_instance.profile_role, "establishment") and teacher_instance.profile_role.establishment:
establishment_id = teacher_instance.profile_role.establishment.id
if email and establishment_id:
sendRegisterTeacher(email, establishment_id)
return JsonResponse(teacher_serializer.data, safe=False) return JsonResponse(teacher_serializer.data, safe=False)
return JsonResponse(teacher_serializer.errors, safe=False) return JsonResponse(teacher_serializer.errors, safe=False)

View File

@ -0,0 +1,63 @@
<!-- Nouveau template pour l'inscription d'un enseignant -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Bienvenue sur N3wt School</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background-color: #f4f4f4;
padding: 10px;
text-align: center;
}
.content {
padding: 20px;
}
.footer {
font-size: 12px;
text-align: center;
margin-top: 30px;
color: #777;
}
.logo {
width: 120px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<!-- Utilisation d'un lien absolu pour le logo -->
<img src="{{URL_DJANGO}}/static/img/logo_min.svg" alt="Logo N3wt School" class="logo" style="display:block;margin:auto;" />
<h1>Bienvenue sur N3wt School</h1>
</div>
<div class="content">
<p>Bonjour,</p>
<p>Votre compte enseignant a été créé sur la plateforme N3wt School.</p>
<p>Pour accéder à votre espace personnel, veuillez vous connecter à l'adresse suivante :<br>
<a href="{{BASE_URL}}/users/login">{{BASE_URL}}/users/login</a>
</p>
<p>Votre identifiant est : <b>{{ email }}</b></p>
<p>Si c'est votre première connexion, veuillez activer votre compte ici :<br>
<a href="{{BASE_URL}}/users/subscribe?establishment_id={{establishment}}">{{BASE_URL}}/users/subscribe</a>
</p>
<p>Nous vous souhaitons une excellente prise en main de l'outil.<br>
L'équipe N3wt School reste à votre disposition pour toute question.</p>
</div>
<div class="footer">
<p>Ce message est généré automatiquement, merci de ne pas y répondre.</p>
</div>
</div>
</body>
</html>

View File

@ -140,38 +140,19 @@ const TeachersSection = ({
const [removePopupMessage, setRemovePopupMessage] = useState(''); const [removePopupMessage, setRemovePopupMessage] = useState('');
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {}); const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
const [confirmPopupVisible, setConfirmPopupVisible] = useState(false);
const [confirmPopupMessage, setConfirmPopupMessage] = useState('');
const [confirmPopupOnConfirm, setConfirmPopupOnConfirm] = useState(() => {});
const { selectedEstablishmentId } = useEstablishment(); const { selectedEstablishmentId } = useEstablishment();
// --- UTILS --- // --- UTILS ---
// Retourne le profil existant pour un email, utilisé par un teacher actif ou le teacher en cours d'édition // Retourne le profil existant pour un email
const getUsedProfileForEmail = (email, teacherId = null) => { const getUsedProfileForEmail = (email) => {
const usedProfileIds = new Set( // On cherche tous les profils dont l'email correspond
teachers.map(t => t.profile_role && t.profile).filter(Boolean) const matchingProfiles = profiles.filter(p => p.email === email);
);
// Ajoute le profil du teacher en cours d'édition si besoin // On retourne le premier profil correspondant (ou undefined)
if (teacherId) { const result = matchingProfiles.length > 0 ? matchingProfiles[0] : undefined;
const currentTeacher = teachers.find(t => t.id === teacherId);
if (currentTeacher && currentTeacher.profile_role && currentTeacher.profile) { return result;
const profileObj = profiles.find(p => p.id === currentTeacher.profile);
if (profileObj && profileObj.email === email) {
usedProfileIds.add(profileObj.id);
}
} else {
// Cas création immédiate : on cherche le profil par email dans profiles
const profileObj = profiles.find(p => p.email === email);
if (profileObj) {
usedProfileIds.add(profileObj.id);
}
}
}
return profiles.find(
(profile) => profile.email === email && usedProfileIds.has(profile.id)
);
}; };
// Met à jour le formData et newTeacher si besoin // Met à jour le formData et newTeacher si besoin
@ -189,7 +170,7 @@ const TeachersSection = ({
const handleEmailChange = (e) => { const handleEmailChange = (e) => {
const email = e.target.value; const email = e.target.value;
const existingProfile = getUsedProfileForEmail(email, editingTeacher); const existingProfile = getUsedProfileForEmail(email);
if (existingProfile) { if (existingProfile) {
logger.info(`Adresse email déjà utilisée pour le profil ${existingProfile.id}`); logger.info(`Adresse email déjà utilisée pour le profil ${existingProfile.id}`);
@ -290,9 +271,6 @@ const TeachersSection = ({
}; };
const handleUpdateTeacher = (id, updatedData) => { const handleUpdateTeacher = (id, updatedData) => {
// Simplification : le profil est forcément existant, on utilise directement existingProfileId du formData
const currentTeacher = teachers.find((teacher) => teacher.id === id);
if ( if (
updatedData.last_name && updatedData.last_name &&
updatedData.first_name && updatedData.first_name &&
@ -302,7 +280,6 @@ const TeachersSection = ({
id: updatedData.profile_role, id: updatedData.profile_role,
establishment: selectedEstablishmentId, establishment: selectedEstablishmentId,
role_type: updatedData.role_type || 0, role_type: updatedData.role_type || 0,
is_active: true,
profile: updatedData.existingProfileId, profile: updatedData.existingProfileId,
}; };