Merge pull request #63 from Schlabonski/activity-categories

activity categories and members' skills
docker
Christian Merten 9 years ago committed by GitHub
commit 4f09175863

@ -1,3 +1,4 @@
from django.core import mail
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
@ -13,18 +14,20 @@ def send(subject, content, sender, recipients, reply_to=None,
kwargs = {"reply_to": [reply_to]} kwargs = {"reply_to": [reply_to]}
else: else:
kwargs = {} kwargs = {}
for recipient in recipients: with mail.get_connection() as connection:
email = EmailMessage(subject, content, sender, [recipient], **kwargs) for recipient in recipients:
if attachments is not None: email = EmailMessage(subject, content, sender, [recipient],
for attach in attachments: connection=connection, **kwargs)
email.attach_file(attach) if attachments is not None:
try: for attach in attachments:
email.send() email.attach_file(attach)
except Exception as e: try:
print("Error when sending mail:", e) email.send()
failed = True except Exception as e:
else: print("Error when sending mail:", e)
succeeded = True failed = True
else:
succeeded = True
return NOT_SENT if failed and not succeeded else SENT if not failed\ return NOT_SENT if failed and not succeeded else SENT if not failed\
and succeeded else PARTLY_SENT and succeeded else PARTLY_SENT

@ -0,0 +1,58 @@
\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\textsc{Teilnehmerliste \\Sektionsveranstaltung}}\\
\textit{Erstellt: MEMBERLIST-DATE}\\
% DESCRIPTION TABLE
\begin{table}[H]
\begin{tabular}{ll}
\large Aktivität: & ACTIVITY \\
\large Gruppe: & GROUP \\
\large Ziel: & DESTINATION \\
\large Stützpunkt: & PLACE \\
\large Zeitraum: & TIME-PERIOD \\
\end{tabular}
\end{table}
\begin{table}[H]
\begin{tabularx}{\textwidth}{@{} l l Y @{}}
\toprule
\textbf{Name} & \textbf{Fähigkeiten (max. 100)} & \textbf{Kommentare} \\
\midrule
TABLE
\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
TABLE-QUALITIES
\bottomrule
\end{tabular*}
\end{table}
\vspace{1cm}
\end{document}

@ -12,11 +12,11 @@ from django.contrib import admin
from django.contrib.admin import DateFieldListFilter from django.contrib.admin import DateFieldListFilter
from django.utils.translation import ugettext_lazy as translate from django.utils.translation import ugettext_lazy as translate
from django.db.models import TextField, ManyToManyField from django.db.models import TextField, ManyToManyField
from django.forms import Textarea from django.forms import Textarea, RadioSelect, TypedChoiceField
from django.shortcuts import render from django.shortcuts import render
from .models import (Member, Group, MemberList, MemberOnList, Klettertreff, from .models import (Member, Group, MemberList, MemberOnList, Klettertreff,
KlettertreffAttendee) KlettertreffAttendee, ActivityCategory)
# Register your models here. # Register your models here.
@ -28,23 +28,40 @@ class MemberAdmin(admin.ModelAdmin):
formfield_overrides = { formfield_overrides = {
ManyToManyField: {'widget': forms.CheckboxSelectMultiple} ManyToManyField: {'widget': forms.CheckboxSelectMultiple}
} }
change_form_template = "members/change_member.html"
def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['qualities'] =\
Member.objects.get(pk=object_id).get_skills()
return super(MemberAdmin, self).change_view(request, object_id,
form_url=form_url,
extra_context=extra_context)
class GroupAdmin(admin.ModelAdmin): class GroupAdmin(admin.ModelAdmin):
fields = ['name', 'min_age'] fields = ['name', 'min_age']
list_display = ('name', 'min_age') list_display = ('name', 'min_age')
class ActivityCategoryAdmin(admin.ModelAdmin):
fields = ['name', 'description']
class MemberListAdminForm(forms.ModelForm): class MemberListAdminForm(forms.ModelForm):
difficulty = TypedChoiceField(MemberList.difficulty_choices,
widget=RadioSelect,
coerce=int)
class Meta: class Meta:
model = MemberList model = MemberList
exclude = ['add_member'] exclude = ['add_member']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MemberListAdminForm, self).__init__(*args, **kwargs) super(MemberListAdminForm, self).__init__(*args, **kwargs)
self.fields['jugendleiter'].queryset = Member.objects.filter(group__name='Jugendleiter') self.fields['jugendleiter'].queryset = Member.objects.filter(group__name='Jugendleiter')
#self.fields['add_member'].queryset = Member.objects.filter(prename__startswith='F') #self.fields['add_member'].queryset = Member.objects.filter(prename__startswith='F')
class MemberOnListInline(admin.StackedInline): class MemberOnListInline(admin.StackedInline):
model = MemberOnList model = MemberOnList
@ -55,11 +72,12 @@ class MemberOnListInline(admin.StackedInline):
'cols': 40})}, 'cols': 40})},
} }
class MemberListAdmin(admin.ModelAdmin): class MemberListAdmin(admin.ModelAdmin):
inlines = [MemberOnListInline] inlines = [MemberOnListInline]
form = MemberListAdminForm form = MemberListAdminForm
list_display = ['__str__', 'date'] list_display = ['__str__', 'date']
actions = ['convert_to_pdf'] actions = ['convert_to_pdf', 'generate_notes']
formfield_overrides = { formfield_overrides = {
ManyToManyField: {'widget': forms.CheckboxSelectMultiple} ManyToManyField: {'widget': forms.CheckboxSelectMultiple}
} }
@ -69,7 +87,6 @@ class MemberListAdmin(admin.ModelAdmin):
def convert_to_pdf(self, request, queryset): def convert_to_pdf(self, request, queryset):
"""Converts a member list to pdf. """Converts a member list to pdf.
""" """
for memberlist in queryset: for memberlist in queryset:
# create a unique filename # create a unique filename
@ -86,8 +103,8 @@ class MemberListAdmin(admin.ModelAdmin):
line = '{0} {1} & {2}, {3} & {4} & {5} \\\\ \n'.format(memberonlist.member.prename, line = '{0} {1} & {2}, {3} & {4} & {5} \\\\ \n'.format(memberonlist.member.prename,
memberonlist.member.lastname, memberonlist.member.street, memberonlist.member.lastname, memberonlist.member.street,
memberonlist.member.town, memberonlist.member.phone_number, memberonlist.member.email) memberonlist.member.town, memberonlist.member.phone_number, memberonlist.member.email)
f.write(line) f.write(line)
# copy and adapt latex memberlist template # copy and adapt latex memberlist template
shutil.copy('media/memberlists/memberlist_template.tex', shutil.copy('media/memberlists/memberlist_template.tex',
'media/memberlists/'+filename_tex) 'media/memberlists/'+filename_tex)
@ -95,7 +112,7 @@ class MemberListAdmin(admin.ModelAdmin):
# read in template # read in template
with open('media/memberlists/'+filename_tex, 'r') as f: with open('media/memberlists/'+filename_tex, 'r') as f:
template_content = f.read() template_content = f.read()
# adapt template # adapt template
template_content = template_content.replace('ACTIVITY', memberlist.name) template_content = template_content.replace('ACTIVITY', memberlist.name)
groups = ', '.join(g.name for g in memberlist.groups.all()) groups = ', '.join(g.name for g in memberlist.groups.all())
@ -155,6 +172,102 @@ class MemberListAdmin(admin.ModelAdmin):
return response return response
def generate_notes(self, request, queryset):
"""Generates a short note for the jugendleiter"""
for memberlist in queryset:
# unique filename
filename = memberlist.name + "_note_" +\
datetime.today().strftime("%d_%m_%Y")
filename = filename.replace(' ', '_')
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.memberonlist_set.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(
m.prename, m.lastname,
", ".join(qualities), comment or "---",
)
table += line
table_qualities = ""
for activity in activities:
line = '{0} & {1} & {2} & {3} \\\\ \n'.format(
activity,
sum(skills[activity]) / len(skills[activity]),
min(skills[activity]),
max(skills[activity])
)
table_qualities += line
# copy template
shutil.copy('media/memberlists/membernote_template.tex',
'media/memberlists/' + filename_tex)
# read in template
with open('media/memberlists/' + filename_tex, 'r') as f:
template_content = f.read()
# adapt template
template_content = template_content.replace('ACTIVITY', memberlist.name)
groups = ', '.join(g.name for g in memberlist.groups.all())
template_content = template_content.replace('GROUP', groups)
template_content = template_content.replace('DESTINATION', memberlist.destination)
template_content = template_content.replace('PLACE', memberlist.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/memberlists/' + filename_tex, 'w') as f:
f.write(template_content)
# compile using pdflatex
oldwd = os.getcwd()
os.chdir('media/memberlists')
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.chdir(oldwd)
# provide the user with the resulting pdf file
with open('media/memberlists/'+filename_pdf, 'rb') as pdf:
response = HttpResponse(FileWrapper(pdf))
response['Content-Type'] = 'application/pdf'
response['Content-Disposition'] = 'attachment; filename=' + filename_pdf
return response
class KlettertreffAdminForm(forms.ModelForm): class KlettertreffAdminForm(forms.ModelForm):
class Meta: class Meta:
@ -217,3 +330,4 @@ admin.site.register(Member, MemberAdmin)
admin.site.register(Group, GroupAdmin) admin.site.register(Group, GroupAdmin)
admin.site.register(MemberList, MemberListAdmin) admin.site.register(MemberList, MemberListAdmin)
admin.site.register(Klettertreff, KlettertreffAdmin) admin.site.register(Klettertreff, KlettertreffAdmin)
admin.site.register(ActivityCategory, ActivityCategoryAdmin)

@ -7,6 +7,22 @@ from django.utils import timezone
from multiselectfield import MultiSelectField from multiselectfield import MultiSelectField
class ActivityCategory(models.Model):
"""
Describes one kind of activity
"""
name = models.CharField(max_length=20, verbose_name=_('Name'))
description = models.TextField(_('Description'))
def __str__(self):
return self.name
class Meta:
verbose_name = _('Activity')
verbose_name_plural = _('Activities')
class Group(models.Model): class Group(models.Model):
""" """
Represents one group of the association Represents one group of the association
@ -85,22 +101,36 @@ class Member(models.Model):
verbose_name = _('member') verbose_name = _('member')
verbose_name_plural = _('members') verbose_name_plural = _('members')
def get_skills(self):
# get skills by summing up all the activities taken part in
skills = {}
for kind in ActivityCategory.objects.all():
lists = MemberList.objects.filter(activity=kind,
memberonlist__member=self)
skills[kind.name] = sum([l.difficulty * 3 for l in lists
if l.date < datetime.now().date()])
return skills
class MemberList(models.Model): class MemberList(models.Model):
"""Lets the user create a list of members in pdf format. """ """Lets the user create a list of members in pdf format. """
name = models.CharField(verbose_name='Activity', default='', name = models.CharField(verbose_name='Activity', default='',
max_length=50) max_length=50)
place = models.CharField(verbose_name=_('Place'), default='', max_length=50) place = models.CharField(verbose_name=_('Place'), default='', max_length=50)
destination = models.CharField(verbose_name=_('Destination (optional)'), default='', max_length=50, blank=True) destination = models.CharField(verbose_name=_('Destination (optional)'), default='', max_length=50, blank=True)
date = models.DateField(default=datetime.today) date = models.DateField(default=datetime.today)
end = models.DateField(verbose_name=_('End (optional)'), blank=True, default=datetime.today) end = models.DateField(verbose_name=_('End (optional)'), blank=True, default=datetime.today)
#comment = models.TextField(_('Comments'), default='', blank=True) #comment = models.TextField(_('Comments'), default='', blank=True)
groups = models.ManyToManyField(Group) groups = models.ManyToManyField(Group)
jugendleiter = models.ManyToManyField(Member) jugendleiter = models.ManyToManyField(Member)
tour_type_choices = (('Gemeinschaftstour','Gemeinschaftstour'), ('Führungstour', 'Führungstour'), tour_type_choices = (('Gemeinschaftstour','Gemeinschaftstour'), ('Führungstour', 'Führungstour'),
('Ausbildung', 'Ausbildung')) ('Ausbildung', 'Ausbildung'))
tour_type = MultiSelectField(choices=tour_type_choices, default='', max_choices=1) tour_type = MultiSelectField(choices=tour_type_choices, default='', max_choices=1)
activity = models.ManyToManyField(ActivityCategory, default=None)
difficulty_choices = [(1, _('easy')), (2, _('medium')), (3, _('hard'))]
difficulty = models.IntegerField(verbose_name=_('Difficulty'),
choices=difficulty_choices)
def __str__(self): def __str__(self):
@ -136,7 +166,7 @@ class Klettertreff(models.Model):
topic = models.CharField(_('Topic'), default='', max_length=60) topic = models.CharField(_('Topic'), default='', max_length=60)
jugendleiter = models.ManyToManyField(Member) jugendleiter = models.ManyToManyField(Member)
group = models.ForeignKey(Group, default='') group = models.ForeignKey(Group, default='')
def __str__(self): def __str__(self):
return self.location + ' ' + self.date.strftime('%d.%m.%Y') return self.location + ' ' + self.date.strftime('%d.%m.%Y')
@ -157,7 +187,6 @@ class Klettertreff(models.Model):
return True return True
return False return False
get_jugendleiter.short_description = _('Jugendleiter') get_jugendleiter.short_description = _('Jugendleiter')
class Meta: class Meta:

@ -0,0 +1,20 @@
{% extends "admin/change_form.html" %}
{% load i18n %}
{% load static %}
{% block after_field_sets %}
<p><b>{% trans "Qualities:" %}</b></p>
<table style="width:100%">
<tr>
<th>{% trans "Activity" %}</th>
<th>{% trans "Skill level" %}</th>
</tr>
{% for key, value in qualities.items %}
<tr>
<td>{{ key }}</td>
<td><progress value="{{ value }}" max="100"></progress></td>
</tr>
{% endfor %}
</table>
{% endblock %}
Loading…
Cancel
Save