Add view to send EDE student convocation

This commit is contained in:
Claude Paroz 2018-04-12 12:17:37 +02:00
parent 8225c48da5
commit 84440ceb2a
6 changed files with 214 additions and 15 deletions

View file

@ -7,6 +7,8 @@ from django.contrib import admin
from django.db import models
from django.db.models import Case, Count, When
from django.http import HttpResponse
from django.urls import reverse
from django.utils.html import format_html
from .models import (
Teacher, Option, Student, Section, Level, Klass, Corporation,
@ -110,17 +112,32 @@ class StudentAdmin(admin.ModelAdmin):
list_filter = (('archived', ArchivedListFilter), ('klass', KlassRelatedListFilter))
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')
fields = (('last_name', 'first_name', 'ext_id'), ('street', 'pcode', 'city', 'district'),
('email', 'tel', 'mobile'), ('gender', 'avs', 'birth_date'),
('archived', 'dispense_ecg', 'dispense_eps', 'soutien_dys'),
('klass', 'option_ase'),
('report_sem1', 'report_sem1_sent'),
('report_sem2', 'report_sem2_sent'),
('corporation', 'instructor',),
('supervisor', 'mentor', 'expert'))
readonly_fields = ('report_sem1_sent', 'report_sem2_sent', 'examination_actions')
fieldsets = (
(None, {
'fields': (('last_name', 'first_name', 'ext_id'), ('street', 'pcode', 'city', 'district'),
('email', 'tel', 'mobile'), ('gender', 'avs', 'birth_date'),
('archived', 'dispense_ecg', 'dispense_eps', 'soutien_dys'),
('klass', 'option_ase'),
('report_sem1', 'report_sem1_sent'),
('report_sem2', 'report_sem2_sent'),
('corporation', 'instructor',)
)
}
),
("Examen Qualification EDE", {
'classes': ('collapse',),
'fields': (
('supervisor', ),
('subject', 'title'),
('training_referent', 'referent', 'mentor'),
('internal_expert', 'expert'),
('session', 'date_exam', 'room', 'mark'),
('examination_actions',)
)
}),
)
actions = ['archive']
def archive(self, request, queryset):
for student in queryset:
# Save each item individually to allow for custom save() logic.
@ -128,6 +145,16 @@ class StudentAdmin(admin.ModelAdmin):
student.save()
archive.short_description = "Marquer les étudiants sélectionnés comme archivés"
def examination_actions(self, obj):
if obj.klass.section.name == 'EDE' and obj.klass.level.name == "3":
return format_html(
'<a class="button" href="{}">Mail convocation soutenance</a>',
reverse('student-ede-convocation', args=[obj.pk])
)
else:
return ''
examination_actions.short_description = 'Actions pour les examens EDE'
class CorpContactAdmin(admin.ModelAdmin):
list_display = ('__str__', 'corporation', 'role')

View file

@ -77,6 +77,14 @@ class Teacher(models.Model):
def __str__(self):
return '{0} {1}'.format(self.last_name, self.first_name)
@property
def full_name(self):
return '{0} {1}'.format(self.first_name, self.last_name)
@property
def civility_full_name(self):
return '{0} {1} {2}'.format(self.civility, self.first_name, self.last_name)
def calc_activity(self):
"""
Return a dictionary of calculations relative to teacher courses.
@ -266,10 +274,18 @@ class Student(models.Model):
def full_name(self):
return '{0} {1}'.format(self.first_name, self.last_name)
@property
def civility_full_name(self):
return '{0} {1} {2}'.format(self.civility, self.first_name, self.last_name)
@property
def pcode_city(self):
return '{0} {1}'.format(self.pcode, self.city)
@property
def is_examination_valid(self):
return (self.date_exam and self.room and self.expert and self.internal_expert)
def save(self, **kwargs):
if self.archived and not self.archived_text:
# Fill archived_text with training data, JSON-formatted
@ -370,6 +386,18 @@ class CorpContact(models.Model):
def __str__(self):
return '{0} {1}, {2}'.format(self.last_name, self.first_name, self.corporation or '-')
@property
def full_name(self):
return '{0} {1}'.format(self.first_name, self.last_name)
@property
def civility_full_name(self):
return '{0} {1} {2}'.format(self.title, self.first_name, self.last_name)
@property
def pcode_city(self):
return '{0} {1}'.format(self.pcode, self.city)
class Domain(models.Model):
name = models.CharField(max_length=50, verbose_name='Nom')

View file

@ -1,6 +1,6 @@
import json
import os
from datetime import date
from datetime import date, datetime
from django.conf import settings
from django.contrib.auth.models import User
@ -50,7 +50,10 @@ class StagesTest(TestCase):
Student(first_name="Gil", last_name="Schmid", birth_date="1996-02-14",
pcode="2000", city="Neuchâtel", klass=klass3, corporation=corp),
])
ref1 = Teacher.objects.create(first_name="Julie", last_name="Caux", abrev="JCA")
ref1 = Teacher.objects.create(
first_name="Julie", last_name="Caux", abrev="JCA", email="julie@eample.org",
civility="Madame",
)
cls.p1 = Period.objects.create(
title="Stage de pré-sensibilisation", start_date="2012-11-26", end_date="2012-12-07",
section=sect_ase, level=lev1,
@ -77,7 +80,9 @@ class StagesTest(TestCase):
Training.objects.create(
availability=av3, student=Student.objects.get(first_name="André"), referent=ref1,
)
cls.admin = User.objects.create_user('me', 'me@example.org', 'mepassword')
cls.admin = User.objects.create_user(
'me', 'me@example.org', 'mepassword', first_name='Jean', last_name='Valjean',
)
def setUp(self):
self.client.login(username='me', password='mepassword')
@ -147,6 +152,46 @@ class StagesTest(TestCase):
)
self.assertGreater(int(response['Content-Length']), 10)
def test_send_ede_convocation(self):
st = Student.objects.get(first_name="Albin")
self.client.login(username='me', password='mepassword')
url = reverse('student-ede-convocation', args=[st.pk])
response = self.client.get(url, follow=True)
self.assertContains(response, "Toutes les informations ne sont pas disponibles pour la convocation!")
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()
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()
response = self.client.get(url)
expected_message = """ Albin Dupond,
Madame Julie Caux,
Monsieur Jean Horner,
Nous vous informons que la soutenance du travail de diplôme de Albin Dupond aura lieu dans les locaux de lEcole Santé-social Pierre-Coullery, rue de la Prévoyance 82, 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 de lenfance, dipl. ES
Jean Valjean
me@example.org
tél. 032 886 33 00
"""
self.assertEqual(response.context['form'].initial['message'], expected_message)
class PeriodTest(TestCase):
def setUp(self):

View file

@ -18,14 +18,16 @@ from django.db import transaction
from django.db.models import Case, Count, Value, When, Q
from django.db.models.functions import Concat
from django.http import HttpResponse, HttpResponseNotAllowed, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.shortcuts import get_object_or_404, redirect
from django.template import loader
from django.urls import reverse
from django.urls import reverse, reverse_lazy
from django.utils import timezone
from django.utils.dateformat import format as django_format
from django.utils.translation import ugettext as _
from django.utils.text import slugify
from django.views.generic import DetailView, FormView, TemplateView, ListView
from .base_views import EmailConfirmationBaseView
from .exports import OpenXMLExport
from .forms import EmailBaseForm, PeriodForm, StudentImportForm, UploadHPFileForm, UploadReportForm
from .models import (
@ -657,6 +659,78 @@ class SendStudentReportsView(FormView):
return context
class EmailConfirmationView(EmailConfirmationBaseView):
person_model = Student
success_url = reverse_lazy('admin:stages_student_changelist')
error_message = "Échec denvoi pour létudiant {person} ({err})"
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"
candidate_date_field = 'convocation_date'
def get(self, request, *args, **kwargs):
self.student = Student.objects.get(pk=self.kwargs['pk'])
error = ''
if not self.student.is_examination_valid:
error = "Toutes les informations ne sont pas disponibles pour la convocation!"
elif not self.student.expert.email:
error = "Lexpert externe na pas de courriel valide !"
elif not self.student.internal_expert.email:
error = "Lexpert interne n'a pas de courriel valide !"
if error:
messages.error(request, error)
return redirect(reverse("admin:stages_student_change", args=(self.student.pk,)))
return super().get(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'
# 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,
])
titles = [
self.student.civility,
self.student.expert.title,
self.student.internal_expert.civility,
]
mme_count = titles.count('Madame')
# Civilities, with ladies first!
if mme_count == 0:
civilities = 'Messieurs'
elif mme_count == 1:
civilities = 'Madame, Messieurs'
elif mme_count == 2:
civilities = 'Mesdames, Monsieur'
else:
civilities = 'Mesdames'
msg_context = {
'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'),
'salle': self.student.room,
}
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),
'sender': self.request.user.email,
})
return initial
EXPORT_FIELDS = [
# Student fields
('ID externe', 'student__ext_id'),