Import and send student PDF bulletins

This commit is contained in:
Claude Paroz 2018-01-11 16:57:47 +01:00
parent b578ccc363
commit 7a09a92bde
8 changed files with 162 additions and 2 deletions

View file

@ -34,3 +34,7 @@ class PeriodForm(forms.Form):
class UploadHPFileForm(forms.Form):
upload = forms.FileField(label='Fichier HyperPlanning')
class UploadBulletinForm(forms.Form):
upload = forms.FileField(label='Bulletins CLOEE (pdf)')

View file

@ -189,7 +189,7 @@ class Student(models.Model):
@property
def civility(self):
return 'Monsieur' if self.gender == 'M' else 'Madame'
return {'M': 'Monsieur', 'F': 'Madame'}.get(self.gender, '')
@property
def full_name(self):

Binary file not shown.

View file

@ -4,6 +4,7 @@ from datetime import date
from django.conf import settings
from django.contrib.auth.models import User
from django.core import mail
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils.html import escape
@ -348,3 +349,32 @@ class ImportTests(TestCase):
self.assertContains(response, "NoSIRET est vide à ligne 4. Ligne ignorée")
st1.refresh_from_db()
self.assertEqual(st1.instructor.last_name, 'Geiser')
def test_import_and_send_bulletins(self):
lev1 = Level.objects.create(name='1')
klass1 = Klass.objects.create(
name='1ASEFEa',
section=Section.objects.create(name='ASE'),
level=lev1,
)
Student.objects.bulk_create([
Student(first_name="Albin", last_name="Dupond", birth_date="1994-05-12", gender='M',
pcode="2300", city="La Chaux-de-Fonds", email="albin@example.org",
klass=klass1),
Student(first_name="Justine", last_name="Varrin", birth_date="1994-07-12",
pcode="2000", city="Neuchâtel", email="justine@example.org", klass=klass1),
Student(first_name="Elvire", last_name="Hickx", birth_date="1994-05-20",
pcode="2053", city="Cernier", email="elvire@example.org", klass=klass1),
])
path = os.path.join(os.path.dirname(__file__), 'test_files', '1ASEFEa.pdf')
self.client.login(username='me', password='mepassword')
with open(path, 'rb') as fh:
response = self.client.post(reverse('import-bulletins'), {'upload': fh}, follow=True)
messages = [str(msg) for msg in response.context['messages']]
self.assertIn("Impossible de trouver un fichier PDF pour l'étudiant Hickx Elvire", messages)
self.assertIn('2 messages sur 3 élèves ont été envoyés', messages)
self.assertEqual(len(mail.outbox), 2)
# Second email as bcc
self.assertEqual(mail.outbox[0].recipients(), ['albin@example.org', 'me@example.org'])
self.assertEqual(mail.outbox[1].recipients(), ['justine@example.org', 'me@example.org'])
self.assertIn("le bulletin scolaire de Monsieur Albin Dupond", mail.outbox[0].body)

View file

@ -1,5 +1,8 @@
import json
import os
import re
from subprocess import PIPE, Popen, call
import tempfile
import zipfile
from collections import OrderedDict
@ -10,15 +13,18 @@ from tabimport import CSVImportedFile, FileFactory
from django.conf import settings
from django.contrib import messages
from django.core.files import File
from django.core.mail import EmailMessage
from django.db.models import Case, Count, When, Q
from django.http import HttpResponse, HttpResponseNotAllowed, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template import loader
from django.urls import reverse
from django.utils.translation import ugettext as _
from django.utils.text import slugify
from django.views.generic import DetailView, FormView, TemplateView, ListView
from .exports import OpenXMLExport
from .forms import PeriodForm, StudentImportForm, UploadHPFileForm
from .forms import PeriodForm, StudentImportForm, UploadHPFileForm, UploadBulletinForm
from .models import (
Klass, Section, Option, Student, Teacher, Corporation, CorpContact, Course, Period,
Training, Availability,
@ -497,6 +503,113 @@ class HPContactsImportView(ImportViewBase):
return {'modified': obj_modified, 'errors': errors}
class ImportBulletinView(FormView):
template_name = 'file_import.html'
form_class = UploadBulletinForm
def form_valid(self, form):
upfile = form.cleaned_data['upload']
klass_name = upfile.name[:-4]
try:
klass = Klass.objects.get(name=klass_name)
except Klass.DoesNotExist:
messages.error(self.request, "La classe %s n'existe pas !" % klass_name)
return HttpResponseRedirect(reverse('admin:index'))
# Check poppler-utils presence on server
res = call(['pdftotext', '-v'], stderr=PIPE)
if res != 0:
messages.error(self.request, "Unable to find pdftotext on your system. Try to install the poppler-utils package.")
return HttpResponseRedirect(reverse('admin:index'))
# Move the file to MEDIA directory
pdf_origin = os.path.join(settings.MEDIA_ROOT, upfile.name)
with open(pdf_origin, 'wb+') as destination:
for chunk in upfile.chunks():
destination.write(chunk)
try:
self.send_bulletins(klass, pdf_origin)
except Exception as err:
if settings.DEBUG:
raise
else:
messages.error(self.request, "Erreur durant l'envoi des bulletins PDF: %s" % err)
return HttpResponseRedirect(reverse('admin:index'))
def send_bulletins(self, klass, pdf_path):
path = os.path.abspath(pdf_path)
student_regex = '[E|É]lève\s*:\s*([^\n]*)'
# Directory automatically deleted when the variable is deleted
_temp_dir = tempfile.TemporaryDirectory()
temp_dir = _temp_dir.name
os.system("pdfseparate %s %s/%s_%%d.pdf" % (path, temp_dir, os.path.basename(path)[:-4]))
# Look for student names in each separated PDF and rename PDF with student name
for filename in os.listdir(temp_dir):
p = Popen(['pdftotext', os.path.join(temp_dir, filename), '-'],
shell=False, stdout=PIPE, stderr=PIPE)
output, errors = p.communicate()
m = re.search(student_regex, output.decode('utf-8'))
if not m:
print("Unable to find student name in %s" % filename)
continue
student_name = m.groups()[0]
os.rename(
os.path.join(temp_dir, filename),
"%s.pdf" % (os.path.join(temp_dir, slugify(student_name)))
)
email_sent = 0
pdf_file_list = os.listdir(temp_dir)
students = klass.student_set.exclude(archived=True)
for student in students:
if not student.email:
messages.warning(self.request, "L'étudiant %s ne possède pas d'email." % student)
continue
context = {
'student_name': " ".join([student.civility, student.first_name, student.last_name]),
'sender_name': " ".join([self.request.user.first_name, self.request.user.last_name]),
'sender_email': self.request.user.email,
}
student_filename = slugify('{0} {1}'.format(student.last_name, student.first_name))
student_filename = '{0}.pdf'.format(student_filename)
try:
attach_idx = pdf_file_list.index(student_filename)
except ValueError:
messages.error(self.request,
"Impossible de trouver un fichier PDF pour l'étudiant %s" % student)
continue
to = [student.email]
if student.instructor and student.instructor.email:
to.append(student.instructor.email)
email = EmailMessage(
subject='Bulletins scolaires',
body=loader.render_to_string('email/bulletins_scolaires.txt', context),
from_email=self.request.user.email,
to=to,
bcc=[self.request.user.email],
)
# Attach PDF file to email
pdf_file = os.path.join(temp_dir, pdf_file_list[attach_idx])
pdf_name = 'bulletin_scol_{0}'.format(student_filename)
with open(pdf_file, 'rb') as pdf:
email.attach(pdf_name, pdf.read(), 'application/pdf')
try:
email.send(fail_silently=False)
email_sent += 1
except Exception as err:
messages.error(self.request, "Échec d'envoi pour le candidat {0} ({1})".format(student, err))
messages.warning(self.request, '{0} messages sur {1} élèves ont été envoyés'
.format(email_sent, students.count()))
EXPORT_FIELDS = [
# Student fields
('ID externe', 'student__ext_id'),