PDF to update students data
This commit is contained in:
parent
e08356e6eb
commit
8b673665ce
6 changed files with 167 additions and 9 deletions
|
|
@ -20,6 +20,7 @@ urlpatterns = [
|
|||
url(r'^classes/(?P<pk>\d+)/$', views.KlassView.as_view(), name='class'),
|
||||
|
||||
url(r'^imputations/export/$', views.imputations_export, name='imputations_export'),
|
||||
url(r'^print/update_form/$', views.print_update_form, name='print_update_form'),
|
||||
|
||||
# AJAX/JSON urls
|
||||
url(r'^section/(?P<pk>\d+)/periods/', views.section_periods, name='section_periods'),
|
||||
|
|
|
|||
|
|
@ -161,6 +161,18 @@ class Student(models.Model):
|
|||
def __str__(self):
|
||||
return '%s %s' % (self.last_name, self.first_name)
|
||||
|
||||
@property
|
||||
def civility(self):
|
||||
return 'Monsieur' if self.gender == 'M' else 'Madame'
|
||||
|
||||
@property
|
||||
def full_name(self):
|
||||
return '{0} {1}'.format(self.first_name, self.last_name)
|
||||
|
||||
@property
|
||||
def pcode_city(self):
|
||||
return '{0} {1}'.format(self.pcode, self.city)
|
||||
|
||||
def save(self, **kwargs):
|
||||
if self.archived and not self.archived_text:
|
||||
# Fill archived_text with training data, JSON-formatted
|
||||
|
|
@ -222,6 +234,10 @@ class Corporation(models.Model):
|
|||
sect = ' (%s)' % self.sector if self.sector else ''
|
||||
return "%s%s, %s %s" % (self.name, sect, self.pcode, self.city)
|
||||
|
||||
@property
|
||||
def pcode_city(self):
|
||||
return '{0} {1}'.format(self.pcode, self.city)
|
||||
|
||||
|
||||
class CorpContact(models.Model):
|
||||
corporation = models.ForeignKey(Corporation, verbose_name='Institution', on_delete=models.CASCADE)
|
||||
|
|
|
|||
106
stages/pdf.py
106
stages/pdf.py
|
|
@ -71,3 +71,109 @@ class ChargeSheetPDF(SimpleDocTemplate):
|
|||
self.story.append(Paragraph('la direction', style_normal))
|
||||
self.story.append(PageBreak())
|
||||
self.build(self.story)
|
||||
|
||||
|
||||
class UpdateDataFormPDF(SimpleDocTemplate):
|
||||
"""
|
||||
Génération des formulaires PDF de mise à jour des données.
|
||||
"""
|
||||
def __init__(self, path):
|
||||
super().__init__(path, pagesize=A4, topMargin=0*cm, leftMargin=2*cm)
|
||||
self.text = (
|
||||
"Afin de mettre à jour nos bases de données, nous vous serions reconnaissant "
|
||||
"de contrôler les données ci-dessous qui vous concernent selon votre filière "
|
||||
"et de retourner le présent document corrigé et complété à votre maître de classe jusqu'au "
|
||||
"vendredi 9 septembre prochain.<br/><br/>"
|
||||
"Nous vous remercions de votre précieuse collaboration.<br/><br/>"
|
||||
"Le secrétariat"
|
||||
)
|
||||
self.underline = '__________________________________'
|
||||
|
||||
def produce(self, klass):
|
||||
self.story = []
|
||||
for student in klass.student_set.all():
|
||||
self.story.append(Image(find('img/header.gif'), width=520, height=75))
|
||||
self.story.append(Spacer(0, 2*cm))
|
||||
destinataire = '{0}<br/>{1}<br/>{2}'.format(student.civility, student.full_name, student.klass)
|
||||
self.story.append(Paragraph(destinataire, style_adress))
|
||||
self.story.append(Spacer(0, 2*cm))
|
||||
self.story.append(Paragraph('{0},<br/>'.format(student.civility), style_normal))
|
||||
self.story.append(Paragraph(self.text, style_normal))
|
||||
self.story.append(Spacer(0, 2*cm))
|
||||
|
||||
data = [['Données enregistrées', 'Données corrigées et/ou complétées']]
|
||||
t = Table(data, colWidths=[8*cm, 8*cm])
|
||||
t.setStyle(TableStyle([
|
||||
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
||||
('FONT', (0, 0), (-1, -1), 'Helvetica-Bold'),
|
||||
]))
|
||||
t.hAlign = TA_CENTER
|
||||
self.story.append(t)
|
||||
|
||||
# Personal data
|
||||
data = [
|
||||
['NOM', student.last_name, self.underline],
|
||||
['PRENOM', student.first_name, self.underline],
|
||||
['ADRESSE', student.street, self.underline],
|
||||
['LOCALITE', student.pcode_city, self.underline],
|
||||
['MOBILE', student.mobile, self.underline],
|
||||
['CLASSE', student.klass, self.underline],
|
||||
['', '', ''],
|
||||
]
|
||||
|
||||
# Corporation data
|
||||
if self.is_corp_required(student.klass.name):
|
||||
if student.corporation is None:
|
||||
data.extend([
|
||||
["Données de l'Employeur", '', ''],
|
||||
['NOM', '', self.underline],
|
||||
['ADRESSE', '', self.underline],
|
||||
['LOCALITE', '', self.underline],
|
||||
['', '', '']
|
||||
])
|
||||
else:
|
||||
data.extend([
|
||||
["Données de l'Employeur", '', ''],
|
||||
['NOM', student.corporation.name, self.underline],
|
||||
['ADRESSE', student.corporation.street, self.underline],
|
||||
['LOCALITE', student.corporation.pcode_city, self.underline],
|
||||
['', '', '']
|
||||
])
|
||||
|
||||
# Instructor data
|
||||
if self.is_instr_required(student.klass.name):
|
||||
if student.instructor is None:
|
||||
data.extend([
|
||||
['Données du FEE/FPP (personne de contact pour les informations)', '', ''],
|
||||
['NOM', '', self.underline],
|
||||
['PRENOM', '', self.underline],
|
||||
['TELEPHONE', '', self.underline],
|
||||
['E-MAIL', '', self.underline],
|
||||
])
|
||||
else:
|
||||
data.extend([
|
||||
['Données du FEE/FPP (personne de contact pour les informations)', '', ''],
|
||||
['NOM', student.instructor.last_name, self.underline],
|
||||
['PRENOM', student.instructor.first_name, self.underline],
|
||||
['TELEPHONE', student.instructor.tel, self.underline],
|
||||
['E-MAIL', student.instructor.email, self.underline],
|
||||
])
|
||||
|
||||
t = Table(data, colWidths=[3*cm, 5*cm, 8*cm])
|
||||
t.setStyle(TableStyle([
|
||||
('ALIGN', (1, 0), (-1, -1), 'LEFT'),
|
||||
('FONT', (0, 0), (0, -1), 'Helvetica-Bold'),
|
||||
]))
|
||||
t.hAlign = TA_CENTER
|
||||
self.story.append(t)
|
||||
self.story.append(PageBreak())
|
||||
if len(self.story) == 0:
|
||||
self.story.append(Paragraph("Pas d'élèves dans cette classe", style_normal))
|
||||
|
||||
self.build(self.story)
|
||||
|
||||
def is_corp_required(self, klass_name):
|
||||
return any(el in klass_name for el in ['FE', 'EDS', 'EDEpe'])
|
||||
|
||||
def is_instr_required(self, klass_name):
|
||||
return any(el in klass_name for el in ['FE', 'EDS'])
|
||||
|
|
|
|||
|
|
@ -18,15 +18,24 @@ class StagesTest(TestCase):
|
|||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
Section.objects.bulk_create([
|
||||
Section(name='ASE'), Section(name='ASSC'), Section(name='EDE')
|
||||
Section(name='ASE'), Section(name='ASSC'), Section(name='EDE'), Section(name='EDS')
|
||||
])
|
||||
sect_ase = Section.objects.get(name='ASE')
|
||||
lev1 = Level.objects.create(name='1')
|
||||
lev2 = Level.objects.create(name='2')
|
||||
klass1 = Klass.objects.create(name="1ASE3", section=sect_ase, level=lev1)
|
||||
klass2 = Klass.objects.create(name="2ASE3", section=sect_ase, level=lev2)
|
||||
klass3 = Klass.objects.create(name="2EDS", section=Section.objects.get(name='EDS'), level=lev2)
|
||||
dom_hand = Domain.objects.create(name="handicap")
|
||||
dom_pe = Domain.objects.create(name="petite enfance")
|
||||
corp = Corporation.objects.create(
|
||||
name="Centre pédagogique XY", typ="Institution", street="Rue des champs 12",
|
||||
city="Moulineaux", pcode="2500",
|
||||
)
|
||||
contact = CorpContact.objects.create(
|
||||
corporation=corp, title="Monsieur", first_name="Jean", last_name="Horner",
|
||||
is_main=True, role="Responsable formation",
|
||||
)
|
||||
Student.objects.bulk_create([
|
||||
Student(first_name="Albin", last_name="Dupond", birth_date="1994-05-12",
|
||||
pcode="2300", city="La Chaux-de-Fonds", klass=klass1),
|
||||
|
|
@ -36,16 +45,10 @@ class StagesTest(TestCase):
|
|||
pcode="2053", city="Cernier", klass=klass1),
|
||||
Student(first_name="André", last_name="Allemand", birth_date="1994-10-11",
|
||||
pcode="2314", city="La Sagne", klass=klass2),
|
||||
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")
|
||||
corp = Corporation.objects.create(
|
||||
name="Centre pédagogique XY", typ="Institution", street="Rue des champs 12",
|
||||
city="Moulineaux", pcode="2500",
|
||||
)
|
||||
contact = CorpContact.objects.create(
|
||||
corporation=corp, title="Monsieur", first_name="Jean", last_name="Horner",
|
||||
is_main=True, role="Responsable formation",
|
||||
)
|
||||
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,
|
||||
|
|
@ -130,6 +133,14 @@ class StagesTest(TestCase):
|
|||
self.assertEqual(len(decoded), 2)
|
||||
self.assertEqual([item['priority'] for item in decoded], [True, False])
|
||||
|
||||
def test_export_update_forms(self):
|
||||
self.client.login(username='me', password='mepassword')
|
||||
response = self.client.get(reverse('print_update_form'))
|
||||
self.assertEqual(
|
||||
response['Content-Disposition'], 'attachment; filename="modification.zip"'
|
||||
)
|
||||
self.assertGreater(int(response['Content-Length']), 10)
|
||||
|
||||
|
||||
class PeriodTest(TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import zipfile
|
||||
from collections import OrderedDict
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
|
|
@ -23,6 +26,7 @@ from .models import (
|
|||
Klass, Section, Student, Teacher, Corporation, CorpContact, Course, Period,
|
||||
Training, Availability,
|
||||
)
|
||||
from .pdf import UpdateDataFormPDF
|
||||
from .utils import is_int
|
||||
|
||||
|
||||
|
|
@ -628,3 +632,22 @@ def imputations_export(request):
|
|||
response['Content-Disposition'] = 'attachment; filename=%s%s.xlsx' % (
|
||||
'Imputations_export', date.strftime(date.today(), '%Y-%m-%d'))
|
||||
return response
|
||||
|
||||
|
||||
def print_update_form(request):
|
||||
"""
|
||||
PDF form to update personal data
|
||||
"""
|
||||
tmp_file = tempfile.NamedTemporaryFile()
|
||||
with zipfile.ZipFile(tmp_file, mode='w', compression=zipfile.ZIP_DEFLATED) as filezip:
|
||||
for klass in Klass.objects.filter(level__gte=2).exclude(section__name='MP_ASSC').exclude(section__name='MP_ASE'):
|
||||
path = os.path.join(tempfile.gettempdir(), '{0}.pdf'.format(klass.name))
|
||||
pdf = UpdateDataFormPDF(path)
|
||||
pdf.produce(klass)
|
||||
filezip.write(pdf.filename)
|
||||
break
|
||||
|
||||
with open(filezip.filename, mode='rb') as fh:
|
||||
response = HttpResponse(fh.read(), content_type='application/zip')
|
||||
response['Content-Disposition'] = 'attachment; filename="modification.zip"'
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
<li><a href="{% url 'stages_export' %}">Exporter les données de stages</a> (récentes)</li>
|
||||
<li><a href="{% url 'stages_export' 'all' %}">Exporter les données de stages</a> (toutes)</li>
|
||||
<li><a href="{% url 'imputations_export' %}">Exporter les données comptables</a></li>
|
||||
<li><a href="{% url 'print_update_form' %}">Imprimer les formulaires de MAJ</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue