diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py index b7eaaf3..24e1c6c 100644 --- a/jdav_web/members/admin.py +++ b/jdav_web/members/admin.py @@ -26,7 +26,7 @@ from django.db.models import TextField, ManyToManyField, ForeignKey, Count,\ from django.forms import Textarea, RadioSelect, TypedChoiceField from django.shortcuts import render from django.core.exceptions import PermissionDenied -from .pdf import render_tex +from .pdf import render_tex, fill_pdf_form from contrib.admin import CommonAdminInlineMixin, CommonAdminMixin @@ -669,7 +669,7 @@ class FreizeitAdmin(FilteredMemberFieldMixin, CommonAdminMixin, nested_admin.Nes list_display = ['__str__', 'date'] search_fields = ('name',) ordering = ('-date',) - actions = ['crisis_intervention_list', 'notes_list', 'seminar_report'] + actions = ['crisis_intervention_list', 'notes_list', 'seminar_report', 'sjr_application'] view_on_site = False #formfield_overrides = { # ManyToManyField: {'widget': forms.CheckboxSelectMultiple}, @@ -730,6 +730,17 @@ class FreizeitAdmin(FilteredMemberFieldMixin, CommonAdminMixin, nested_admin.Nes return render_tex(title + "_Seminarbericht", 'members/seminar_report.tex', context) seminar_report.short_description = _('Generate seminar report') + def sjr_application(self, request, queryset): + # this ensures legacy compatibilty + memberlist = queryset[0] + if not self.may_view_excursion(request, memberlist): + return self.not_allowed_view(request, memberlist) + context = memberlist.sjr_application_fields() + attachments = [b.proof.path for b in memberlist.statement.bill_set.all()] + title = memberlist.ljpproposal.title if hasattr(memberlist, 'ljpproposal') else memberlist.name + return fill_pdf_form(title + "_SJR_Antrag", 'members/sjr_template.pdf', context, attachments) + sjr_application.short_description = _('Generate SJR application') + def get_urls(self): urls = super().get_urls() @@ -750,6 +761,8 @@ class FreizeitAdmin(FilteredMemberFieldMixin, CommonAdminMixin, nested_admin.Nes return custom_urls + urls def action_view(self, request, object_id): + if "sjr_application" in request.POST: + return self.sjr_application(request, [Freizeit.objects.get(pk=object_id)]) if "seminar_report" in request.POST: return self.seminar_report(request, [Freizeit.objects.get(pk=object_id)]) if "notes_list" in request.POST: diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index 9ec0dc4..f618dea 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -973,6 +973,41 @@ class Freizeit(CommonModel): sks.append(dict(name=activity, skill_avg=skill_avg, skill_min=skill_min, skill_max=skill_max)) return (people, sks) + def sjr_application_fields(self): + members = set(map(lambda x: x.member, self.membersonlist.distinct())) + total = len(members) + total_b27_local = len([m for m in members + if m.age <= 27 and settings.SEKTION in m.town]) + total_b27_non_local = len([m for m in members + if m.age <= 27 and not settings.SEKTION in m.town]) + jls = self.jugendleiter.distinct() + title = self.ljpproposal.title if hasattr(self, 'ljpproposal') else self.name + base = {'Haushaltsjahr': str(datetime.now().year), + 'Art / Thema / Titel': title, + 'Ort': self.place, + 'Datum von': self.date.strftime('%d.%m.%Y'), + 'Datum bis': self.end.strftime('%d.%m.%Y'), + 'Dauer': str(self.duration).replace('.', ','), + 'Teilnehmenden gesamt': str(total), + 'bis 27 aus HD': str(total_b27_local), + 'bis 27 nicht aus HD': str(total_b27_non_local), + 'Verpflegungstage': str(self.duration * self.participant_count).replace('.', ','), + 'Betreuer/in': str(len(jls)), + 'Ort, Datum': '{p}, {d}'.format(p=settings.SEKTION, d=datetime.now().strftime('%d.%m.%Y'))} + print(members) + for i, m in enumerate(members): + suffix = str(' {}'.format(i + 1)) + # indexing starts at zero, but the listing in the pdf starts at 1 + if i + 1 == 1: + suffix = '' + elif i + 1 == 12: + suffix = '12' + base['Vor- und Nachname' + suffix] = m.name + base['Anschrift' + suffix] = m.address + base['Alter' + suffix] = str(m.age) + base['Status' + suffix] = str(2) + return base + @staticmethod def filter_queryset_by_permissions(member, queryset=None): if queryset is None: diff --git a/jdav_web/members/pdf.py b/jdav_web/members/pdf.py index 94b68e5..00a9978 100644 --- a/jdav_web/members/pdf.py +++ b/jdav_web/members/pdf.py @@ -4,10 +4,23 @@ import os import subprocess import time import glob +from io import BytesIO +from pypdf import PdfReader, PdfWriter +from django import template from django.template.loader import get_template from django.conf import settings from django.http import HttpResponse, HttpResponseRedirect from wsgiref.util import FileWrapper +from PIL import Image + + +def find_template(template_name): + for engine in template.engines.all(): + for loader in engine.engine.template_loaders: + for origin in loader.get_template_sources(template_name): + if os.path.exists(origin.name): + return origin.name + raise template.TemplateDoesNotExist(f"Could not find template: {template_name}") def media_path(fp): @@ -58,3 +71,43 @@ def render_tex(name, template_path, context): response['Content-Disposition'] = 'attachment; filename='+filename_pdf return response + + +def fill_pdf_form(name, template_path, fields, attachments=[]): + filename = name + "_" + datetime.today().strftime("%d_%m_%Y") + filename = filename.replace(' ', '_').replace('&', '').replace('/', '_') + # drop umlauts, accents etc. + filename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore').decode() + filename_pdf = filename + '.pdf' + + path = find_template(template_path) + + if not os.path.exists(media_dir()): + os.makedirs(media_dir()) + + reader = PdfReader(path) + writer = PdfWriter() + + writer.append(reader) + + writer.update_page_form_field_values(None, fields, auto_regenerate=False) + + for fp in attachments: + try: + img = Image.open(fp) + img_pdf = BytesIO() + img.save(img_pdf, "pdf") + writer.append(img_pdf) + except: + print("Could not add image", fp) + + with open(media_path(filename_pdf), 'wb') as output_stream: + writer.write(output_stream) + + # provide the user with the resulting pdf file + with open(media_path(filename_pdf), 'rb') as pdf: + response = HttpResponse(FileWrapper(pdf))#, content='application/pdf') + response['Content-Type'] = 'application/pdf' + response['Content-Disposition'] = 'attachment; filename='+filename_pdf + + return response diff --git a/jdav_web/members/templates/members/sjr_template.pdf b/jdav_web/members/templates/members/sjr_template.pdf new file mode 100644 index 0000000..5bb7239 Binary files /dev/null and b/jdav_web/members/templates/members/sjr_template.pdf differ diff --git a/jdav_web/templates/admin/members/freizeit/change_form_object_tools.html b/jdav_web/templates/admin/members/freizeit/change_form_object_tools.html index a8218d7..99af802 100644 --- a/jdav_web/templates/admin/members/freizeit/change_form_object_tools.html +++ b/jdav_web/templates/admin/members/freizeit/change_form_object_tools.html @@ -10,6 +10,13 @@ +
  • +
    + {% csrf_token %} + +
    +
  • +
  • {% csrf_token %}