ljp vbk generation

pull/121/head
Christian Merten 11 months ago
parent 5227efe17b
commit f59a97578c
Signed by: christian.merten
GPG Key ID: D953D69721B948B3

@ -1,9 +1,20 @@
import os
from django.conf import settings
from django.http import HttpResponse
from django import template
from django.template.loader import get_template
from wsgiref.util import FileWrapper
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):
return os.path.join(os.path.join(settings.MEDIA_ROOT, "memberlists"), fp)

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-01 14:54+0100\n"
"POT-Creation-Date: 2025-02-02 00:45+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -260,6 +260,10 @@ msgstr "SJR Antrag erstellen"
msgid "Generate seminar report"
msgstr "Landesjugendplan Antrag erstellen"
#: templates/admin/members/freizeit/change_form_object_tools.html
msgid "Generate LJP V-BK form"
msgstr "Erzeuge LJP V-BK Formular"
#: templates/admin/members/freizeit/change_form_object_tools.html
msgid "Generate overview"
msgstr "Hinweise für Jugendleiter*innen erstellen"

@ -29,7 +29,7 @@ from django.forms import Textarea, RadioSelect, TypedChoiceField, CheckboxInput
from django.shortcuts import render
from django.core.exceptions import PermissionDenied, ValidationError
from .pdf import render_tex, fill_pdf_form, merge_pdfs, serve_pdf
from .excel import generate_group_overview
from .excel import generate_group_overview, VBK_3_1, VBK_3_2, generate_ljp_vbk
from contrib.admin import CommonAdminInlineMixin, CommonAdminMixin
@ -844,7 +844,7 @@ class GroupAdmin(CommonAdminMixin, admin.ModelAdmin):
class ActivityCategoryAdmin(admin.ModelAdmin):
fields = ['name', 'description']
fields = ['name', 'ljp_category', 'description']
class FreizeitAdminForm(forms.ModelForm):
@ -1045,6 +1045,13 @@ class GenerateSeminarReportForm(forms.Form):
widget=CheckboxInput(attrs={'style': 'display: inherit'}),
required=False)
class GenerateVBKForm(forms.Form):
categories = ((VBK_3_1, _('Staff training')),
(VBK_3_2, _('Educational programme')))
category = forms.ChoiceField(choices=categories, label=_('Category'))
class GenerateSjrForm(forms.Form):
def __init__(self, *args, **kwargs):
@ -1052,7 +1059,6 @@ class GenerateSjrForm(forms.Form):
super(GenerateSjrForm,self).__init__(*args,**kwargs)
self.fields['invoice'] = forms.ChoiceField(choices=self.attachments, label=_('Invoice'))
class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
@ -1116,6 +1122,29 @@ class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
return render_tex(memberlist.name + "_Notizen", 'members/notes_list.tex', context)
notes_list.short_description = _('Generate overview')
def render_seminar_vbk_options(self, request, memberlist, form):
context = dict(self.admin_site.each_context(request),
title=_('Generate LJP V-BK form'),
opts=self.opts,
memberlist=memberlist,
form=form,
object=memberlist)
return render(request, 'admin/generate_seminar_vbk.html', context=context)
def seminar_vbk(self, request, memberlist):
if not self.may_view_excursion(request, memberlist):
return self.not_allowed_view(request, memberlist)
if "apply" in request.POST:
form = GenerateVBKForm(request.POST)
if not form.is_valid():
messages.error(request, _('Please select a category.'))
return self.render_seminar_vbk_options(request, memberlist, form)
category = int(form.cleaned_data['category'])
title = memberlist.ljpproposal.title if hasattr(memberlist, 'ljpproposal') else memberlist.name
fp = generate_ljp_vbk(memberlist, category)
return serve_media(fp, 'application/xlsx')
return self.render_seminar_vbk_options(request, memberlist, GenerateVBKForm())
def render_seminar_report_options(self, request, memberlist, form):
context = dict(self.admin_site.each_context(request),
title=_('Generate seminar report'),
@ -1233,6 +1262,8 @@ class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
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_vbk" in request.POST:
return self.seminar_vbk(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:

@ -1,8 +1,9 @@
from datetime import datetime
import os
import xlsxwriter
import openpyxl
from django.conf import settings
from contrib.media import media_path
from contrib.media import media_path, find_template
from .models import WEEKDAYS
def generate_group_overview(all_groups, limit_to_public = True):
@ -67,3 +68,49 @@ def generate_group_overview(all_groups, limit_to_public = True):
workbook.close()
return filename
VBK_3_1, VBK_3_2 = 1, 2
VBK_TEMPLATES = {
VBK_3_1: 'members/LJP_VBK_3-1.xlsx',
VBK_3_2: 'members/LJP_VBK_3-2.xlsx',
}
def generate_ljp_vbk(excursion, mode):
"""
Generate the VBK forms for LJP given an excursion. Returns the filename to the filled excel file.
"""
print(mode, VBK_TEMPLATES, mode in VBK_TEMPLATES)
if not mode in VBK_TEMPLATES:
raise ValueError(f"Invalid mode {mode}.")
template_path = VBK_TEMPLATES[mode]
path = find_template(template_path)
workbook = openpyxl.load_workbook(path)
sheet = workbook.active
title = excursion.ljpproposal.title if hasattr(excursion, 'ljpproposal') else excursion.name
sheet['I6'] = settings.SEKTION_IBAN
sheet['I8'] = settings.SEKTION_ACCOUNT_HOLDER
sheet['P3'] = excursion.end.year
sheet['B4'] = f"Sektion {settings.SEKTION}"
sheet['B5'] = settings.SEKTION_STREET
sheet['B6'] = settings.SEKTION_TOWN
sheet['B7'] = settings.RESPONSIBLE_MAIL
sheet['B36'] = f"{settings.SEKTION}, {datetime.today():%d.%m.%Y}"
sheet['F19'] = f"B {excursion.date:%y}-{excursion.pk}"
sheet['D19'] = settings.SEKTION
sheet['G19'] = title
sheet['I19'] = f"von {excursion.date:%d.%m.%y} bis {excursion.end:%d.%m.%y}"
sheet['J19'] = f"{excursion.duration}"
sheet['L19'] = f"{excursion.ljp_participant_count}"
sheet['H19'] = excursion.get_ljp_activity_category()
sheet['M19'] = excursion.place
if hasattr(excursion, 'statement'):
sheet['Q19'] = f"{excursion.statement.total_theoretic}"
filename = f"LJP_V-BK_3.{mode}_{title}.xlsx"
workbook.save(media_path(filename))
return filename

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-02 00:32+0100\n"
"POT-Creation-Date: 2025-02-02 00:45+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -351,6 +351,18 @@ msgstr "Modus"
msgid "Prepend V32"
msgstr "V32 Formblatt einfügen"
#: members/admin.py
msgid "Staff training"
msgstr "Jugendleiter*innenweiterbildung"
#: members/admin.py
msgid "Educational programme"
msgstr "Themenorientierte Bildungsmaßnahme"
#: members/admin.py members/models.py
msgid "Category"
msgstr "Kategorie"
#: members/admin.py
msgid "Invoice"
msgstr "Beleg"
@ -379,6 +391,14 @@ msgstr "Kriseninterventionsliste erstellen"
msgid "Generate overview"
msgstr "Hinweise für Jugendleiter erstellen"
#: members/admin.py members/templates/admin/generate_seminar_vbk.html
msgid "Generate LJP V-BK form"
msgstr "Erzeuge LJP V-BK Formular"
#: members/admin.py
msgid "Please select a category."
msgstr "Bitte wähle eine Kategorie aus."
#: members/admin.py members/templates/admin/generate_seminar_report.html
msgid "Generate seminar report"
msgstr "Landesjugendplan Antrag erstellen"
@ -491,8 +511,7 @@ msgstr "LJP Spielart"
#: members/models.py
msgid ""
"The official category for LJP applications associated with this activity."
msgstr ""
"Die offizielle Spielart für LJP Anträge mit dieser Aktivität."
msgstr "Die offizielle Spielart für LJP Anträge mit dieser Aktivität."
#: members/models.py
msgid "Description"
@ -1046,10 +1065,6 @@ msgstr "Fortbildungstyp"
msgid "Training categories"
msgstr "Fortbildungstypen"
#: members/models.py
msgid "Category"
msgstr "Kategorien"
#: members/models.py
msgid "Comments"
msgstr "Kommentar"
@ -1073,6 +1088,7 @@ msgstr "Fortbildungen"
#: members/templates/admin/demote_to_waiter.html
#: members/templates/admin/freizeit_finance_overview.html
#: members/templates/admin/generate_seminar_report.html
#: members/templates/admin/generate_seminar_vbk.html
#: members/templates/admin/generate_sjr_application.html
#: members/templates/admin/invite_as_user.html
#: members/templates/admin/invite_for_group.html
@ -1098,6 +1114,7 @@ msgstr "Zurück auf die Warteliste setzen"
#: members/templates/admin/demote_to_waiter.html
#: members/templates/admin/freizeit_finance_overview.html
#: members/templates/admin/generate_seminar_report.html
#: members/templates/admin/generate_seminar_vbk.html
#: members/templates/admin/generate_sjr_application.html
#: members/templates/admin/invite_as_user.html
#: members/templates/admin/invite_for_group.html
@ -1389,10 +1406,36 @@ msgstr ""
"Felder im Formblatt selbst aus und unterschreibe das PDF."
#: members/templates/admin/generate_seminar_report.html
#: members/templates/admin/generate_seminar_vbk.html
#: members/templates/admin/generate_sjr_application.html
msgid "Generate"
msgstr "Erstellen"
#: members/templates/admin/generate_seminar_vbk.html
msgid ""
"Every LJP application needs a V-BK form containing the most important facts "
"about the seminar.\n"
"Here you can automatically generate such a form in Excel format."
msgstr ""
"Jeder LJP Antrag benötigt ein V-BK Formular, das die wichtigsten Randdaten "
"des Seminars enthält. Hier kannst du automatisch ein solches Formular im "
"Excel Format erstellen."
#: members/templates/admin/generate_seminar_vbk.html
msgid ""
"Your excursion currently has no cost-plan attached, hence the total costs "
"can't be automatically\n"
"calculated and added to the form."
msgstr ""
"Deine Ausfahrt hat zur Zeit keinen Kostenplan. Daher können die Gesamtkosten "
"nicht automatisch berechnet und dem Formular hinzugefügt werden."
#: members/templates/admin/generate_seminar_vbk.html
msgid ""
"Depending on the type of seminar, please select one of the two options below."
msgstr ""
"Bitte wähle aus, um welche Art von Seminar es sich handelt."
#: members/templates/admin/generate_sjr_application.html members/tests.py
msgid "Here you can generate an allowance application for the SJR."
msgstr "Hier kannst du einen SJR-Zuschussantrag erstellen."

@ -11,19 +11,10 @@ from django.template.loader import get_template
from django.conf import settings
from django.http import HttpResponse, HttpResponseRedirect
from wsgiref.util import FileWrapper
from contrib.media import media_path, media_dir, serve_media, ensure_media_dir
from contrib.media import media_path, media_dir, serve_media, ensure_media_dir, find_template
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 serve_pdf(filename_pdf):
return serve_media(filename_pdf, 'application/pdf')

@ -0,0 +1,56 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static %}
{% block extrahead %}
{{ block.super }}
{{ media }}
<script src="{% static 'admin/js/cancel.js' %}" async></script>
<script type="text/javascript" src="{% static "admin/js/vendor/jquery/jquery.js" %}"></script>
<script type="text/javascript" src="{% static "admin/js/jquery.init.js" %}"></script>
{% endblock %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} invite-waiter
{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; {% translate 'Generate LJP V-BK form' %}
</div>
{% endblock %}
{% block content %}
<p>
{% blocktrans %}Every LJP application needs a V-BK form containing the most important facts about the seminar.
Here you can automatically generate such a form in Excel format.{% endblocktrans %}
</p>
{% if not memberlist.statement %}
<p>
{% blocktrans %}Your excursion currently has no cost-plan attached, hence the total costs can't be automatically
calculated and added to the form.{% endblocktrans %}
</p>
{% endif %}
<p>
{% blocktrans %}Depending on the type of seminar, please select one of the two options below.{% endblocktrans %}
</p>
<form action="" method="post">
{% csrf_token %}
<p>
<table>
{{ form }}
</table>
</p>
<br>
<input type="hidden" name="action" value="seminar_vbk">
<input type="hidden" name="seminar_vbk">
<input class="default" style="color: $default-link-color" type="submit" name="apply"
value="{% translate 'Generate' %}">
<a href="#" class="button cancel-link">{% translate "Cancel" %}</a>
</form>
{% endblock %}

@ -24,6 +24,13 @@
</form>
</li>
<li>
<form method="post" action="{% url 'admin:members_freizeit_action' original.pk %}">
{% csrf_token %}
<input type="submit" name="seminar_vbk" value="{% trans 'Generate LJP V-BK form' %}">
</form>
</li>
<li>
<form method="post" action="{% url 'admin:members_freizeit_action' original.pk %}">
{% csrf_token %}

Loading…
Cancel
Save