Rework report import view and add individual student report send form
This commit is contained in:
parent
7488cbcd31
commit
b966e140b8
12 changed files with 167 additions and 74 deletions
|
|
@ -36,5 +36,15 @@ class UploadHPFileForm(forms.Form):
|
|||
upload = forms.FileField(label='Fichier HyperPlanning')
|
||||
|
||||
|
||||
class UploadBulletinForm(forms.Form):
|
||||
class UploadReportForm(forms.Form):
|
||||
semester = forms.ChoiceField(label='Semestre', choices=(('1', '1'), ('2', '2')), required=True)
|
||||
upload = forms.FileField(label='Bulletins CLOEE (pdf)')
|
||||
|
||||
|
||||
class EmailStudentBaseForm(forms.Form):
|
||||
id_student = 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': 20, 'cols': 120}))
|
||||
|
|
|
|||
BIN
stages/static/img/envelope.png
Normal file
BIN
stages/static/img/envelope.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 239 B |
|
|
@ -283,6 +283,12 @@ class ImportTests(TestCase):
|
|||
def setUp(self):
|
||||
User.objects.create_user('me', 'me@example.org', 'mepassword')
|
||||
|
||||
def tearDown(self):
|
||||
# Clean uploaded bulletins
|
||||
bulletins_dir = os.path.join(settings.MEDIA_ROOT, 'bulletins')
|
||||
for f in os.listdir(bulletins_dir):
|
||||
os.remove(os.path.join(bulletins_dir, f))
|
||||
|
||||
def test_import_students(self):
|
||||
"""
|
||||
Import of the main students file.
|
||||
|
|
@ -369,12 +375,23 @@ class ImportTests(TestCase):
|
|||
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)
|
||||
response = self.client.post(
|
||||
reverse('import-reports', args=[klass1.pk]),
|
||||
data={'upload': fh, 'semester': '1'},
|
||||
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)
|
||||
self.assertIn('2 bulletins PDF ont été importés pour la classe 1ASEFEa (sur 3 élèves)', messages)
|
||||
student = Student.objects.get(last_name="Dupond")
|
||||
self.assertEqual(student.report_sem1.name, 'bulletins/1ASEFEa_1.pdf')
|
||||
|
||||
# Now send
|
||||
send_url = reverse('send-student-reports', args=[student.pk, '1'])
|
||||
response = self.client.get(send_url)
|
||||
data = response.context['form'].initial
|
||||
self.assertEqual(data['to'], "albin@example.org")
|
||||
response = self.client.post(send_url, data=data, follow=True)
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
# 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)
|
||||
|
|
|
|||
163
stages/views.py
163
stages/views.py
|
|
@ -14,7 +14,8 @@ 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.db.models import Case, Count, Value, When, Q
|
||||
from django.db.models.functions import Concat
|
||||
from django.http import HttpResponse, HttpResponseNotAllowed, HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template import loader
|
||||
|
|
@ -24,7 +25,7 @@ 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, UploadBulletinForm
|
||||
from .forms import EmailStudentBaseForm, PeriodForm, StudentImportForm, UploadHPFileForm, UploadReportForm
|
||||
from .models import (
|
||||
Klass, Section, Option, Student, Teacher, Corporation, CorpContact, Course, Period,
|
||||
Training, Availability,
|
||||
|
|
@ -503,25 +504,31 @@ class HPContactsImportView(ImportViewBase):
|
|||
return {'modified': obj_modified, 'errors': errors}
|
||||
|
||||
|
||||
class ImportBulletinView(FormView):
|
||||
class ImportReportsView(FormView):
|
||||
template_name = 'file_import.html'
|
||||
form_class = UploadBulletinForm
|
||||
form_class = UploadReportForm
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.klass = get_object_or_404(Klass, pk=kwargs['pk'])
|
||||
self.title = "Importation d'un fichier PDF de moyennes pour la classe {}".format(self.klass.name)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
upfile = form.cleaned_data['upload']
|
||||
klass_name = upfile.name[:-4]
|
||||
redirect_url = reverse('class', args=[self.klass.pk])
|
||||
|
||||
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'))
|
||||
if self.klass.name != klass_name:
|
||||
messages.error(self.request,
|
||||
"Le fichier téléchargé ne correspond pas à la classe {} !".format(self.klass.name)
|
||||
)
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
# 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'))
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
# Move the file to MEDIA directory
|
||||
pdf_origin = os.path.join(settings.MEDIA_ROOT, upfile.name)
|
||||
|
|
@ -530,15 +537,16 @@ class ImportBulletinView(FormView):
|
|||
destination.write(chunk)
|
||||
|
||||
try:
|
||||
self.send_bulletins(klass, pdf_origin)
|
||||
self.import_reports(pdf_origin, form.cleaned_data['semester'])
|
||||
except Exception as err:
|
||||
raise
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
else:
|
||||
messages.error(self.request, "Erreur durant l'envoi des bulletins PDF: %s" % err)
|
||||
return HttpResponseRedirect(reverse('admin:index'))
|
||||
messages.error(self.request, "Erreur durant l'importation des bulletins PDF: %s" % err)
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
def send_bulletins(self, klass, pdf_path):
|
||||
def import_reports(self, pdf_path, semester):
|
||||
path = os.path.abspath(pdf_path)
|
||||
student_regex = '[E|É]lève\s*:\s*([^\n]*)'
|
||||
# Directory automatically deleted when the variable is deleted
|
||||
|
|
@ -548,66 +556,101 @@ class ImportBulletinView(FormView):
|
|||
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
|
||||
pdf_count = 0
|
||||
pdf_field = 'report_sem' + semester
|
||||
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()
|
||||
output, errs = 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).order_by('last_name', 'first_name')
|
||||
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)
|
||||
# Find a student with the found student_name
|
||||
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)
|
||||
student = self.klass.student_set.exclude(archived=True
|
||||
).annotate(fullname=Concat('last_name', Value(' '), 'first_name')).get(fullname=student_name)
|
||||
except Student.DoesNotExist:
|
||||
messages.warning(
|
||||
self.request,
|
||||
"Impossible de trouver l'étudiant {} dans la classe {}".format(student_name, self.klass.name)
|
||||
)
|
||||
continue
|
||||
with open(os.path.join(temp_dir, filename), 'rb') as pdf:
|
||||
getattr(student, pdf_field).save(filename, File(pdf), save=True)
|
||||
student.save()
|
||||
pdf_count += 1
|
||||
|
||||
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],
|
||||
messages.success(
|
||||
self.request,
|
||||
'{0} bulletins PDF ont été importés pour la classe {1} (sur {2} élèves)'.format(
|
||||
pdf_count, self.klass.name,
|
||||
self.klass.student_set.exclude(archived=True).count()
|
||||
)
|
||||
# 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()))
|
||||
class SendStudentReportsView(FormView):
|
||||
template_name = 'email_report.html'
|
||||
form_class = EmailStudentBaseForm
|
||||
|
||||
def get_initial(self):
|
||||
initial = super().get_initial()
|
||||
self.student = Student.objects.get(pk=self.kwargs['pk'])
|
||||
self.semestre = self.kwargs['semestre']
|
||||
|
||||
to = [self.student.email]
|
||||
if self.student.instructor and self.student.instructor.email:
|
||||
to.append(self.student.instructor.email)
|
||||
|
||||
context = {
|
||||
'student': self.student,
|
||||
'sender': self.request.user,
|
||||
}
|
||||
|
||||
initial.update({
|
||||
'id_student': self.student.pk,
|
||||
'cci': self.request.user.email,
|
||||
'to': '; '.join(to),
|
||||
'subject': "Bulletin semestriel",
|
||||
'message': loader.render_to_string('email/bulletins_scolaires.txt', context),
|
||||
'sender': self.request.user.email,
|
||||
})
|
||||
return initial
|
||||
|
||||
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(';'),
|
||||
)
|
||||
# Attach PDF file to email
|
||||
student_filename = slugify('{0} {1}'.format(self.student.last_name, self.student.first_name))
|
||||
student_filename = '{0}.pdf'.format(student_filename)
|
||||
#pdf_file = os.path.join(dir_klass, pdf_file_list[attach_idx])
|
||||
pdf_name = 'bulletin_scol_{0}'.format(student_filename)
|
||||
with open(getattr(self.student, 'report_sem%d' % self.semestre).path, 'rb') as pdf:
|
||||
email.attach(pdf_name, pdf.read(), 'application/pdf')
|
||||
|
||||
try:
|
||||
email.send()
|
||||
except Exception as err:
|
||||
messages.error(self.request, "Échec d’envoi pour l'étudiant {0} ({1})".format(self.student, err))
|
||||
else:
|
||||
messages.success(self.request, "Le message a été envoyé.")
|
||||
return HttpResponseRedirect(reverse('class', args=[self.student.klass.pk]))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'candidat': self.student,
|
||||
'title': 'Envoi du bulletin semestriel',
|
||||
'pdf_field': getattr(self.student, 'report_sem%d' % self.semestre),
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
EXPORT_FIELDS = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue