Add view to send EDE student convocation
This commit is contained in:
parent
8225c48da5
commit
84440ceb2a
6 changed files with 214 additions and 15 deletions
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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, "L’expert externe n’a 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 l’Ecole 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 l’attente du plaisir de vous rencontrer prochainement, nous vous prions d’agréer, Madame, Messieurs, nos salutations les meilleures.
|
||||
|
||||
|
||||
|
||||
Secrétariat de la filière Education de l’enfance, 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):
|
||||
|
|
|
|||
|
|
@ -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 d’envoi 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 = "L’expert externe n’a pas de courriel valide !"
|
||||
elif not self.student.internal_expert.email:
|
||||
error = "L’expert 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'),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue