tex: rewrite pdf generation, add seminar report, add some utility functions

v1-0-stable
Christian Merten 3 years ago
parent 2eb664e35e
commit ed8f3e9c0e
Signed by: christian.merten
GPG Key ID: D953D69721B948B3

@ -49,7 +49,7 @@ class Statement(models.Model):
null=True,
on_delete=models.SET_NULL)
night_cost = models.DecimalField(verbose_name=_('Price per night'), default=0, decimal_places=2, max_digits=3)
night_cost = models.DecimalField(verbose_name=_('Price per night'), default=0, decimal_places=2, max_digits=5)
submitted = models.BooleanField(verbose_name=_('Submitted'), default=False)
submitted_date = models.DateTimeField(verbose_name=_('Submitted on'), default=None, null=True)
@ -189,6 +189,10 @@ class Statement(models.Model):
def total_bills(self):
return sum([bill.amount for bill in self.bill_set.all() if bill.costs_covered])
@property
def total_bills_theoretic(self):
return sum([bill.amount for bill in self.bill_set.all()])
@property
def euro_per_km(self):
if self.excursion is None:
@ -214,6 +218,14 @@ class Statement(models.Model):
return cvt_to_decimal(self.excursion.duration * self.ALLOWANCE_PER_DAY)
@property
def total_allowance(self):
return self.allowance_per_yl * self.real_staff_count
@property
def total_transportation(self):
return self.transportation_per_yl * self.real_staff_count
@property
def real_night_cost(self):
return min(self.night_cost, 11)
@ -225,6 +237,10 @@ class Statement(models.Model):
return self.excursion.night_count * self.real_night_cost
@property
def total_nights(self):
return self.nights_per_yl * self.real_staff_count
@property
def total_per_yl(self):
return self.transportation_per_yl \
@ -269,6 +285,10 @@ class Statement(models.Model):
def total(self):
return self.total_bills + self.total_staff
@property
def total_theoretic(self):
return self.total_bills_theoretic + self.total_staff
def total_pretty(self):
return "{}".format(self.total)
total_pretty.short_description = _('Total')

@ -9,6 +9,7 @@ import random
import string
from functools import partial, update_wrapper
from django.template.loader import get_template
from django.urls import path, reverse
from django.http import HttpResponse, HttpResponseRedirect
from wsgiref.util import FileWrapper
@ -22,6 +23,7 @@ from django.db.models import TextField, ManyToManyField, ForeignKey, Count,\
Sum, Case, Q, F, When, Value, IntegerField, Subquery, OuterRef
from django.forms import Textarea, RadioSelect, TypedChoiceField
from django.shortcuts import render
from .pdf import render_tex
import nested_admin
@ -596,7 +598,7 @@ class FreizeitAdmin(nested_admin.NestedModelAdmin):
list_display = ['__str__', 'date']
search_fields = ('name',)
ordering = ('-date',)
actions = ['convert_to_pdf', 'generate_notes', 'convert_to_ljp']
actions = ['crisis_intervention_list', 'notes_list', 'seminar_report']
#formfield_overrides = {
# ManyToManyField: {'widget': forms.CheckboxSelectMultiple},
# ForeignKey: {'widget': apply_select2(forms.Select)}
@ -608,336 +610,25 @@ class FreizeitAdmin(nested_admin.NestedModelAdmin):
def __init__(self, *args, **kwargs):
super(FreizeitAdmin, self).__init__(*args, **kwargs)
def convert_to_pdf(self, request, queryset):
"""Converts a member list to pdf.
"""
def crisis_intervention_list(self, request, queryset):
for memberlist in queryset:
# create a unique filename
filename = memberlist.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_table = 'table_' + filename
filename_tex = filename + '.tex'
filename_pdf = filename + '.pdf'
# open temporary file for table
with open(media_path(filename_table), 'w+', encoding='utf-8') as f:
if memberlist.membersonlist.count() == 0:
f.write('{0} & {1} & {2} & {3} \\\\ \n'.format(
'keine Teilnehmer', '-', '-', '-'
))
for memberonlist in memberlist.membersonlist.all():
# write table of members in latex compatible format
member = memberonlist.member
# use parents phone number if available
phone_number = member.phone_number_parents if\
member.phone_number_parents else member.phone_number
# use parents email address if available
email = member.email_parents if\
member.email_parents else member.email
line = '{0} {1} & {2} & {3} & \\Email{{{4}}} \\\\ \n'.format(
esc_all(memberonlist.member.prename),
esc_all(memberonlist.member.lastname),
esc_all(memberonlist.member.address),
esc_all(memberonlist.member.contact_phone_number),
memberonlist.member.contact_email) # don't escape here, because url is used in tex
f.write(line)
# copy and adapt latex memberlist template
shutil.copy(media_path('memberlist_template.tex'),
media_path(filename_tex))
# read in template
with open(media_path(filename_tex), 'r', encoding='utf-8') as f:
template_content = f.read()
# adapt template
name = esc_all(memberlist.name)
template_content = template_content.replace('ACTIVITY', name)
groups = ', '.join(g.name for g in
memberlist.groups.all())
template_content = template_content.replace('GROUP',
esc_all(groups))
destination = esc_all(memberlist.destination)
template_content = template_content.replace('DESTINATION',
destination)
place = esc_all(memberlist.place)
template_content = template_content.replace('PLACE', place)
template_content = template_content.replace('MEMBERLIST-DATE',
datetime.today().strftime('%d.%m.%Y'))
time_period = memberlist.date.strftime('%d.%m.%Y')
if memberlist.end != memberlist.date:
time_period += " - " + memberlist.end.strftime('%d.%m.%Y')
template_content = template_content.replace('TIME-PERIOD', time_period)
jugendleiter = ', '.join(j.name for j in memberlist.jugendleiter.all())
template_content = template_content.replace('JUGENDLEITER', jugendleiter)
# create tickboxes for tour type
tour_type = ''
for tt in ['Gemeinschaftstour', 'Führungstour', 'Ausbildung']:
print(memberlist.tour_type)
if tt == memberlist.get_tour_type():
tour_type += '\\tickedbox ' + tt
else:
tour_type += '\\checkbox'
tour_type += '\\enspace ' + tt
tour_type += '\\qquad \\qquad '
template_content = template_content.replace('TOUR-TYPE', tour_type)
# create tickboxes for tour approach
tour_approach = ''
for tt in ['Muskelkraft', 'Öffentliche VM', 'Fahrgemeinschaften']:
print(memberlist.tour_approach)
if tt == memberlist.get_tour_approach():
tour_approach += '\\tickedbox ' + tt
else:
tour_approach += '\\checkbox'
tour_approach += '\\enspace ' + tt
tour_approach += '\\qquad \\qquad '
template_content = template_content.replace('TOUR-APPROACH', tour_approach)
template_content = template_content.replace('TABLE-NAME',
filename_table)
# write adapted template to file
with open(media_path(filename_tex), 'w', encoding='utf-8') as f:
f.write(template_content)
# compile using pdflatex
oldwd = os.getcwd()
os.chdir(media_dir())
subprocess.call(['pdflatex', filename_tex])
time.sleep(1)
# do some cleanup
for f in glob.glob('*.log'):
os.remove(f)
for f in glob.glob('*.aux'):
os.remove(f)
os.remove(filename_tex)
os.remove(filename_table)
os.chdir(oldwd)
context = dict(memberlist=memberlist)
return render_tex(memberlist.name + "_Krisenliste", 'members/crisis_intervention_list.tex', context)
crisis_intervention_list.short_description = _('Generate crisis intervention list')
# 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
convert_to_pdf.short_description = _('Convert to PDF')
def generate_notes(self, request, queryset):
"""Generates a short note for the jugendleiter"""
def notes_list(self, request, queryset):
for memberlist in queryset:
# unique filename
filename = memberlist.name + "_note_" + 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_tex = filename + '.tex'
filename_pdf = filename + '.pdf'
# generate table
table = ""
activities = [a.name for a in memberlist.activity.all()]
skills = {a: [] for a in activities}
for memberonlist in memberlist.membersonlist.all():
m = memberonlist.member
qualities = []
for activity, value in m.get_skills().items():
if activity not in activities:
continue
skills[activity].append(value)
qualities.append("\\textit{%s:} %s" % (activity, value))
comment = ". ".join(c for c
in (m.comments,
memberonlist.comments) if
c).replace("..", ".")
line = '{0} {1} & {2} & {3} \\\\'.format(
esc_ampersand(m.prename), esc_ampersand(m.lastname),
esc_ampersand(", ".join(qualities)),
esc_ampersand(comment) or "---")
table += esc_underscore(line)
table_qualities = ""
for activity in activities:
skill_avg = 0 if len(skills[activity]) == 0 else\
sum(skills[activity]) / len(skills[activity])
skill_min = 0 if len(skills[activity]) == 0 else\
min(skills[activity])
skill_max = 0 if len(skills[activity]) == 0 else\
max(skills[activity])
line = '{0} & {1} & {2} & {3} \\\\ \n'.format(
esc_ampersand(activity),
skill_avg,
skill_min,
skill_max
)
table_qualities += esc_underscore(line)
# copy template
shutil.copy(media_path('membernote_template.tex'),
media_path(filename_tex))
# read in template
with open(media_path(filename_tex), 'r', encoding='utf-8') as f:
template_content = f.read()
# adapt template
name = esc_all(memberlist.name)
template_content = template_content.replace('ACTIVITY', name)
groups = ', '.join(g.name for g in memberlist.groups.all())
template_content = template_content.replace('GROUP',
esc_all(groups))
destination = esc_all(memberlist.destination)
template_content = template_content.replace('DESTINATION', destination)
place = esc_all(memberlist.place)
template_content = template_content.replace('PLACE', place)
template_content = template_content.replace('MEMBERLIST-DATE',
datetime.today().strftime('%d.%m.%Y'))
time_period = memberlist.date.strftime('%d.%m.%Y')
if memberlist.end != memberlist.date:
time_period += " - " + memberlist.end.strftime('%d.%m.%Y')
template_content = template_content.replace('TIME-PERIOD', time_period)
jugendleiter = ', '.join(j.name for j in memberlist.jugendleiter.all())
template_content = template_content.replace('JUGENDLEITER', jugendleiter)
template_content = template_content.replace('TABLE-QUALITIES',
table_qualities)
template_content = template_content.replace('TABLE', table)
# write adapted template to file
with open(media_path(filename_tex), 'w', encoding='utf-8') as f:
f.write(template_content)
# compile using pdflatex
oldwd = os.getcwd()
os.chdir(media_dir())
subprocess.call(['pdflatex', filename_tex])
time.sleep(1)
people, skills = memberlist.skill_summary
context = dict(memberlist=memberlist, people=people, skills=skills)
return render_tex(memberlist.name + "_Notizen", 'members/notes_list.tex', context)
notes_list.short_description = _('Generate overview')
# do some cleanup
for f in glob.glob('*.log'):
os.remove(f)
for f in glob.glob('*.aux'):
os.remove(f)
os.remove(filename_tex)
os.chdir(oldwd)
# provide the user with the resulting pdf file
with open(media_path(filename_pdf), 'rb') as pdf:
response = HttpResponse(FileWrapper(pdf))
response['Content-Type'] = 'application/pdf'
response['Content-Disposition'] = 'attachment; filename=' + filename_pdf
return response
generate_notes.short_description = _('Generate overview')
def convert_to_ljp(self, request, queryset):
"""Converts a member list to pdf but without email and with birth date.
Suitable for LJP lists.
"""
def seminar_report(self, request, queryset):
for memberlist in queryset:
# create a unique filename
filename = memberlist.name + "_ljp_" + 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_table = 'table_' + filename
filename_tex = filename + '.tex'
filename_pdf = filename + '.pdf'
# open temporary file for table
with open(media_path(filename_table), 'w+', encoding='utf-8') as f:
if memberlist.membersonlist.count() == 0:
f.write('{0} & {1} & {2} & {3} \\\\ \n'.format(
'keine Teilnehmer', '-', '-', '-'
))
for memberonlist in memberlist.membersonlist.all():
# write table of members in latex compatible format
member = memberonlist.member
# use parents phone number if available
phone_number = member.phone_number_parents if\
member.phone_number_parents else member.phone_number
# use parents email address if available
email = member.email_parents if\
member.email_parents else member.email
line = '{0} {1} & {2} & {3} & & & \\\\ \\hline \n'.format(
esc_all(memberonlist.member.prename),
esc_all(memberonlist.member.lastname),
esc_all(memberonlist.member.address),
esc_all(memberonlist.member.birth_date.strftime("%d.%m.%Y")))
f.write(line)
# copy and adapt latex memberlist template
shutil.copy(media_path('memberlist_ljp_template.tex'),
media_path(filename_tex))
# read in template
with open(media_path(filename_tex), 'r', encoding='utf-8') as f:
template_content = f.read()
# adapt template
name = esc_all(memberlist.name)
template_content = template_content.replace('ACTIVITY', name)
groups = ', '.join(g.name for g in
memberlist.groups.all())
template_content = template_content.replace('GROUP',
esc_all(groups))
destination = esc_all(memberlist.destination)
template_content = template_content.replace('DESTINATION',
destination)
place = esc_all(memberlist.place)
template_content = template_content.replace('PLACE', place)
template_content = template_content.replace('MEMBERLIST-DATE',
datetime.today().strftime('%d.%m.%Y'))
time_period = memberlist.date.strftime('%d.%m.%Y')
if memberlist.end != memberlist.date:
time_period += " - " + memberlist.end.strftime('%d.%m.%Y')
template_content = template_content.replace('TIME-PERIOD', time_period)
jugendleiter = ', '.join(j.name for j in memberlist.jugendleiter.all())
template_content = template_content.replace('JUGENDLEITER', jugendleiter)
template_content = template_content.replace('TABLE-NAME',
filename_table)
# write adapted template to file
with open(media_path(filename_tex), 'w', encoding='utf-8') as f:
f.write(template_content)
# compile using pdflatex
oldwd = os.getcwd()
os.chdir(media_dir())
subprocess.call(['pdflatex', filename_tex])
time.sleep(1)
# do some cleanup
for f in glob.glob('*.log'):
os.remove(f)
for f in glob.glob('*.aux'):
os.remove(f)
os.remove(filename_tex)
os.remove(filename_table)
os.chdir(oldwd)
# 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
convert_to_ljp.short_description = _('Generate list for LJP')
context = dict(memberlist=memberlist)
title = memberlist.ljpproposal.title if hasattr(memberlist, 'ljpproposal') else memberlist.name
return render_tex(title + "_Seminarbericht", 'members/seminar_report.tex', context)
seminar_report.short_description = _('Generate seminar report')
class KlettertreffAdminForm(forms.ModelForm):

@ -105,6 +105,10 @@ class Person(models.Model):
"""Age of member"""
return relativedelta(datetime.today(), self.birth_date).years
@property
def birth_date_str(self):
return self.birth_date.strftime("%d.%m.%Y")
def request_mail_confirmation(self):
self.confirmed_mail = False
self.confirm_mail_key = uuid.uuid4().hex
@ -500,6 +504,26 @@ class NewMemberOnList(models.Model):
verbose_name = _('Member')
verbose_name_plural = _('Members')
@property
def comments_tex(self):
raw = ". ".join(c for c in (self.member.comments, self.comments) if c).replace("..", ".")
if not raw:
return "---"
else:
return raw
@property
def skills(self):
activities = [a.name for a in memberlist.activity.all()]
return {k: v for k, v in self.member.get_skills().items() if k in activities}
@property
def qualities_tex(self):
qualities = []
for activity, value in self.skills:
qualities.append("\\textit{%s:} %s" % (activity, value))
return ", ".join(qualities)
class Freizeit(models.Model):
"""Lets the user create a 'Freizeit' and generate a members overview in pdf format. """
@ -594,6 +618,48 @@ class Freizeit(models.Model):
jls = set(self.jugendleiter.distinct())
return len(ps - jls)
@property
def time_period_str(self):
time_period = self.date.strftime('%d.%m.%Y')
if self.end != self.date:
time_period += " - " + self.end.strftime('%d.%m.%Y')
return time_period
@property
def groups_str(self):
return ', '.join(g.name for g in self.groups.all())
@property
def staff_str(self):
return ', '.join(yl.name for yl in self.jugendleiter.all())
@property
def skill_summary(self):
activities = [a.name for a in self.activity.all()]
skills = {a: [] for a in activities}
people = []
for memberonlist in self.membersonlist.all():
m = memberonlist.member
qualities = []
for activity, value in m.get_skills().items():
if activity not in activities:
continue
skills[activity].append(value)
qualities.append("\\textit{%s:} %s" % (activity, value))
people.append(dict(name=m.name, qualities=", ".join(qualities), comments=memberonlist.comments_tex))
sks = []
for activity in activities:
skill_avg = 0 if len(skills[activity]) == 0 else\
sum(skills[activity]) / len(skills[activity])
skill_min = 0 if len(skills[activity]) == 0 else\
min(skills[activity])
skill_max = 0 if len(skills[activity]) == 0 else\
max(skills[activity])
sks.append(dict(name=activity, skill_avg=skill_avg, skill_min=skill_min, skill_max=skill_max))
return (people, sks)
class MemberNoteList(models.Model):
"""
A member list with a title and a bunch of members to take some notes.
@ -693,7 +759,7 @@ class Intervention(models.Model):
"""An intervention during a seminar as part of a LJP proposal"""
date_start = models.DateTimeField(verbose_name=_('Starting time'))
duration = models.DecimalField(verbose_name=_('Duration in hours'),
max_digits=3,
max_digits=4,
decimal_places=2)
activity = models.TextField(verbose_name=_('Activity and method'))

@ -0,0 +1,56 @@
from datetime import datetime
import unicodedata
import os
import subprocess
import time
import glob
from django.template.loader import get_template
from django.conf import settings
from django.http import HttpResponse, HttpResponseRedirect
from wsgiref.util import FileWrapper
def media_path(fp):
return os.path.join(os.path.join(settings.MEDIA_MEMBERLISTS, "memberlists"), fp)
def media_dir():
return os.path.join(settings.MEDIA_MEMBERLISTS, "memberlists")
def render_tex(name, template_path, context):
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_tex = filename + '.tex'
filename_pdf = filename + '.pdf'
tmpl = get_template(template_path)
res = tmpl.render(dict(context, creation_date=datetime.today().strftime('%d.%m.%Y')))
with open(media_path(filename_tex), 'w', encoding='utf-8') as f:
f.write(res)
# compile using pdflatex
oldwd = os.getcwd()
os.chdir(media_dir())
subprocess.call(['pdflatex', '-halt-on-error',filename_tex])
time.sleep(1)
# do some cleanup
for f in glob.glob('*.log'):
os.remove(f)
for f in glob.glob('*.aux'):
os.remove(f)
#os.remove(filename_tex)
#os.remove(filename_table)
os.chdir(oldwd)
# 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

@ -0,0 +1,99 @@
{% load tex_extras %}
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{booktabs}
\usepackage{amssymb}
\usepackage{cmbright}
\usepackage{graphicx}
\usepackage{textpos}
\usepackage[colorlinks, breaklinks]{hyperref}
\usepackage{float}
\usepackage[margin=1in]{geometry}
\usepackage{array}
\usepackage{tabularx}
\newcommand{\picpos}[4]{
\begin{textblock*}{#1}(#2, #3)
\includegraphics[width=\textwidth]{#4}
\end{textblock*}
}
% custom url command for properly formatting emails
\DeclareUrlCommand\Email{\urlstyle{same}}
% allow linebreak after every character
\expandafter\def\expandafter\UrlBreaks\expandafter{\UrlBreaks
\do\/\do\a\do\b\do\c\do\d\do\e\do\f\do\g\do\h\do\i\do\j\do\k
\do\l\do\m\do\n\do\o\do\p\do\q\do\r\do\s\do\t\do\u\do\v
\do\w\do\x\do\y\do\z
\do\A\do\B\do\C\do\D\do\E\do\F\do\G\do\H\do\I\do\J\do\K
\do\L\do\M\do\N\do\O\do\P\do\Q\do\R\do\S\do\T\do\U\do\V
\do\W\do\X\do\Y\do\Z}
\renewcommand{\arraystretch}{1.5}
\newcolumntype{L}{>{\hspace{0pt}}X}
\newcommand{\tickedbox}{
\makebox[0pt][l]{$\square$}\raisebox{.15ex}{\hspace{0.1em}$\checkmark$}
}
\newcommand{\checkbox}{
\makebox[0pt][l]{$\square$}
}
\begin{document}
% HEADER RIGHT
\picpos{4.5cm}{11.5cm}{0cm}{dav_logo.png}
\begin{textblock*}{5cm}(11.5cm, 2.3cm)
\begin{flushright}
\small
\noindent Deutscher Alpenverein e. V. \\
Sektion Ludwigsburg\\
Fuchshofstr. 66\\
71638 Ludwigsburg\\
Tel.: 07141 927893\\
Fax: 07141 924042\\
info@alpenverein-ludwigsburg.de\\
\end{flushright}
\end{textblock*}
% HEADLINE
{\noindent\LARGE{Teilnehmer:innenliste\\[2mm]Sektionsveranstaltung}}\\[1mm]
\textit{Erstellt: {{ creation_date }} }\\
% DESCRIPTION TABLE
\begin{table}[H]
\begin{tabular}{ll}
\large Aktivität: & {{ memberlist.name|esc_all }} \\
\large Gruppe: & {{ memberlist.groups_str|esc_all }} \\
\large Ziel: & {{ memberlist.place|esc_all }} \\
\large Stützpunkt: & {{ memberlist.destination|esc_all }} \\
\large Zeitraum: & {{ memberlist.time_period_str|esc_all }} \\
\large Betreuer:innen: & {{ memberlist.staff_str|esc_all }} \\
\large Art der Tour: & {% checked_if_true 'Gemeinschaftstour' memberlist.get_tour_type %}
{% checked_if_true 'Führungstour' memberlist.get_tour_type %}
{% checked_if_true 'Ausbildung' memberlist.get_tour_type %} \\
\large Anreise: & {% checked_if_true 'ÖPNV' memberlist.get_tour_approach %}
{% checked_if_true 'Muskelkraft' memberlist.get_tour_approach %}
{% checked_if_true 'Fahrgemeinschaften' memberlist.get_tour_approach %}
\end{tabular}
\end{table}
\begin{table}[H]
\begin{tabularx}{1\linewidth}{@{\extracolsep{\fill}}lLlL}
\toprule
\textbf{Name} & \textbf{Anschrift} & \textbf{Telefon} & \textbf{E-Mail} \\
\midrule
{% for m in memberlist.membersonlist.all %}
{{ m.member.name|esc_all }} & {{ m.member.address|esc_all }} & {{ m.member.contact_phone_number|esc_all }} & \Email{ {{ m.member.contact_email }} } \\
{% endfor %}
\bottomrule
\end{tabularx}
\end{table}
\vspace{1cm}
\noindent Bitte die ausgefüllte Teilnehmerliste vor Antritt der Aktivität per E-Mail an
\href{mailto:info@alpenverein-ludwigsburg.de}{info@alpenverein-ludwigsburg.de} und
\href{mailto:vorstand@alpenverein-ludwigsburg.de}{vorstand@alpenverein-ludwigsburg.de} senden.
\end{document}

@ -0,0 +1,64 @@
{% load tex_extras %}
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{booktabs}
\usepackage{tabularx}
\usepackage{ragged2e}
\usepackage{amssymb}
\usepackage{cmbright}
\usepackage{graphicx}
\usepackage{textpos}
\usepackage[colorlinks]{hyperref}
\usepackage{float}
\usepackage[margin=1cm]{geometry}
\renewcommand{\arraystretch}{1.5}
\newcolumntype{Y}{>{\RaggedRight\arraybackslash}X}
\begin{document}
% HEADLINE
{\noindent\LARGE{Teilnehmer:innenübersicht}}\\[1mm]
\textit{Erstellt: {{ creation_date }} }\\
% DESCRIPTION
\begin{table}[H]
\begin{tabular}{ll}
\large Aktivität: & {{ memberlist.name|esc_all }} \\
\large Gruppe: & {{ memberlist.groups_str|esc_all }} \\
\large Ziel: & {{ memberlist.place|esc_all }} \\
\large Stützpunkt: & {{ memberlist.destination|esc_all }} \\
\large Zeitraum: & {{ memberlist.time_period_str|esc_all }} \\
\end{tabular}
\end{table}
\begin{table}[H]
\begin{tabularx}{\textwidth}{@{} l l Y @{}}
\toprule
\textbf{Name} & \textbf{Fähigkeiten (max. 100)} & \textbf{Kommentare} \\
\midrule
{% for p in people %}
{{ p.name|esc_all }} & {{ p.qualities|esc_all }} & {{ p.comments|esc_all }} \\
{% endfor %}
\bottomrule
\end{tabularx}
\end{table}
\noindent\large Fähigkeiten der Gruppe\\
\begin{table}[H]
\begin{tabular*}{1\linewidth}{@{\extracolsep{\fill}}llll}
\toprule
\textbf{Name} & \textbf{Durchschnitt} & \textbf{Minimum} & \textbf{Maximum} \\
\midrule
{% for skill in skills %}
{{ skill.name|esc_all }} & {{ skill.skill_avg|esc_all }} & {{ skill.skill_min|esc_all }} & {{ skill.skill_max|esc_all }} \\
{% endfor %}
\bottomrule
\end{tabular*}
\end{table}
\vspace{1cm}
\end{document}

@ -0,0 +1,156 @@
{% load tex_extras %}
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{booktabs}
\usepackage{amssymb}
\usepackage{cmbright}
\usepackage{graphicx}
\usepackage{textpos}
\usepackage[colorlinks, breaklinks]{hyperref}
\usepackage{float}
\usepackage[margin=1in]{geometry}
\usepackage{array}
\usepackage{ragged2e}
\usepackage{tabularx}
\usepackage{titlesec}
\titleformat{\section}
{\Large\slshape}{\thesection\;}
{0em}{}
\newcommand{\picpos}[4]{
\begin{textblock*}{#1}(#2, #3)
\includegraphics[width=\textwidth]{#4}
\end{textblock*}
}
% custom url command for properly formatting emails
\DeclareUrlCommand\Email{\urlstyle{same}}
% allow linebreak after every character
\expandafter\def\expandafter\UrlBreaks\expandafter{\UrlBreaks
\do\/\do\a\do\b\do\c\do\d\do\e\do\f\do\g\do\h\do\i\do\j\do\k
\do\l\do\m\do\n\do\o\do\p\do\q\do\r\do\s\do\t\do\u\do\v
\do\w\do\x\do\y\do\z
\do\A\do\B\do\C\do\D\do\E\do\F\do\G\do\H\do\I\do\J\do\K
\do\L\do\M\do\N\do\O\do\P\do\Q\do\R\do\S\do\T\do\U\do\V
\do\W\do\X\do\Y\do\Z}
\renewcommand{\arraystretch}{1.5}
\newcolumntype{L}{>{\hspace{0pt}}X}
\newcolumntype{Y}{>{\RaggedRight\arraybackslash}X}
\newcommand{\tickedbox}{
\makebox[0pt][l]{$\square$}\raisebox{.15ex}{\hspace{0.1em}$\checkmark$}
}
\newcommand{\checkbox}{
\makebox[0pt][l]{$\square$}
}
\begin{document}
% HEADER RIGHT
\picpos{4.5cm}{11.5cm}{0cm}{dav_logo.png}
\begin{textblock*}{5cm}(11.5cm, 2.3cm)
\begin{flushright}
\small
\noindent Deutscher Alpenverein e. V. \\
Sektion Ludwigsburg\\
Fuchshofstr. 66\\
71638 Ludwigsburg\\
Tel.: 07141 927893\\
Fax: 07141 924042\\
info@alpenverein-ludwigsburg.de\\
\end{flushright}
\end{textblock*}
% HEADLINE
{\noindent\LARGE{Seminarbericht}}\\[1mm]
\textit{Erstellt: {{ creation_date }} }\\
% DESCRIPTION TABLE
\begin{table}[H]
\begin{tabular}{ll}
\large Kursname: & {% if not memberlist.ljpproposal %}{{ memberlist.name|esc_all }}{% else %}{{ memberlist.ljpproposal.title }} {% endif %} \\
\large Gruppe: & {{ memberlist.groups_str|esc_all }} \\
\large Ziel: & {{ memberlist.place|esc_all }} \\
\large Stützpunkt: & {{ memberlist.destination|esc_all }} \\
\large Zeitraum: & {{ memberlist.time_period_str|esc_all }} \\
\large Betreuer:innen: & {{ memberlist.staff_str|esc_all }} \\
\end{tabular}
\end{table}
{% if memberlist.ljpproposal %}
\section{Alpine Ziele}
{{ memberlist.ljpproposal.goals_alpinistic|esc_all }}
\section{Pädagogische Ziele}
{{ memberlist.ljpproposal.goals_pedagogic|esc_all }}
\section{Inhalt und Methoden}
{{ memberlist.ljpproposal.methods|esc_all }}
\section{Evaluation}
{{ memberlist.ljpproposal.evaluation|esc_all }}
\section{Erfahrungen und mögliche Verbesserungsvorschläge}
{{ memberlist.ljpproposal.experiences|esc_all }}
\section{Methodischer Ablauf}
\begin{table}[H]
\begin{tabularx}{1\linewidth}{@{}l l Y @{}}
\toprule
\textbf{Zeitpunkt} & \textbf{Dauer} & \textbf{Aktivität und Methode} \\
\midrule
{% for intervention in memberlist.ljpproposal.intervention_set.all %}
{{ intervention.date_start|datetime_short }} & {{ intervention.duration }} h & {{ intervention.activity|esc_all }} \\
{% endfor %}
\bottomrule
\end{tabularx}
\end{table}
{% endif %}
\section{Teilnehmer:innenliste}
\begin{table}[H]
\begin{tabularx}{1\linewidth}{@{\extracolsep{\fill}}lLl|l|l|l}
\hline
\textbf{Name} & \textbf{Anschrift} & \textbf{Geburtsdatum} & \textbf{m} & \textbf{w} & \textbf{d} \\ \hline
%\midrule
{% for m in memberlist.membersonlist.all %}
{{ m.member.name|esc_all }} & {{ m.member.address|esc_all }} & {{ m.member.birth_date_str|esc_all }} & & & \\
{% endfor %}
%\bottomrule
\end{tabularx}
\end{table}
{% if memberlist.statement %}
\section{Kosten}
\begin{table}[H]
\begin{tabularx}{1\linewidth}{@{}L r @{}}
\toprule
\textbf{Beschreibung} & \textbf{Betrag} \\
\midrule
Aufwandsentschädigung & {{ memberlist.statement.total_allowance }}\\
Fahrtkosten & {{ memberlist.statement.total_transportation }}\\
Übernachtungskosten & {{ memberlist.statement.total_nights }}\\
{% for bill in memberlist.statement.bill_set.all %}
{{ bill.short_description|esc_all }} & {{ bill.amount }}\\
{% endfor %}
\bottomrule
Gesamt & {{ memberlist.statement.total_theoretic }}\\
\end{tabularx}
\end{table}
{% endif %}
\end{document}

@ -0,0 +1,20 @@
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag
def checked_if_true(name, value):
if name == value:
return '\\tickedbox {} \qquad \qquad'.format(name)
else:
return '\\checkbox \\enspace \\enspace {} \qquad \qquad'.format(name)
@register.filter
def esc_all(val):
return mark_safe(str(val).replace('_', '\\_').replace('&', '\\&'))
@register.filter
def datetime_short(date):
return date.strftime('%d.%m.%Y %H:%M')
Loading…
Cancel
Save