Add view to send PDF letter to EDE expert
This commit is contained in:
parent
84440ceb2a
commit
3b43477e17
7 changed files with 168 additions and 7 deletions
|
|
@ -148,7 +148,9 @@ class StudentAdmin(admin.ModelAdmin):
|
|||
def examination_actions(self, obj):
|
||||
if obj.klass.section.name == 'EDE' and obj.klass.level.name == "3":
|
||||
return format_html(
|
||||
'<a class="button" href="{}">Courrier pour l’expert</a> '
|
||||
'<a class="button" href="{}">Mail convocation soutenance</a>',
|
||||
reverse('print-pdf-to-expert-ede', args=[obj.pk]),
|
||||
reverse('student-ede-convocation', args=[obj.pk])
|
||||
)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -85,6 +85,10 @@ class Teacher(models.Model):
|
|||
def civility_full_name(self):
|
||||
return '{0} {1} {2}'.format(self.civility, self.first_name, self.last_name)
|
||||
|
||||
@property
|
||||
def role(self):
|
||||
return {'Monsieur': 'enseignant-formateur', 'Madame': 'enseignante-formatrice'}.get(self.civility, '')
|
||||
|
||||
def calc_activity(self):
|
||||
"""
|
||||
Return a dictionary of calculations relative to teacher courses.
|
||||
|
|
@ -282,6 +286,13 @@ class Student(models.Model):
|
|||
def pcode_city(self):
|
||||
return '{0} {1}'.format(self.pcode, self.city)
|
||||
|
||||
@property
|
||||
def role(self):
|
||||
if self.klass.section.is_fe():
|
||||
return {'M': 'apprenti', 'F': 'apprentie'}.get(self.gender, '')
|
||||
else:
|
||||
return {'M': 'étudiant', 'F': 'étudiante'}.get(self.gender, '')
|
||||
|
||||
@property
|
||||
def is_examination_valid(self):
|
||||
return (self.date_exam and self.room and self.expert and self.internal_expert)
|
||||
|
|
@ -398,6 +409,10 @@ class CorpContact(models.Model):
|
|||
def pcode_city(self):
|
||||
return '{0} {1}'.format(self.pcode, self.city)
|
||||
|
||||
@property
|
||||
def adjective_ending(self):
|
||||
return 'e' if self.title == 'Madame' else ''
|
||||
|
||||
|
||||
class Domain(models.Model):
|
||||
name = models.CharField(max_length=50, verbose_name='Nom')
|
||||
|
|
|
|||
113
stages/pdf.py
113
stages/pdf.py
|
|
@ -4,9 +4,10 @@ from datetime import date
|
|||
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles.finders import find
|
||||
from django.utils.dateformat import format as django_format
|
||||
from django.utils.text import slugify
|
||||
|
||||
from reportlab.lib.pagesizes import A4, landscape
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib.units import cm
|
||||
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
|
||||
from reportlab.lib import colors
|
||||
|
|
@ -21,6 +22,8 @@ style_bold = PS(name='CORPS', fontName='Helvetica-Bold', fontSize=10, alignment
|
|||
style_title = PS(name='CORPS', fontName='Helvetica-Bold', fontSize=12, alignment = TA_LEFT, spaceBefore=1*cm)
|
||||
style_adress = PS(name='CORPS', fontName='Helvetica', fontSize=10, alignment = TA_LEFT, leftIndent=280)
|
||||
style_normal_right = PS(name='CORPS', fontName='Helvetica', fontSize=8, alignment = TA_RIGHT)
|
||||
style_bold_center = PS(name="CORPS", fontName="Helvetica-Bold", fontSize=9, alignment=TA_CENTER)
|
||||
style_footer = PS(name='CORPS', fontName='Helvetica', fontSize=7, alignment=TA_CENTER)
|
||||
|
||||
LOGO_EPC = find('img/logo_EPC.png')
|
||||
LOGO_ESNE = find('img/logo_ESNE.png')
|
||||
|
|
@ -59,7 +62,7 @@ class EpcBaseDocTemplate(SimpleDocTemplate):
|
|||
canvas.line(doc.leftMargin, doc.height + 0.2 * cm, doc.width + doc.leftMargin, doc.height + 0.2 * cm)
|
||||
canvas.restoreState()
|
||||
|
||||
def setNormalTemplatePage(self):
|
||||
def set_normal_template_page(self):
|
||||
first_page_table_frame = Frame(
|
||||
self.leftMargin, self.bottomMargin, self.width + 1 * cm, self.height - 4 * cm,
|
||||
id='first_table', showBoundary=0, leftPadding=0 * cm
|
||||
|
|
@ -75,6 +78,30 @@ class EpcBaseDocTemplate(SimpleDocTemplate):
|
|||
self.story = [NextPageTemplate(['*', 'LaterPages'])]
|
||||
|
||||
|
||||
class EpcBaseLetterTemplate(EpcBaseDocTemplate):
|
||||
def __init__(self, filename, title=''):
|
||||
super().__init__(filename)
|
||||
self.story = []
|
||||
self.title = title
|
||||
|
||||
def header(self, canvas, doc):
|
||||
canvas.saveState()
|
||||
canvas.drawImage(
|
||||
LOGO_EPC, doc.leftMargin, doc.height - 0.5 * cm, 5 * cm, 3 * cm, preserveAspectRatio=True
|
||||
)
|
||||
canvas.drawImage(
|
||||
LOGO_ESNE, doc.width - 2 * cm, doc.height - 0.5 * cm, 5 * cm, 3 * cm, preserveAspectRatio=True
|
||||
)
|
||||
# Footer
|
||||
canvas.line(doc.leftMargin, 1 * cm, doc.width + doc.leftMargin, 1 * cm)
|
||||
footer = Paragraph('Ecole Santé-social Pierre-Coullery | Prévoyance 82 - 2300 La Chaux-de-Fonds | '
|
||||
'032 886 33 00 | cifom-epc@rpn.ch', style_footer)
|
||||
w, h = footer.wrap(doc.width, doc.bottomMargin)
|
||||
footer.drawOn(canvas, doc.leftMargin, h)
|
||||
|
||||
canvas.restoreState()
|
||||
|
||||
|
||||
class ChargeSheetPDF(SimpleDocTemplate):
|
||||
"""
|
||||
Génération des feuilles de charges en pdf.
|
||||
|
|
@ -85,9 +112,9 @@ class ChargeSheetPDF(SimpleDocTemplate):
|
|||
filename = slugify('{0}_{1}'.format(teacher.last_name, teacher.first_name)) + '.pdf'
|
||||
path = os.path.join(tempfile.gettempdir(), filename)
|
||||
super().__init__(path, pagesize=A4, topMargin=0*cm, leftMargin=2*cm)
|
||||
self.story = []
|
||||
|
||||
def produce(self, activities):
|
||||
self.story = []
|
||||
header = open(find('img/header.gif'), 'rb')
|
||||
self.story.append(Image(header, width=520, height=75))
|
||||
self.story.append(Spacer(0, 2*cm))
|
||||
|
|
@ -121,7 +148,7 @@ class ChargeSheetPDF(SimpleDocTemplate):
|
|||
t.hAlign = TA_CENTER
|
||||
self.story.append(t)
|
||||
self.story.append(Spacer(0, 2*cm))
|
||||
d = 'La Chaux-de-Fonds, le {0}'.format(date.today().strftime('%d.%m.%y'))
|
||||
d = 'La Chaux-de-Fonds, le {0}'.format(django_format(date.today(), 'j F Y'))
|
||||
self.story.append(Paragraph(d, style_normal))
|
||||
self.story.append(Spacer(0, 0.5*cm))
|
||||
self.story.append(Paragraph('la direction', style_normal))
|
||||
|
|
@ -244,3 +271,81 @@ class UpdateDataFormPDF(SimpleDocTemplate):
|
|||
|
||||
def is_instr_required(self, klass_name):
|
||||
return any(el in klass_name for el in ['FE', 'EDS'])
|
||||
|
||||
|
||||
class ExpertEDEPDF(EpcBaseLetterTemplate):
|
||||
"""
|
||||
PDF letter to expert EDE
|
||||
"""
|
||||
|
||||
def __init__(self, student, **kwargs):
|
||||
filename = slugify('{0}_{1}'.format(student.last_name, student.first_name)) + '.pdf'
|
||||
path = os.path.join(tempfile.gettempdir(), filename)
|
||||
super().__init__(path, title="", **kwargs)
|
||||
self.set_normal_template_page()
|
||||
|
||||
def produce(self, student):
|
||||
# Expert adresse
|
||||
self.story.append(Paragraph(student.expert.title, style_adress))
|
||||
self.story.append(Paragraph(student.expert.full_name, style_adress))
|
||||
self.story.append(Paragraph(student.expert.street, style_adress))
|
||||
self.story.append((Paragraph(student.expert.pcode_city, style_adress)))
|
||||
ptext = """
|
||||
<br/><br/><br/>
|
||||
La Chaux-de-Fonds, le {current_date}<br/>
|
||||
N/réf.:ASH/val<br/>
|
||||
<br/><br/><br/>
|
||||
<strong>Travail de diplôme</strong>
|
||||
<br/><br/><br/>
|
||||
{expert_title},<br/><br/>
|
||||
Vous avez accepté de fonctionner comme expert{expert_accord} pour un travail de diplôme de l'un-e de nos
|
||||
étudiant-e-s. Nous vous remercions très chaleureusement de votre disponibilité.<br/><br/>
|
||||
En annexe, nous avons l'avantage de vous remettre le travail de {student_civility_full_name},
|
||||
ainsi que la grille d'évaluation commune aux deux membres du jury.<br/><br/>
|
||||
La soutenance de ce travail de diplôme se déroulera le:<br/><br/>
|
||||
"""
|
||||
self.story.append(Paragraph(ptext.format(
|
||||
current_date=django_format(date.today(), 'j F Y'),
|
||||
expert_title=student.expert.title,
|
||||
expert_accord=student.expert.adjective_ending,
|
||||
student_civility_full_name=student.civility_full_name,
|
||||
), style_normal))
|
||||
ptext = "<br/>{0} à l'Ecole Santé-social Pierre-Coullery, salle {1}<br/><br/>"
|
||||
self.story.append(Paragraph(ptext.format(
|
||||
django_format(student.date_exam, 'l j F Y à H\hi'),
|
||||
student.room
|
||||
), style_bold_center))
|
||||
|
||||
ptext = """
|
||||
<br/>
|
||||
L'autre membre du jury sera {internal_expert_civility} {internal_expert_full_name}, {internal_expert_role} dans notre école.<br/>
|
||||
<br/>
|
||||
Par ailleurs, nous nous permettons de vous faire parvenir en annexe le formulaire "Indemnisation d'experts aux examens"
|
||||
que vous voudrez bien compléter au niveau des "données privées / coordonnées de paiement" et nous retourner dans les meilleurs délais.
|
||||
<br/><br/>
|
||||
Restant à votre disposition pour tout complément d'information et en vous remerciant de
|
||||
l'attention que vous porterez à la présente, nous vous prions d'agréer, {expert_title}, l'asurance de notre considération distinguée.<br/>
|
||||
<br/><br/><br/>
|
||||
La responsable de filière:<br/>
|
||||
<br/><br/>
|
||||
Ann Schaub-Murray
|
||||
<br/><br/><br/>
|
||||
Annexes: ment.
|
||||
<br/><br/>
|
||||
Copies pour information: <br/>
|
||||
- {student_civility} {student_full_name}, {student_role} <br/>
|
||||
- {internal_expert_civility2} {internal_expert_full_name2}, {internal_expert_role2}
|
||||
"""
|
||||
self.story.append(Paragraph(ptext.format(
|
||||
internal_expert_civility=student.internal_expert.civility,
|
||||
internal_expert_full_name=student.internal_expert.full_name,
|
||||
internal_expert_role=student.internal_expert.role,
|
||||
expert_title=student.expert.title,
|
||||
student_civility=student.civility,
|
||||
student_full_name=student.full_name,
|
||||
student_role=student.role,
|
||||
internal_expert_civility2=student.internal_expert.civility,
|
||||
internal_expert_full_name2=student.internal_expert.full_name,
|
||||
internal_expert_role2=student.internal_expert.role
|
||||
), style_normal))
|
||||
self.build(self.story)
|
||||
|
|
|
|||
|
|
@ -192,6 +192,25 @@ tél. 032 886 33 00
|
|||
"""
|
||||
self.assertEqual(response.context['form'].initial['message'], expected_message)
|
||||
|
||||
def test_print_letter_ede_expert(self):
|
||||
st = Student.objects.get(first_name="Albin")
|
||||
self.client.login(username='me', password='mepassword')
|
||||
url = reverse('print-pdf-to-expert-ede', args=[st.pk])
|
||||
response = self.client.post(url, follow=True)
|
||||
self.assertContains(response, "Toutes les informations ne sont pas disponibles pour la lettre à l’expert!")
|
||||
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.post(url, follow=True)
|
||||
self.assertEqual(
|
||||
response['Content-Disposition'],
|
||||
'attachment; filename="dupond_albin.pdf"'
|
||||
)
|
||||
self.assertEqual(response['Content-Type'], 'application/pdf')
|
||||
self.assertGreater(len(response.content), 200)
|
||||
|
||||
|
||||
class PeriodTest(TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ from .models import (
|
|||
Klass, Section, Option, Student, Teacher, Corporation, CorpContact, Course, Period,
|
||||
Training, Availability,
|
||||
)
|
||||
from .pdf import UpdateDataFormPDF
|
||||
from .pdf import ExpertEDEPDF, UpdateDataFormPDF
|
||||
from .utils import is_int
|
||||
|
||||
|
||||
|
|
@ -909,6 +909,24 @@ def print_update_form(request):
|
|||
return response
|
||||
|
||||
|
||||
def print_pdf_to_expert_ede(request, pk):
|
||||
"""
|
||||
Imprime le PDF à envoyer à l'expert EDE en accompagnement du
|
||||
travail de diplôme
|
||||
"""
|
||||
student = get_object_or_404(Student, pk=pk)
|
||||
if not student.is_examination_valid:
|
||||
messages.error(request, "Toutes les informations ne sont pas disponibles pour la lettre à l’expert!")
|
||||
return redirect(reverse("admin:stages_student_change", args=(student.pk,)))
|
||||
pdf = ExpertEDEPDF(student)
|
||||
pdf.produce(student)
|
||||
|
||||
with open(pdf.filename, mode='rb') as fh:
|
||||
response = HttpResponse(fh.read(), content_type='application/pdf')
|
||||
response['Content-Disposition'] = 'attachment; filename="{0}"'.format(os.path.basename(pdf.filename))
|
||||
return response
|
||||
|
||||
|
||||
GENERAL_EXPORT_FIELDS = [
|
||||
('Num_Ele', 'ext_id'),
|
||||
('Nom_Ele', 'last_name'),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue