diff --git a/candidats/admin.py b/candidats/admin.py
index 45a2756..e369da6 100644
--- a/candidats/admin.py
+++ b/candidats/admin.py
@@ -6,6 +6,7 @@ from django.contrib import admin
from django.core.mail import EmailMessage
from django.db.models import BooleanField
from django.template import loader
+from django.urls import reverse
from stages.exports import OpenXMLExport
from .models import Candidate, Interview, GENDER_CHOICES
@@ -119,7 +120,7 @@ class CandidateAdminForm(forms.ModelForm):
class CandidateAdmin(admin.ModelAdmin):
form = CandidateAdminForm
- list_display = ('last_name', 'first_name', 'section', 'confirm_mail')
+ list_display = ('last_name', 'first_name', 'section', 'confirm_mail', 'convocation')
list_filter = ('section', 'option')
readonly_fields = ('total_result_points', 'total_result_mark', 'date_confirmation_mail')
actions = [export_candidates, send_confirmation_mail]
@@ -157,6 +158,17 @@ class CandidateAdmin(admin.ModelAdmin):
return obj.date_confirmation_mail is not None
confirm_mail.boolean = True
+ def convocation(self, obj):
+ if obj.interview is None:
+ return '???'
+ elif obj.interview and obj.convocation_date:
+ return obj.interview
+ else:
+ url = reverse('candidate-convocation', args=[obj.pk])
+ return 'Envoyer convocation '
+ convocation.short_description = 'Convoc. aux examens'
+ convocation.allow_tags = True
+
class InterviewAdmin(admin.ModelAdmin):
pass
diff --git a/candidats/models.py b/candidats/models.py
index 05863c6..6a29d25 100644
--- a/candidats/models.py
+++ b/candidats/models.py
@@ -166,7 +166,11 @@ class Interview(models.Model):
def __str__(self):
return '{0} : {1}/{2} - ({3}) -salle:{4}-{5}'.format(
- django_format(self.date, "l j F Y à H\hi"),
+ self.date_formatted,
self.teacher_int or '?', self.teacher_file or '?',
self.status, self.room, self.candidat or '???'
)
+
+ @property
+ def date_formatted(self):
+ return django_format(self.date, "l j F Y à H\hi")
diff --git a/candidats/tests.py b/candidats/tests.py
index 4a000a8..81700df 100644
--- a/candidats/tests.py
+++ b/candidats/tests.py
@@ -113,3 +113,53 @@ me@example.org
self.assertContains(response, "Échec d’envoi pour le candidat Dupond Henri (Error sending mail)")
henri.refresh_from_db()
self.assertIsNone(henri.date_confirmation_mail)
+
+ def test_convocation_ede(self):
+ ede = Section.objects.create(name='EDE')
+ henri = Candidate.objects.create(
+ first_name='Henri', last_name='Dupond', gender='M', section=ede,
+ email='henri@example.org', deposite_date=date.today()
+ )
+ inter = Interview.objects.create(date=datetime(2018, 3, 10, 10, 30), room='B103', candidat=henri)
+ self.client.login(username='me', password='mepassword')
+ response = self.client.get(reverse('candidate-convocation', args=[henri.pk]))
+ self.assertContains(response, '
Dupond Henri ')
+ self.assertContains(response, ' ', html=True)
+ self.assertContains(response, """
+Monsieur Henri Dupond,
+
+Nous vous adressons par la présente votre convocation personnelle à la procédure d’admission de la filière Education de l’enfance, dipl. ES.
+
+Vous êtes attendu-e à l’Ecole Santé-social Pierre-Coullery, rue de la Prévoyance 82, 2300 La Chaux-de-Fonds aux dates suivantes:
+
+ - mercredi 7 mars 2018, à 13h30, salle 405, pour l’examen écrit (durée approx. 4 heures)
+
+ - samedi 10 mars 2018 à 10h30, en salle B103, pour l’entretien d’admission (durée approx. 45 min.).
+
+En cas d’empêchement de dernière minute, nous vous remercions d’annoncer votre absence au secrétariat (Tél. 032 886 33 00).
+
+De plus, afin que nous puissions enregistrer définitivement votre inscription, nous vous remercions par avance
+de nous faire parvenir, dans les meilleurs délais, le ou les documents suivants:
+ - Formulaire d'inscription, Attest. de paiement, Casier judic., CV, Texte réflexif, Photo passeport, Bilan act. prof./dernier stage, Bull. de notes
+
+Dans l’intervalle, nous vous adressons, Monsieur, nos salutations les plus cordiales.
+
+Secrétariat de la filière Education de l’enfance, dipl. ES
+Hans Schmid
+me@example.org
+tél. 032 886 33 00"""
+ )
+ response = self.client.post(reverse('candidate-convocation', args=[henri.pk]), data={
+ 'id_candidate': str(henri.pk),
+ 'cci': 'me@example.org',
+ 'to': henri.email,
+ 'subject': "Procédure de qualification",
+ 'message': "Monsieur Henri Dupond, ...",
+ 'sender': 'me@example.org',
+ })
+ self.assertRedirects(response, reverse('admin:candidats_candidate_changelist'))
+ self.assertEqual(len(mail.outbox), 1)
+ self.assertEqual(mail.outbox[0].recipients(), ['henri@example.org', 'me@example.org'])
+ self.assertEqual(mail.outbox[0].subject, "Procédure de qualification")
+ henri.refresh_from_db()
+ self.assertIsNotNone(henri.convocation_date)
diff --git a/candidats/views.py b/candidats/views.py
new file mode 100644
index 0000000..0483c57
--- /dev/null
+++ b/candidats/views.py
@@ -0,0 +1,90 @@
+from django import forms
+from django.conf import settings
+from django.contrib import messages
+from django.core.mail import EmailMessage
+from django.template import loader
+from django.urls import reverse_lazy
+from django.utils import timezone
+from django.views.generic import FormView
+
+from candidats.models import Candidate
+
+
+class ConvocationForm(forms.Form):
+ id_candidate = forms.CharField(widget=forms.HiddenInput())
+ sender = forms.CharField(widget=forms.HiddenInput())
+ to = forms.CharField(widget=forms.TextInput(attrs={'size': '60'}))
+ cci = forms.CharField(widget=forms.TextInput(attrs={'size': '60'}))
+ subject = forms.CharField(widget=forms.TextInput(attrs={'size': '60'}))
+ message = forms.CharField(widget=forms.Textarea(attrs={'rows': 25, 'cols': 120}))
+
+
+class SendConvocationView(FormView):
+ template_name = 'candidats/convocation.html'
+ form_class = ConvocationForm
+ success_url = reverse_lazy('admin:candidats_candidate_changelist')
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ candidate = Candidate.objects.get(pk=self.kwargs['pk'])
+ docs = [
+ 'registration_form', 'certificate_of_payement', 'police_record', 'cv', 'reflexive_text',
+ 'has_photo', 'work_certificate', 'marks_certificate',
+ ]
+ if candidate.option == 'PE-5400h':
+ docs.append('promise', 'contract', 'certif_of_800h')
+ elif candidate.option == 'PE-3600h':
+ docs.append('certif_of_cfc', 'promise', 'contract')
+ elif candidate.option == 'PS':
+ docs.append('certif_of_800h')
+
+ missing_documents = {'documents': ', '.join([
+ Candidate._meta.get_field(doc).verbose_name for doc in docs if not getattr(candidate, doc)
+ ])}
+
+ msg_context = {
+ 'candidate_name': " ".join([candidate.civility, candidate.first_name, candidate.last_name]),
+ 'candidate_civility': candidate.civility,
+ 'date_lieu_examen': settings.DATE_LIEU_EXAMEN_EDE,
+ 'date_entretien': candidate.interview.date_formatted,
+ 'salle_entretien': candidate.interview.room,
+ 'rappel': loader.render_to_string('email/rappel_document_EDE.txt', missing_documents),
+ 'sender_name': " ".join([self.request.user.first_name, self.request.user.last_name]),
+ 'sender_email': self.request.user.email,
+ }
+
+ form = ConvocationForm(initial={
+ 'id_candidate': candidate.pk,
+ 'cci': self.request.user.email,
+ 'to': candidate.email,
+ 'subject': "Procédure de qualification",
+ 'message': loader.render_to_string('email/candidate_convocation_EDE.txt', msg_context),
+ 'sender': self.request.user.email,
+ })
+ context.update({
+ 'candidat': candidate,
+ 'form': form,
+ })
+ return context
+
+ def form_valid(self, form):
+ email = EmailMessage(
+ subject=form.cleaned_data['subject'],
+ body=form.cleaned_data['message'],
+ from_email=form.cleaned_data['sender'],
+ to=form.cleaned_data['to'].split(';'),
+ bcc=form.cleaned_data['cci'].split(';'),
+ )
+ candidate = Candidate.objects.get(pk=self.kwargs['pk'])
+ try:
+ email.send()
+ except Exception as err:
+ messages.error(self.request, "Échec d’envoi pour le candidat {0} ({1})".format(candidate, err))
+ else:
+ candidate.convocation_date = timezone.now()
+ candidate.save()
+ messages.success(self.request,
+ "Le message de convocation a été envoyé pour le candidat {0}".format(candidate)
+ )
+ return super().form_valid(form)
diff --git a/common/settings.py b/common/settings.py
index bcc8fa1..5ad26d1 100644
--- a/common/settings.py
+++ b/common/settings.py
@@ -146,6 +146,8 @@ CHARGE_SHEET_TITLE = "Feuille de charge pour l'année scolaire 2017-2018"
MAX_ENS_PERIODS = 1900
MAX_ENS_FORMATION = 250
+DATE_LIEU_EXAMEN_EDE = "mercredi 7 mars 2018, à 13h30, salle 405"
+
if 'TRAVIS' in os.environ:
SECRET_KEY = 'secretkeyfortravistests'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
diff --git a/common/urls.py b/common/urls.py
index c190a6c..54fd3c8 100644
--- a/common/urls.py
+++ b/common/urls.py
@@ -2,6 +2,7 @@ from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import RedirectView
+from candidats import views as candidats_views
from stages import views
urlpatterns = [
@@ -21,6 +22,9 @@ urlpatterns = [
url(r'^classes/$', views.KlassListView.as_view(), name='classes'),
url(r'^classes/(?P\d+)/$', views.KlassView.as_view(), name='class'),
+ url(r'^candidate/(?P\d+)/send_convocation/$', candidats_views.SendConvocationView.as_view(),
+ name='candidate-convocation'),
+
url(r'^imputations/export/$', views.imputations_export, name='imputations_export'),
url(r'^print/update_form/$', views.print_update_form, name='print_update_form'),
url(r'^general_export/$', views.general_export, name='general-export'),
diff --git a/templates/candidats/convocation.html b/templates/candidats/convocation.html
new file mode 100644
index 0000000..17a5eed
--- /dev/null
+++ b/templates/candidats/convocation.html
@@ -0,0 +1,20 @@
+{% extends "admin/base_site.html" %}
+{% load i18n static %}
+
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+{{ candidat }}
+
+{% endblock %}
diff --git a/templates/email/candidate_convocation_EDE.txt b/templates/email/candidate_convocation_EDE.txt
new file mode 100644
index 0000000..3196d50
--- /dev/null
+++ b/templates/email/candidate_convocation_EDE.txt
@@ -0,0 +1,19 @@
+{{ candidate_name }},
+
+Nous vous adressons par la présente votre convocation personnelle à la procédure d’admission de la filière Education de l’enfance, dipl. ES.
+
+Vous êtes attendu-e à l’Ecole Santé-social Pierre-Coullery, rue de la Prévoyance 82, 2300 La Chaux-de-Fonds aux dates suivantes:
+
+ - {{ date_lieu_examen }}, pour l’examen écrit (durée approx. 4 heures)
+
+ - {{ date_entretien }}, en salle {{ salle_entretien }}, pour l’entretien d’admission (durée approx. 45 min.).
+
+En cas d’empêchement de dernière minute, nous vous remercions d’annoncer votre absence au secrétariat (Tél. 032 886 33 00).
+{{ rappel }}
+
+Dans l’intervalle, nous vous adressons, {{ candidate_civility }}, nos salutations les plus cordiales.
+
+Secrétariat de la filière Education de l’enfance, dipl. ES
+{{ sender_name }}
+{{ sender_email }}
+tél. 032 886 33 00
diff --git a/templates/email/rappel_document_EDE.txt b/templates/email/rappel_document_EDE.txt
new file mode 100644
index 0000000..b15c765
--- /dev/null
+++ b/templates/email/rappel_document_EDE.txt
@@ -0,0 +1,4 @@
+
+De plus, afin que nous puissions enregistrer définitivement votre inscription, nous vous remercions par avance
+de nous faire parvenir, dans les meilleurs délais, le ou les documents suivants:
+ - {{ documents }}
\ No newline at end of file