[candidats] Add summary print action

This commit is contained in:
Claude Paroz 2018-01-26 19:15:10 +01:00
parent ffb54c9f78
commit 50af7f321c
5 changed files with 218 additions and 5 deletions

View file

@ -1,3 +1,6 @@
import os
import tempfile
import zipfile
from collections import OrderedDict
from datetime import date
@ -5,11 +8,13 @@ from django import forms
from django.contrib import admin
from django.core.mail import EmailMessage
from django.db.models import BooleanField
from django.http import HttpResponse
from django.template import loader
from django.urls import reverse
from stages.exports import OpenXMLExport
from .models import Candidate, Interview, GENDER_CHOICES
from .pdf import InscriptionSummaryPDF
def export_candidates(modeladmin, request, queryset):
@ -84,10 +89,29 @@ def send_confirmation_mail(modeladmin, request, queryset):
candidate.date_confirmation_mail = date.today()
candidate.save()
modeladmin.message_user(request, "%d messages de confirmation ont été envoyés." % email_sent)
send_confirmation_mail.short_description = "Envoyer email de confirmation"
def print_summary(modeladmin, request, queryset):
"""
Print a summary of inscription
"""
filename = 'archive_InscriptionResumes.zip'
path = os.path.join(tempfile.gettempdir(), filename)
with zipfile.ZipFile(path, mode='w', compression=zipfile.ZIP_DEFLATED) as filezip:
for candidate in queryset:
pdf = InscriptionSummaryPDF(candidate)
pdf.produce(candidate)
filezip.write(pdf.filename)
with open(filezip.filename, mode='rb') as fh:
response = HttpResponse(fh.read(), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename="{0}"'.format(filename)
return response
print_summary.short_description = 'Imprimer résumé'
class CandidateAdminForm(forms.ModelForm):
interview = forms.ModelChoiceField(queryset=Interview.objects.all(), required=False)
@ -123,7 +147,7 @@ class CandidateAdmin(admin.ModelAdmin):
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]
actions = [export_candidates, send_confirmation_mail, print_summary]
fieldsets = (
(None, {
'fields': (('first_name', 'last_name', 'gender'),

View file

@ -138,6 +138,9 @@ class Candidate(models.Model):
else:
return ''
def get_ok(self, fieldname):
return 'OK' if getattr(self, fieldname) is True else 'NON'
INTERVIEW_CHOICES = (
('N', 'Normal'),

113
candidats/pdf.py Normal file
View file

@ -0,0 +1,113 @@
import os
import tempfile
from reportlab.lib.enums import TA_LEFT
from reportlab.lib.styles import ParagraphStyle as PS
from reportlab.lib.units import cm
from reportlab.platypus import Paragraph, Preformatted, Table, TableStyle
from django.utils.text import slugify
from stages.pdf import EpcBaseDocTemplate
from .models import (
AES_ACCORDS_CHOICES, DIPLOMA_CHOICES, DIPLOMA_STATUS_CHOICES,
OPTION_CHOICES, RESIDENCE_PERMITS_CHOICES,
)
style_normal = PS(name='CORPS', fontName='Helvetica', fontSize=9, alignment=TA_LEFT)
style_normal_bold = PS(name='CORPS', fontName='Helvetica-Bold', fontSize=9, alignment=TA_LEFT, spaceBefore=0.5 * cm)
class InscriptionSummaryPDF(EpcBaseDocTemplate):
"""
PDF for summary of inscription
"""
def __init__(self, candidate, **kwargs):
filename = slugify('{0}_{1}'.format(candidate.last_name, candidate.first_name)) + '.pdf'
path = os.path.join(tempfile.gettempdir(), filename)
super().__init__(path, title="Dossier d'inscription", **kwargs)
self.setNormalTemplatePage()
def produce(self, candidate):
# personal data
options = dict(OPTION_CHOICES)
diploma = dict(DIPLOMA_CHOICES)
diploma_status = dict(DIPLOMA_STATUS_CHOICES)
aes_accords = dict(AES_ACCORDS_CHOICES)
residence_permits = dict(RESIDENCE_PERMITS_CHOICES)
myTableStyle = TableStyle([
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
('FONT', (0, 0), (-1, -1), 'Helvetica'),
('SIZE', (0, 0), (-1, -1), 8)
])
self.story.append(Paragraph("Données personnelles", style_normal_bold))
data = [
['Nom: ', candidate.last_name, 'Date de naissance:', candidate.birth_date],
['Prénom:', candidate.first_name, 'Canton:', candidate.district],
['N° de tél.:', candidate.mobile, '',''],
]
t = Table(data, colWidths=[2 * cm, 6 * cm, 4 * cm, 5 * cm], hAlign=TA_LEFT)
t.setStyle(myTableStyle)
self.story.append(t)
# Chosen Option
data = []
self.story.append(Paragraph("Option choisie", style_normal_bold))
data.append([options.get(candidate.option, '')])
t = Table(data, colWidths=[17 * cm], hAlign=TA_LEFT)
t.setStyle(myTableStyle)
self.story.append(t)
# Diploma
data = []
self.story.append(Paragraph("Titres / diplômes /attest. prof.", style_normal_bold))
data.append([
diploma[candidate.diploma],
'{0} {1}'.format(candidate.diploma_detail, diploma_status[candidate.diploma_status])
])
if candidate.diploma == 1: # CFC ASE
data.append(['Evaluation du dernier stage', candidate.get_ok('work_certificate')])
elif candidate.diploma == 2: # CFC autre domaine
data.append(['Attestation de 800h. min. domaine Enfance',candidate.get_ok('certif_of_800_childhood')])
data.append(["Bilan de l'activité professionnelle", candidate.get_ok('work_certificate')])
elif candidate.diploma == 3 or candidate.diploma == 4: # Matu. aca ou ECG ou Portfolio
data.append(['Attestation de 800h. min. domaine Général', candidate.get_ok('certif_800_general')])
data.append(['Attestation de 800h. min. domaine Enfance', candidate.get_ok('certif_of_800_childhood')])
data.append(["Bilan de l'activité professionnelle", candidate.get_ok('work_certificate')])
if candidate.option != 'PS':
data.append(["Contrat de travail", candidate.get_ok('contract')])
data.append(["Promesse d'engagement", candidate.get_ok('promise')])
data.append(["Taux d'activité", candidate.activity_rate])
t = Table(data, colWidths=[12 * cm, 5 * cm], hAlign=TA_LEFT)
t.setStyle(myTableStyle)
self.story.append(t)
# Others documents
data = []
self.story.append(Paragraph("Autres documents", style_normal_bold))
docs = [
'registration_form', 'certificate_of_payement', 'police_record', 'cv', 'has_photo',
'reflexive_text', 'marks_certificate', 'aes_accords', 'residence_permits'
]
for doc in docs:
data.append([candidate._meta.get_field(doc).verbose_name, candidate.get_ok(doc)])
data.append(['Validation des accords AES', aes_accords[candidate.aes_accords]])
data.append(['Autorisation de séjour (pour les personnes étrangères)', residence_permits[candidate.residence_permits]])
t = Table(data, colWidths=[12 * cm, 5 * cm], hAlign=TA_LEFT)
t.setStyle(myTableStyle)
self.story.append(t)
# Remarks
data = []
self.story.append(Paragraph("Remarques", style_normal_bold))
data.append([Preformatted(candidate.comment, style_normal, maxLineLength=100)])
t = Table(data, colWidths=[17 * cm], hAlign=TA_LEFT)
t.setStyle(myTableStyle)
self.story.append(t)
self.build(self.story)

View file

@ -163,3 +163,22 @@ tél. 032 886 33 00"""
self.assertEqual(mail.outbox[0].subject, "Procédure de qualification")
henri.refresh_from_db()
self.assertIsNotNone(henri.convocation_date)
def test_summary_pdf(self):
ede = Section.objects.create(name='EDE')
cand = Candidate.objects.create(
first_name='Henri', last_name='Dupond', gender='M', section=ede,
email='henri@example.org', deposite_date=date.today()
)
change_url = reverse('admin:candidats_candidate_changelist')
self.client.login(username='me', password='mepassword')
response = self.client.post(change_url, {
'action': 'print_summary',
'_selected_action': Candidate.objects.values_list('pk', flat=True)
}, follow=True)
self.assertEqual(
response['Content-Disposition'],
'attachment; filename="archive_InscriptionResumes.zip"'
)
self.assertEqual(response['Content-Type'], 'application/zip')
self.assertGreater(len(response.content), 200)

View file

@ -6,20 +6,74 @@ from django.conf import settings
from django.contrib.staticfiles.finders import find
from django.utils.text import slugify
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer,
PageBreak, Table, TableStyle, Image)
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle as PS
from reportlab.platypus import (
Frame, Image, NextPageTemplate, PageBreak, PageTemplate, Paragraph,
SimpleDocTemplate, Spacer, Table, TableStyle,
)
style_normal = PS(name='CORPS', fontName='Helvetica', fontSize=8, alignment = TA_LEFT)
style_bold = PS(name='CORPS', fontName='Helvetica-Bold', fontSize=10, alignment = TA_LEFT)
style_title = PS(name='CORPS', fontName='Helvetica-Bold', fontSize=12, alignment = TA_LEFT, spaceBefore=2*cm)
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)
LOGO_EPC = find('img/logo_EPC.png')
LOGO_ESNE = find('img/logo_ESNE.png')
class EpcBaseDocTemplate(SimpleDocTemplate):
filiere = 'Formation EDE'
def __init__(self, filename, title='', pagesize=A4):
super().__init__(
filename, pagesize=pagesize, _pageBreakQuick=0,
lefMargin=1.5 * cm, bottomMargin=1.5 * cm, topMargin=1.5 * cm, rightMargin=1.5 * cm
)
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
)
canvas.line(doc.leftMargin, doc.height - 0.5 * cm, doc.width + doc.leftMargin, doc.height - 0.5 * cm)
canvas.drawString(doc.leftMargin, doc.height - 1.1 * cm, self.filiere)
canvas.drawRightString(doc.width + doc.leftMargin, doc.height - 1.1 * cm, self.title)
canvas.line(doc.leftMargin, doc.height - 1.3 * cm, doc.width + doc.leftMargin, doc.height - 1.3 * cm)
canvas.restoreState()
def later_header(self, canvas, doc):
canvas.saveState()
canvas.line(doc.leftMargin, doc.height + 1 * cm, doc.width + doc.leftMargin, doc.height + 1 * cm)
canvas.drawString(doc.leftMargin, doc.height + 0.5 * cm, self.filiere)
canvas.drawRightString(doc.width + doc.leftMargin, doc.height + 0.5 * cm, self.title)
canvas.line(doc.leftMargin, doc.height + 0.2 * cm, doc.width + doc.leftMargin, doc.height + 0.2 * cm)
canvas.restoreState()
def setNormalTemplatePage(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
)
later_pages_table_frame = Frame(
self.leftMargin, self.bottomMargin, self.width + 1 * cm, self.height - 2 * cm,
id='later_table', showBoundary=0, leftPadding=0 * cm
)
# Page template
first_page = PageTemplate(id='FirstPage', frames=[first_page_table_frame], onPage=self.header)
later_pages = PageTemplate(id='LaterPages', frames=[later_pages_table_frame], onPage=self.later_header)
self.addPageTemplates([first_page, later_pages])
self.story = [NextPageTemplate(['*', 'LaterPages'])]
class ChargeSheetPDF(SimpleDocTemplate):
"""