Remove exam fields on Student (replaced by Examination)

This commit is contained in:
Claude Paroz 2020-04-21 15:29:28 +02:00
parent 1b0b759c59
commit fa519cddff
10 changed files with 189 additions and 160 deletions

View file

@ -131,8 +131,8 @@ class ExaminationInline(admin.StackedInline):
'<a class="button" href="{}">Courrier pour lexpert</a>&nbsp;'
'<a class="button" href="{}">Mail convocation soutenance</a>&nbsp;'
'<a class="button" href="{}">Indemnité au mentor</a>',
reverse('print-expert-compens-ede', args=[obj.student.pk]),
reverse('student-ede-convocation', args=[obj.student.pk]),
reverse('print-expert-compens-ede', args=[obj.pk]),
reverse('student-ede-convocation', args=[obj.pk]),
reverse('print-mentor-compens-ede', args=[obj.student.pk]),
)
elif obj and obj.student.is_eds_3():
@ -143,8 +143,8 @@ class ExaminationInline(admin.StackedInline):
'<a class="button" href="{}">Courrier pour lexpert</a>&nbsp;'
'<a class="button" href="{}">Mail convocation soutenance</a>&nbsp;'
'<a class="button" href="{}">Indemnité au mentor</a>',
reverse('print-expert-compens-eds', args=[obj.student.pk]),
reverse('student-eds-convocation', args=[obj.student.pk]),
reverse('print-expert-compens-eds', args=[obj.pk]),
reverse('student-eds-convocation', args=[obj.pk]),
reverse('print-mentor-compens-ede', args=[obj.student.pk]),
)
else:
@ -157,7 +157,7 @@ class StudentAdmin(admin.ModelAdmin):
ordering = ('last_name', 'first_name')
list_filter = (('archived', ArchivedListFilter), ('klass', KlassRelatedListFilter))
search_fields = ('last_name', 'first_name', 'pcode', 'city', 'klass__name')
autocomplete_fields = ('corporation', 'instructor', 'supervisor', 'mentor', 'expert', 'expert_ep')
autocomplete_fields = ('corporation', 'instructor', 'supervisor', 'mentor')
readonly_fields = ('report_sem1_sent', 'report_sem2_sent')
fieldsets = [
(None, {

View file

@ -0,0 +1,84 @@
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('stages', '0028_remove_last_appointment'),
]
operations = [
migrations.AlterModelOptions(
name='examedesession',
options={'ordering': ['-year', 'season'], 'verbose_name': 'Session dexamen'},
),
migrations.RemoveField(
model_name='student',
name='date_confirm_ep_received',
),
migrations.RemoveField(
model_name='student',
name='date_confirm_received',
),
migrations.RemoveField(
model_name='student',
name='date_exam',
),
migrations.RemoveField(
model_name='student',
name='date_exam_ep',
),
migrations.RemoveField(
model_name='student',
name='date_soutenance_ep_mailed',
),
migrations.RemoveField(
model_name='student',
name='date_soutenance_mailed',
),
migrations.RemoveField(
model_name='student',
name='expert_ep',
),
migrations.RemoveField(
model_name='student',
name='internal_expert',
),
migrations.RemoveField(
model_name='student',
name='internal_expert_ep',
),
migrations.RemoveField(
model_name='student',
name='mark',
),
migrations.RemoveField(
model_name='student',
name='mark_acq',
),
migrations.RemoveField(
model_name='student',
name='mark_ep',
),
migrations.RemoveField(
model_name='student',
name='mark_ep_acq',
),
migrations.RemoveField(
model_name='student',
name='room',
),
migrations.RemoveField(
model_name='student',
name='room_ep',
),
migrations.RemoveField(
model_name='student',
name='session',
),
migrations.RemoveField(
model_name='student',
name='session_ep',
),
]

View file

@ -252,7 +252,8 @@ class ExamEDESession(models.Model):
season = models.CharField('saison', max_length=10)
class Meta:
verbose_name = "Session dexamen EDE"
verbose_name = "Session dexamen"
ordering = ['-year', 'season']
def __str__(self):
return '{0} {1}'.format(self.year, self.season)
@ -315,36 +316,6 @@ class Student(models.Model):
on_delete=models.SET_NULL, verbose_name='Référent de PP')
referent = models.ForeignKey(Teacher, null=True, blank=True, related_name='rel_referent',
on_delete=models.SET_NULL, verbose_name='Référent avant-projet')
internal_expert = models.ForeignKey(Teacher, related_name='rel_internal_expert', verbose_name='Expert interne',
null=True, blank=True, on_delete=models.SET_NULL)
session = models.ForeignKey(
ExamEDESession, null=True, blank=True, on_delete=models.SET_NULL, related_name='students_es',
)
date_exam = models.DateTimeField(blank=True, null=True)
room = models.CharField('Salle', max_length=15, blank=True)
mark = models.DecimalField('Note', max_digits=3, decimal_places=2, blank=True, null=True)
mark_acq = models.CharField('Note', max_length=5, choices=ACQ_MARK_CHOICES, blank=True)
date_soutenance_mailed = models.DateTimeField("Convoc. env.", blank=True, null=True)
date_confirm_received = models.DateTimeField("Récept. confirm", blank=True, null=True)
# ============== Fields for Entretien professionnel =========
session_ep = models.ForeignKey(
ExamEDESession, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='Session EP',
related_name='students_ep',
)
date_exam_ep = models.DateTimeField("Date exam. EP", blank=True, null=True)
room_ep = models.CharField('Salle EP', max_length=15, blank=True)
mark_ep = models.DecimalField('Note EP', max_digits=3, decimal_places=2, blank=True, null=True)
mark_ep_acq = models.CharField('Note EP', max_length=5, choices=ACQ_MARK_CHOICES, blank=True)
internal_expert_ep = models.ForeignKey(
Teacher, related_name='rel_internal_expert_ep', verbose_name='Expert interne EP',
null=True, blank=True, on_delete=models.SET_NULL
)
expert_ep = models.ForeignKey(
'CorpContact', related_name='rel_expert_ep', verbose_name='Expert externe EP',
null=True, blank=True, on_delete=models.SET_NULL
)
date_soutenance_ep_mailed = models.DateTimeField("Convoc. env.", blank=True, null=True)
date_confirm_ep_received = models.DateTimeField("Récept. confirm", blank=True, null=True)
# ===============
mc_comment = models.TextField("Commentaires", blank=True)
@ -411,30 +382,6 @@ class Student(models.Model):
def is_eds_3(self):
return self.klass and self.klass.section.name == 'EDS' and self.klass.level.name == '3'
def missing_examination_data(self):
missing = []
if not self.date_exam:
missing.append("La date dexamen est manquante")
if not self.room:
missing.append("La salle dexamen nest pas définie")
if not self.expert:
missing.append("Lexpert externe nest pas défini")
if not self.internal_expert:
missing.append("Lexpert interne nest pas défini")
return missing
def missing_examination_ep_data(self):
missing = []
if not self.date_exam_ep:
missing.append("La date dexamen est manquante")
if not self.room_ep:
missing.append("La salle dexamen nest pas définie")
if not self.expert_ep:
missing.append("Lexpert externe nest pas défini")
if not self.internal_expert_ep:
missing.append("Lexpert interne nest pas défini")
return missing
class Examination(models.Model):
ACQ_MARK_CHOICES = (

View file

@ -335,7 +335,7 @@ class CompensationForm:
self.story.append(t)
self.story.append(Spacer(0, 0.5 * cm))
def add_accounting_stamp(self, mandat=None):
def add_accounting_stamp(self, student, mandat=None):
account = otp = total = ''
if mandat == self.EXPERT_MANDAT:
account = self.EXPERT_ACCOUNT
@ -343,9 +343,9 @@ class CompensationForm:
account = self.MENTOR_ACCOUNT
total = '500.-'
if self.student.klass.is_Ede_pe():
if student.klass.is_Ede_pe():
otp = self.OTP_EDE_PE_OTP
elif self.student.klass.is_Ede_ps():
elif student.klass.is_Ede_ps():
otp = self.OTP_EDE_PS_OTP
self.story.append((Paragraph(self.points * 2, style_normal)))
@ -433,8 +433,8 @@ class ExpertEdeLetterPdf(CompensationForm, EpcBaseDocTemplate):
<br/><br/><br/>
"""
def __init__(self, out, student):
self.student = student
def __init__(self, out, exam):
self.exam = exam
super().__init__(out)
self.addPageTemplates([
PageTemplate(id='FirstPage', frames=[self.page_frame], onPage=self.header),
@ -443,10 +443,10 @@ class ExpertEdeLetterPdf(CompensationForm, EpcBaseDocTemplate):
def exam_data(self):
return {
'expert': self.student.expert,
'internal_expert': self.student.internal_expert,
'date_exam': self.student.date_exam,
'room': self.student.room,
'expert': self.exam.external_expert,
'internal_expert': self.exam.internal_expert,
'date_exam': self.exam.date_exam,
'room': self.exam.room,
}
def produce(self):
@ -471,7 +471,7 @@ class ExpertEdeLetterPdf(CompensationForm, EpcBaseDocTemplate):
title_lower=self.title.lower(),
expert_civility=exam_data['expert'].civility,
expert_accord=exam_data['expert'].adjective_ending,
student_civility_full_name=self.student.civility_full_name,
student_civility_full_name=self.exam.student.civility_full_name,
), style_normal))
date_text = "<br/>{0} à l'Ecole Santé-social Pierre-Coullery, salle {1}<br/><br/>"
@ -507,7 +507,7 @@ class ExpertEdeLetterPdf(CompensationForm, EpcBaseDocTemplate):
self.story.append(Paragraph(
"Mandat: Soutenance de {0} {1}, classe {2}".format(
self.student.civility, self.student.full_name, self.student.klass
self.exam.student.civility, self.exam.student.full_name, self.exam.student.klass
), style_normal
))
self.story.append(Paragraph(
@ -515,7 +515,7 @@ class ExpertEdeLetterPdf(CompensationForm, EpcBaseDocTemplate):
))
self.story.append(Spacer(0, 2 * cm))
self.add_accounting_stamp(self.EXPERT_MANDAT)
self.add_accounting_stamp(self.exam.student, self.EXPERT_MANDAT)
self.build(self.story)
@ -556,7 +556,7 @@ class MentorCompensationPdfForm(CompensationForm, EpcBaseDocTemplate):
))
self.story.append(Spacer(0, 3 * cm))
self.add_accounting_stamp(self.MENTOR_MANDAT)
self.add_accounting_stamp(self.student, self.MENTOR_MANDAT)
self.build(self.story)

View file

@ -12,7 +12,7 @@ from django.utils.html import escape
from candidats.models import Candidate
from .models import (
Level, Domain, Section, Klass, Option, Period, Student, Corporation, Availability,
CorpContact, Teacher, Training, Course,
CorpContact, Teacher, Training, Course, Examination, ExamEDESession,
)
from .utils import school_year
@ -211,23 +211,24 @@ class StagesTests(TestCase):
def test_send_ede_convocation(self):
st = Student.objects.get(first_name="Albin")
exam = Examination.objects.create(student=st, session=ExamEDESession.objects.create(year=2020, season='1'))
self.client.login(username='me', password='mepassword')
url = reverse('student-ede-convocation', args=[st.pk])
url = reverse('student-ede-convocation', args=[exam.pk])
response = self.client.get(url, follow=True)
for err in ("La date dexamen est manquante",
"La salle dexamen nest pas définie",
"Lexpert externe nest pas défini",
"Lexpert interne nest pas défini"):
self.assertContains(response, err)
st.date_exam = datetime(2018, 6, 28, 12, 00)
st.room = "B123"
st.expert = CorpContact.objects.get(last_name="Horner")
st.internal_expert = Teacher.objects.get(last_name="Caux")
st.save()
exam.date_exam = datetime(2018, 6, 28, 12, 00)
exam.room = "B123"
exam.external_expert = CorpContact.objects.get(last_name="Horner")
exam.internal_expert = Teacher.objects.get(last_name="Caux")
exam.save()
response = self.client.get(url, follow=True)
self.assertContains(response, "Lexpert externe na pas de courriel valide !")
st.expert.email = "horner@example.org"
st.expert.save()
exam.external_expert.email = "horner@example.org"
exam.external_expert.save()
response = self.client.get(url)
expected_message = """ Albin Dupond,
Madame Julie Caux,
@ -261,8 +262,8 @@ tél. 032 886 33 00
'sender': 'me@example.org',
})
self.assertEqual(len(mail.outbox), 1)
st.refresh_from_db()
self.assertIsNotNone(st.date_soutenance_mailed)
exam.refresh_from_db()
self.assertIsNotNone(exam.date_soutenance_mailed)
def test_send_eds_convocation(self):
klass = Klass.objects.create(
@ -272,9 +273,10 @@ tél. 032 886 33 00
first_name="Laurent", last_name="Hots", birth_date="1994-07-12",
pcode="2000", city="Neuchâtel", klass=klass
)
exam = Examination.objects.create(student=st, session=ExamEDESession.objects.create(year=2020, season='1'))
self.client.login(username='me', password='mepassword')
url = reverse('student-eds-convocation', args=[st.pk])
url = reverse('student-eds-convocation', args=[exam.pk])
response = self.client.get(url, follow=True)
for err in ("Létudiant-e na pas de courriel valide",
"La date dexamen est manquante",
@ -283,15 +285,16 @@ tél. 032 886 33 00
"Lexpert interne nest pas défini"):
self.assertContains(response, err)
st.email = 'hots@example.org'
st.date_exam = datetime(2018, 6, 28, 12, 00)
st.room = "B123"
st.expert = CorpContact.objects.get(last_name="Horner")
st.internal_expert = Teacher.objects.get(last_name="Caux")
st.save()
exam.date_exam = datetime(2018, 6, 28, 12, 00)
exam.room = "B123"
exam.external_expert = CorpContact.objects.get(last_name="Horner")
exam.internal_expert = Teacher.objects.get(last_name="Caux")
exam.save()
response = self.client.get(url, follow=True)
self.assertContains(response, "Lexpert externe na pas de courriel valide !")
st.expert.email = "horner@example.org"
st.expert.save()
exam.external_expert.email = "horner@example.org"
exam.external_expert.save()
response = self.client.get(url)
expected_message = """ Laurent Hots,
Madame Julie Caux,
@ -325,21 +328,22 @@ tél. 032 886 33 00
'sender': 'me@example.org',
})
self.assertEqual(len(mail.outbox), 1)
st.refresh_from_db()
self.assertIsNotNone(st.date_soutenance_mailed)
exam.refresh_from_db()
self.assertIsNotNone(exam.date_soutenance_mailed)
def test_print_ede_compensation_forms(self):
st = Student.objects.get(first_name="Albin")
url = reverse('print-expert-compens-ede', args=[st.pk])
exam = Examination.objects.create(student=st, session=ExamEDESession.objects.create(year=2020, season='1'))
url = reverse('print-expert-compens-ede', args=[exam.pk])
self.client.login(username='me', password='mepassword')
response = self.client.get(url, follow=True)
self.assertContains(response, "Toutes les informations ne sont pas disponibles")
st.expert = CorpContact.objects.get(last_name="Horner")
st.internal_expert = Teacher.objects.get(last_name="Caux")
st.date_exam = datetime(2018, 6, 28, 12, 00)
st.room = "B123"
st.save()
exam.external_expert = CorpContact.objects.get(last_name="Horner")
exam.internal_expert = Teacher.objects.get(last_name="Caux")
exam.date_exam = datetime(2018, 6, 28, 12, 00)
exam.room = "B123"
exam.save()
self.client.login(username='me', password='mepassword')
response = self.client.get(url, follow=True)
self.assertEqual(
@ -349,8 +353,8 @@ tél. 032 886 33 00
self.assertEqual(response['Content-Type'], 'application/pdf')
self.assertGreater(int(response['Content-Length']), 1000)
# Expert without corporation
st.expert = CorpContact.objects.create(first_name='James', last_name='Bond')
st.save()
exam.external_expert = CorpContact.objects.create(first_name='James', last_name='Bond')
exam.save()
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 200)
@ -373,16 +377,17 @@ tél. 032 886 33 00
first_name="Laurent", last_name="Hots", birth_date="1994-07-12",
pcode="2000", city="Neuchâtel", klass=klass
)
url = reverse('print-expert-compens-eds', args=[st.pk])
exam = Examination.objects.create(student=st, session=ExamEDESession.objects.create(year=2020, season='1'))
url = reverse('print-expert-compens-eds', args=[exam.pk])
self.client.login(username='me', password='mepassword')
response = self.client.get(url, follow=True)
self.assertContains(response, "Toutes les informations ne sont pas disponibles")
st.expert = CorpContact.objects.get(last_name="Horner")
st.internal_expert = Teacher.objects.get(last_name="Caux")
st.date_exam = datetime(2018, 6, 28, 12, 00)
st.room = "B123"
st.save()
exam.external_expert = CorpContact.objects.get(last_name="Horner")
exam.internal_expert = Teacher.objects.get(last_name="Caux")
exam.date_exam = datetime(2018, 6, 28, 12, 00)
exam.room = "B123"
exam.save()
response = self.client.get(url, follow=True)
self.assertEqual(
@ -392,8 +397,8 @@ tél. 032 886 33 00
self.assertEqual(response['Content-Type'], 'application/pdf')
self.assertGreater(int(response['Content-Length']), 1000)
# Expert without corporation
st.expert = CorpContact.objects.create(first_name='James', last_name='Bond')
st.save()
exam.external_expert = CorpContact.objects.create(first_name='James', last_name='Bond')
exam.save()
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 200)

View file

@ -24,7 +24,7 @@ from .imports import HPContactsImportView, HPImportView, ImportReportsView, Stud
from ..forms import CorporationMergeForm, EmailBaseForm, StudentCommentForm
from ..models import (
Klass, Section, Student, Teacher, Corporation, CorpContact, Period,
Training, Availability
Training, Availability, Examination,
)
from ..pdf import (
ChargeSheetPDF, ExpertEdeLetterPdf, ExpertEdsLetterPdf, UpdateDataFormPDF,
@ -401,53 +401,41 @@ class StudentConvocationExaminationView(EmailConfirmationView):
title = "Convocation à la soutenance du travail de diplôme"
email_template = 'email/student_convocation_EDE.txt'
@property
def expert(self):
return self.student.expert
@property
def internal_expert(self):
return self.student.internal_expert
@property
def date_soutenance_mailed(self):
return self.student.date_soutenance_mailed
def missing_examination_data(self):
return self.student.missing_examination_data()
def dispatch(self, request, *args, **kwargs):
self.student = Student.objects.get(pk=self.kwargs['pk'])
errors = self.missing_examination_data()
self.exam = Examination.objects.get(pk=self.kwargs['pk'])
errors = self.exam.missing_examination_data()
errors.extend(self.check_errors())
if errors:
messages.error(request, "\n".join(errors))
return redirect(reverse("admin:stages_student_change", args=(self.student.pk,)))
return redirect(reverse("admin:stages_student_change", args=(self.exam.student.pk,)))
return super().dispatch(request, *args, **kwargs)
def get_person(self):
return self.exam.student
def check_errors(self):
errors = []
if not self.student.email:
if not self.exam.student.email:
errors.append("Létudiant-e na pas de courriel valide !")
if self.expert and not self.expert.email:
if self.exam.external_expert and not self.exam.external_expert.email:
errors.append("Lexpert externe na pas de courriel valide !")
if self.internal_expert and not self.internal_expert.email:
if self.exam.internal_expert and not self.exam.internal_expert.email:
errors.append("Lexpert interne n'a pas de courriel valide !")
if self.date_soutenance_mailed is not None:
if self.exam.date_soutenance_mailed is not None:
errors.append("Une convocation a déjà été envoyée !")
return errors
def msg_context(self):
# Recipients with ladies first!
recip_names = sorted([
self.student.civility_full_name,
self.expert.civility_full_name,
self.internal_expert.civility_full_name,
self.exam.student.civility_full_name,
self.exam.external_expert.civility_full_name,
self.exam.internal_expert.civility_full_name,
])
titles = [
self.student.civility,
self.expert.civility,
self.internal_expert.civility,
self.exam.student.civility,
self.exam.external_expert.civility,
self.exam.internal_expert.civility,
]
mme_count = titles.count('Madame')
# Civilities, with ladies first!
@ -463,16 +451,18 @@ class StudentConvocationExaminationView(EmailConfirmationView):
'recipient1': recip_names[0],
'recipient2': recip_names[1],
'recipient3': recip_names[2],
'student': self.student,
'student': self.exam.student,
'sender': self.request.user,
'global_civilities': civilities,
'date_examen': django_format(self.student.date_exam, 'l j F Y à H\hi') if self.student.date_exam else '-',
'salle': self.student.room,
'date_examen': django_format(self.exam.date_exam, 'l j F Y à H\hi') if self.exam.date_exam else '-',
'salle': self.exam.room,
'internal_expert': self.exam.internal_expert,
'external_expert': self.exam.external_expert,
}
def get_initial(self):
initial = super().get_initial()
to = [self.student.email, self.expert.email, self.internal_expert.email]
to = [self.exam.student.email, self.exam.external_expert.email, self.exam.internal_expert.email]
initial.update({
'cci': self.request.user.email,
@ -484,8 +474,8 @@ class StudentConvocationExaminationView(EmailConfirmationView):
return initial
def on_success(self, student):
self.student.date_soutenance_mailed = timezone.now()
self.student.save()
self.exam.date_soutenance_mailed = timezone.now()
self.exam.save()
class StudentConvocationEDSView(StudentConvocationExaminationView):
@ -523,24 +513,24 @@ class PrintExpertEDECompensationForm(PDFBaseView):
"""
pdf_class = ExpertEdeLetterPdf
def filename(self, student):
return slugify('{0}_{1}'.format(student.last_name, student.first_name)) + '_Expert.pdf'
def filename(self, exam):
return slugify('{0}_{1}'.format(exam.student.last_name, exam.student.first_name)) + '_Expert.pdf'
def get_object(self):
return Student.objects.get(pk=self.kwargs['pk'])
return Examination.objects.get(pk=self.kwargs['pk'])
def check_object(self, student):
missing = student.missing_examination_data()
def check_object(self, exam):
missing = exam.missing_examination_data()
if missing:
messages.error(self.request, "\n".join(
["Toutes les informations ne sont pas disponibles pour la lettre à lexpert!"]
+ missing
))
return redirect(reverse("admin:stages_student_change", args=(student.pk,)))
return redirect(reverse("admin:stages_student_change", args=(exam.student.pk,)))
def get(self, request, *args, **kwargs):
student = self.get_object()
response = self.check_object(student)
exam = self.get_object()
response = self.check_object(exam)
if response:
return response
return super().get(request, *args, **kwargs)
@ -575,14 +565,14 @@ class PrintExpertEDSCompensationForm(PrintExpertEDECompensationForm):
"""
pdf_class = ExpertEdsLetterPdf
def check_object(self, student):
missing = student.missing_examination_data()
def check_object(self, exam):
missing = exam.missing_examination_data()
if missing:
messages.error(self.request, "\n".join(
["Toutes les informations ne sont pas disponibles pour la lettre à lexpert!"]
+ missing
))
return redirect(reverse("admin:stages_student_change", args=(student.pk,)))
return redirect(reverse("admin:stages_student_change", args=[exam.student.pk]))
class PrintKlassList(ZippedFilesBaseView):

View file

@ -21,6 +21,9 @@ class EmailConfirmationBaseView(FormView):
success_message = "Le message a été envoyé pour {person}"
error_message = "Échec denvoi pour {person} ({err})"
def get_person(self):
return self.person_model.objects.get(pk=self.kwargs['pk'])
def form_valid(self, form):
email = EmailMessage(
subject=form.cleaned_data['subject'],
@ -29,7 +32,7 @@ class EmailConfirmationBaseView(FormView):
to=form.cleaned_data['to'].split(';'),
bcc=form.cleaned_data['cci'].split(';'),
)
person = self.person_model.objects.get(pk=self.kwargs['pk'])
person = self.get_person()
try:
email.send()
except Exception as err:
@ -46,7 +49,7 @@ class EmailConfirmationBaseView(FormView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'person': self.person_model.objects.get(pk=self.kwargs['pk']),
'person': self.get_person(),
'title': self.title,
})
return context

View file

@ -432,7 +432,7 @@ def export_qualification(request, section='ede'):
# Data
empty_values = [''] * 7
for student in Student.objects.filter(klass__name__startswith='3%s' % section.upper(), archived=False
).select_related('klass', 'referent', 'training_referent', 'mentor', 'expert', 'internal_expert',
).select_related('klass', 'referent', 'training_referent', 'mentor',
).prefetch_related('examination_set'
).order_by('klass__name', 'last_name'):
stud_values = [

View file

@ -8,7 +8,7 @@ Nous vous informons que la soutenance du travail de diplôme de {{ student.civil
- {{ date_examen }} en salle {{ salle }}
Nous informons également {{ student.expert.civility }} {{ student.expert.last_name }} que le mémoire lui est adressé ce jour par courrier postal.
Nous informons également {{ external_expert.civility }} {{ external_expert.last_name }} que le mémoire lui est adressé ce jour par courrier postal.
Nous vous remercions de nous confirmer par retour de courriel que vous avez bien reçu ce message et dans lattente du plaisir de vous rencontrer prochainement, nous vous prions dagréer, {{ global_civilities }}, nos salutations les meilleures.

View file

@ -8,7 +8,7 @@ Nous vous informons que la soutenance du travail final de {{ student.civility_fu
- {{ date_examen }} en salle {{ salle }}
Nous informons également {{ student.expert.civility }} {{ student.expert.last_name }} que le mémoire lui est adressé ce jour par courrier postal.
Nous informons également {{ external_expert.civility }} {{ external_expert.last_name }} que le mémoire lui est adressé ce jour par courrier postal.
Nous vous remercions de nous confirmer par retour de courriel que vous avez bien reçu ce message et dans lattente du plaisir de vous rencontrer prochainement, nous vous prions dagréer, {{ global_civilities }}, nos salutations les meilleures.