Add convocation email functionality for EDS section

This commit is contained in:
Claude Paroz 2019-10-04 13:48:15 +02:00
parent 35e5c84933
commit 067f6f96ac
6 changed files with 194 additions and 24 deletions

View file

@ -107,7 +107,8 @@ class StudentAdmin(admin.ModelAdmin):
search_fields = ('last_name', 'first_name', 'pcode', 'city', 'klass__name')
autocomplete_fields = ('corporation', 'instructor', 'supervisor', 'mentor', 'expert')
readonly_fields = (
'report_sem1_sent', 'report_sem2_sent', 'examination_actions',
'report_sem1_sent', 'report_sem2_sent',
'examination_actions', 'examination_ep_actions',
'date_soutenance_mailed', 'date_soutenance_ep_mailed'
)
fieldsets = (
@ -140,7 +141,7 @@ class StudentAdmin(admin.ModelAdmin):
('session_ep', 'date_exam_ep', 'room_ep', 'mark_ep'),
('internal_expert_ep', 'expert_ep'),
('date_soutenance_ep_mailed', 'date_confirm_ep_received'),
#('examination_actions',)
('examination_ep_actions',)
)
}),
)
@ -174,6 +175,18 @@ class StudentAdmin(admin.ModelAdmin):
return ''
examination_actions.short_description = 'Actions pour les examens ES'
def examination_ep_actions(self, obj):
if obj.klass and obj.klass.section.name == 'EDS' and obj.klass.level.name == "3":
return format_html(
'<a class="button" href="{}">Courrier pour lexpert</a>&nbsp;'
'<a class="button" href="{}">Mail convocation soutenance</a>&nbsp;',
'', #reverse('print-expert-compens-eds', args=[obj.pk]),
reverse('student-eds-convocation', args=[obj.pk]),
)
else:
return ''
examination_ep_actions.short_description = 'Actions pour les examens ES'
class CorpContactAdmin(admin.ModelAdmin):
list_display = ('__str__', 'corporation', 'role')

View file

@ -407,6 +407,18 @@ class Student(models.Model):
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 StudentFile(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)

View file

@ -26,6 +26,7 @@ class StagesTests(TestCase):
sect_ase = Section.objects.get(name='MP_ASE')
lev1 = Level.objects.create(name='1')
lev2 = Level.objects.create(name='2')
lev3 = Level.objects.create(name='3')
klass1 = Klass.objects.create(name="1ASE3", section=sect_ase, level=lev1)
klass2 = Klass.objects.create(name="2ASE3", section=sect_ase, level=lev2)
klass3 = Klass.objects.create(name="2EDS", section=Section.objects.get(name='EDS'), level=lev2)
@ -261,6 +262,70 @@ tél. 032 886 33 00
st.refresh_from_db()
self.assertIsNotNone(st.date_soutenance_mailed)
def test_send_eds_convocation(self):
klass = Klass.objects.create(
name="3EDS", section=Section.objects.get(name='EDS'), level=Level.objects.get(name='3')
)
st = Student.objects.create(
first_name="Laurent", last_name="Hots", birth_date="1994-07-12",
pcode="2000", city="Neuchâtel", klass=klass
)
self.client.login(username='me', password='mepassword')
url = reverse('student-eds-convocation', args=[st.pk])
response = self.client.get(url, follow=True)
for err in ("Létudiant-e na pas de courriel valide",
"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.email = 'hots@example.org'
st.date_exam_ep = datetime(2018, 6, 28, 12, 00)
st.room_ep = "B123"
st.expert_ep = CorpContact.objects.get(last_name="Horner")
st.internal_expert_ep = Teacher.objects.get(last_name="Caux")
st.save()
response = self.client.get(url, follow=True)
self.assertContains(response, "Lexpert externe na pas de courriel valide !")
st.expert_ep.email = "horner@example.org"
st.expert_ep.save()
response = self.client.get(url)
expected_message = """ Laurent Hots,
Madame Julie Caux,
Monsieur Jean Horner,
Nous vous informons que la soutenance du travail final de Laurent Hots aura lieu dans les locaux de lEcole Santé-social Pierre-Coullery, rue Sophie-Mairet 29-31, 2300 La Chaux-de-Fonds en date du:
- jeudi 28 juin 2018 à 12h00 en salle B123
Nous informons également Monsieur Horner 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, Madame, Messieurs, nos salutations les meilleures.
Secrétariat de la filière Education sociale, dipl. ES
Jean Valjean
me@example.org
tél. 032 886 33 00
"""
self.assertEqual(response.context['form'].initial['message'], expected_message)
# Now send the message
response = self.client.post(url, data={
'cci': 'me@example.org',
'to': st.email,
'subject': "Convocation",
'message': "Monsieur Albin, ...",
'sender': 'me@example.org',
})
self.assertEqual(len(mail.outbox), 1)
st.refresh_from_db()
self.assertIsNotNone(st.date_soutenance_ep_mailed)
def test_print_ede_compensation_forms(self):
st = Student.objects.get(first_name="Albin")
url = reverse('print-expert-compens-ede', args=[st.pk])

View file

@ -398,36 +398,55 @@ class EmailConfirmationView(EmailConfirmationBaseView):
class StudentConvocationExaminationView(EmailConfirmationView):
success_message = "Le message de convocation a été envoyé pour létudiant {person}"
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.student.missing_examination_data()
if self.student.expert and not self.student.expert.email:
errors.append("Lexpert externe na pas de courriel valide !")
if self.student.internal_expert and not self.student.internal_expert.email:
errors.append("Lexpert interne n'a pas de courriel valide !")
if self.student.date_soutenance_mailed is not None:
errors.append("Une convocation a déjà été envoyée !")
errors = self.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 super().dispatch(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
to = [self.student.email, self.student.expert.email, self.student.internal_expert.email]
src_email = 'email/student_convocation_EDE.txt'
def check_errors(self):
errors = []
if not self.student.email:
errors.append("Létudiant-e na pas de courriel valide !")
if self.expert and not self.expert.email:
errors.append("Lexpert externe na pas de courriel valide !")
if self.internal_expert and not self.internal_expert.email:
errors.append("Lexpert interne n'a pas de courriel valide !")
if self.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.student.expert.civility_full_name,
self.student.internal_expert.civility_full_name,
self.expert.civility_full_name,
self.internal_expert.civility_full_name,
])
titles = [
self.student.civility,
self.student.expert.civility,
self.student.internal_expert.civility,
self.expert.civility,
self.internal_expert.civility,
]
mme_count = titles.count('Madame')
# Civilities, with ladies first!
@ -439,29 +458,65 @@ class StudentConvocationExaminationView(EmailConfirmationView):
civilities = 'Mesdames, Monsieur'
else:
civilities = 'Mesdames'
msg_context = {
return {
'recipient1': recip_names[0],
'recipient2': recip_names[1],
'recipient3': recip_names[2],
'student': self.student,
'sender': self.request.user,
'global_civilities': civilities,
'date_examen': django_format(self.student.date_exam, 'l j F Y à H\hi'),
'date_examen': django_format(self.student.date_exam, 'l j F Y à H\hi') if self.student.date_exam else '-',
'salle': self.student.room,
}
def get_initial(self):
initial = super().get_initial()
to = [self.student.email, self.expert.email, self.internal_expert.email]
initial.update({
'cci': self.request.user.email,
'to': '; '.join(to),
'subject': "Convocation à la soutenance de travail de diplôme",
'message': loader.render_to_string(src_email, msg_context),
'subject': self.title,
'message': loader.render_to_string(self.email_template, self.msg_context()),
'sender': self.request.user.email,
})
return initial
def on_success(self, student):
student.date_soutenance_mailed = timezone.now()
student.save()
self.student.date_soutenance_mailed = timezone.now()
self.student.save()
class StudentConvocationEDSView(StudentConvocationExaminationView):
title = "Convocation à la soutenance du travail final"
email_template = 'email/student_convocation_EDS.txt'
@property
def expert(self):
return self.student.expert_ep
@property
def internal_expert(self):
return self.student.internal_expert_ep
@property
def date_soutenance_mailed(self):
return self.student.date_soutenance_ep_mailed
def missing_examination_data(self):
return self.student.missing_examination_ep_data()
def msg_context(self):
context = super().msg_context()
context.update({
'date_examen': django_format(self.student.date_exam_ep, 'l j F Y à H\hi'),
'salle': self.student.room_ep,
})
return context
def on_success(self, student):
self.student.date_soutenance_ep_mailed = timezone.now()
self.student.save()
class PrintUpdateForm(ZippedFilesBaseView):