MIGRATION! ADD: first things for a event registration mangement page.

This commit is contained in:
2019-05-29 16:50:02 +02:00
parent a189f5dbe6
commit bee1623529
12 changed files with 382 additions and 7 deletions

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-05-29 08:31
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dav_events', '0028_auto_20190401_1310'),
]
operations = [
migrations.AddField(
model_name='event',
name='registration_closed',
field=models.BooleanField(default=False, verbose_name='Anmeldung geschlossen'),
),
]

View File

@@ -228,6 +228,9 @@ class Event(models.Model):
internal_note = models.TextField(blank=True,
verbose_name=_('Bearbeitungshinweis'))
registration_closed = models.BooleanField(default=False,
verbose_name=_('Anmeldung geschlossen'))
@property
def workflow(self):
return DefaultWorkflow(self)

View File

@@ -165,6 +165,10 @@
<a class="btn {% if has_permission_update %}btn-warning{% else %}disabled{% endif %}"
href="{% url 'dav_events:update' event.pk %}">{% trans 'Ändern' %}</a>
</li>
<li class="{% if not event.registration_required %}disabled{% endif %}">
<a class="btn {% if not event.registration_required %}disabled{% elif is_published_any %}btn-warning{% endif %}"
href="{% url 'dav_events:registrations' event.pk %}">{% trans 'Anmeldungen' %}</a>
</li>
</ul>
</div>

View File

@@ -0,0 +1,263 @@
{% extends 'dav_events/base.html' %}
{% load bootstrap3 %}
{% load i18n %}
{% load dav_events %}
{% block head-title %}{% trans 'Anmeldungen' %} - {{ event }} - {{ block.super }}{% endblock head-title %}
{% block modals %}
<div id="modal-close-registration-dialog" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{% trans 'Anmeldung schließen?' %}</h4>
</div>
<div class="modal-body">
<p class="text-center">
<strong>{{ event }}</strong>
</p>
<p>
Die Redaktion wird informiert, dass die Veranstaltung im Internet als ausgebucht gekennzeichnet
bzw. nicht abgedruckt werden soll.<br />
Eine Teilnehmeranmeldung über das Touren- und Kurse-Portal wird nicht mehr möglich sein.
</p>
</div>
<div class="modal-footer">
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="action" value="close-registration">
<button type="submit" class="btn btn-success">
{% bootstrap_icon 'ok' %}&thinsp;
{% trans 'Ja, mach zu!' %}
</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">
{% bootstrap_icon 'remove' %}&thinsp;
{% trans 'Abbrechen' %}
</button>
</form>
</div>
</div>
</div>
</div>
<div id="modal-open-registration-dialog" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{% trans 'Anmeldung wieder öffnen?' %}</h4>
</div>
<div class="modal-body">
<p class="text-center">
<strong>{{ event }}</strong>
</p>
<p>
Die Anmeldung über das Touren- und Kurse-Portal wird wieder möglich
(solange der Anmeldeschluß noch nicht abgelaufen ist).<br />
Die Redaktion wird darüber aber nicht informiert.
</p>
</div>
<div class="modal-footer">
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="action" value="open-registration">
<button type="submit" class="btn btn-success">
{% bootstrap_icon 'ok' %}&thinsp;
{% trans 'Ja, super!' %}
</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">
{% bootstrap_icon 'remove' %}&thinsp;
{% trans 'Abbrechen' %}
</button>
</form>
</div>
</div>
</div>
</div>
<div id="modal-kill-deadline-dialog" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{% trans 'Anmeldeschluss aufheben?' %}</h4>
</div>
<div class="modal-body">
<p class="text-center">
<strong>{{ event }}</strong>
</p>
<p>
Der bisher gesetzte Anmeldeschluss wird gelöscht.<br />
Somit wird die Anmeldung über das Touren- und Kurse-Portal wieder möglich.<br />
<strong>Die Redaktion wird darüber nicht automatisch informiert</strong>.
Wenn deine Tour bereits mit Anmeldeschluss im <i>KA Alpin</i> abgedruckt wurde,
können wir da auch nichts mehr machen.<br />
</p>
</div>
<div class="modal-footer">
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="action" value="kill-deadline">
<button type="submit" class="btn btn-success">
{% bootstrap_icon 'ok' %}&thinsp;
{% trans 'Ja, super!' %}
</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">
{% bootstrap_icon 'remove' %}&thinsp;
{% trans 'Abbrechen' %}
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock modals %}
{% block page-container-fluid %}
<div class="action-tabs top-most">
<ul class="nav nav-tabs" role="tablist">
<li>
<a href="{% url 'dav_events:list' %}">{% trans 'Veranstaltungsliste' %}</a>
</li>
<li>
<a href="{% url 'dav_events:detail' event.pk %}">{% trans 'Details' %}</a>
</li>
<li class="{% if not has_permission_update %}disabled{% endif %}">
<a class="{% if not has_permission_update %}disabled{% endif %}"
href="{% url 'dav_events:update' event.pk %}">{% trans 'Ändern' %}</a>
</li>
<li class="active">
<a href="{% url 'dav_events:registrations' event.pk %}">{% trans 'Anmeldungen' %}</a>
</li>
</ul>
</div>
{% if not event.registration_required %}
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
{% trans 'Für diese Veranstaltung wurde keine Anmeldung gefordert.' %}
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading">
<div class="pull-right">
{% render_event_status event show_void=False %}
</div>
<h3 class="panel-title">{{ event }}</h3>
</div>
<div class="panel-body">
<div class="panel-group" id="form-accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div id="headingForm1" class="panel-heading" role="tab">
<h4 class="panel-title">
<a role="button" href="#collapseForm1" data-toggle="collapse" data-parent="#accordion"
aria-expanded="true" aria-controls="collapseForm1">
{% trans 'Allgemeines' %}
</a>
</h4>
</div>
<div id="collapseForm1" class="panel-collapse collapse in"
role="tabpanel" aria-labelledby="headingForm1">
<div class="panel-body">
<div class="row">
<div class="col-sm-3">
<strong>{% trans 'Teilnehmerzahl' %}:</strong>
{{ event.min_participants }} -
{% if event.max_participants > 0 %}
{{ event.max_participants }}
{% else %}
&infin;
{% endif %}
</div>
<div class="col-sm-4">
<strong>{% trans 'Anmeldeschluss' %}:</strong>
{% if event.deadline %}
{{ event.deadline|date:'l, d. F Y' }}
{% else %}
-
{% endif %}
</div>
<div class="col-sm-5">
<strong>{% trans 'Anmeldestatus' %}:</strong>
{% if not event.registration_required %}
<span class="label label-success">{% trans 'Anmeldung nicht erforderlich' %}</span>
{% elif is_done %}
<span class="label label-default">{% trans 'Veranstaltung beendet' %}</span>
{% elif event.registration_closed %}
<span class="label label-danger">{% trans 'Anmeldung geschlossen' %}</span>
{% elif event.is_deadline_expired %}
<span class="label label-warning">{% trans 'Anmeldeschluss abgelaufen' %}</span>
{% elif is_published_any %}
<span class="label label-success">{% trans 'Anmeldung geöffnet' %}</span>
{% else %}
<span class="label label-info">{% trans 'Veranstaltung noch nicht veröffentlicht' %}</span>
{% endif %}
</div>
</div>
</div>
<div class="panel-footer">
{% if event.is_deadline_expired %}
<a id="btn-kill-deadline" class="btn btn-danger"
data-toggle="modal" data-target="#modal-kill-deadline-dialog">
{% bootstrap_icon 'remove-circle' %}&thinsp;
{% trans 'Anmeldeschluss aufheben' %}
</a>
{% elif event.registration_closed %}
<a id="btn-open-registration" class="btn btn-warning"
data-toggle="modal" data-target="#modal-open-registration-dialog">
{% bootstrap_icon 'ok-circle' %}&thinsp;
{% trans 'Anmeldung wieder öffnen' %}
</a>
{% else %}
<a id="btn-close-registration" class="btn btn-warning"
data-toggle="modal" data-target="#modal-close-registration-dialog">
{% bootstrap_icon 'ban-circle' %}&thinsp;
{% trans 'Anmeldung schließen' %}
</a>
{% endif %}
</div>
</div>
</div>
<hr />
<h4>Teilnehmer (Designstudie - Das funktioniert alles noch nicht!)</h4>
<div class="panel panel-default">
<div id="headingAddParticipant" class="panel-heading" role="tab">
<h5 class="panel-title">
<a role="button" href="#collapseAddParticipant" data-toggle="collapse" data-parent="#accordion"
aria-expanded="true" aria-controls="collapseAddParticipant">
Teilnehmer hinzufügen
</a>
</h5>
</div>
<div id="collapseAddParticipant" class="panel-collapse collapse in"
role="tabpanel" aria-labelledby="headingAddParticipant">
<div class="panel-body">
Formular
</div>
<div class="panel-footer">
<button class="btn btn-success">Speichern</button>
</div>
</div>
</div>
{% for participant in participants %}
<div class="panel panel-default">
<div id="headingParticipant_{{ participant.id }}" class="panel-heading" role="tab">
<h5 class="panel-title">
<a role="button" href="#collapseParticipant_{{ participant.id }}" data-toggle="collapse" data-parent="#accordion"
aria-expanded="true" aria-controls="collapseParticipant_{{ participant.id }}">
{{ participant.position }}. {{ participant.get_full_name }}
</a>
</h5>
</div>
<div id="collapseParticipant_{{ participant.id }}" class="panel-collapse collapse"
role="tabpanel" aria-labelledby="headingParticipant_{{ participant.id }}">
<div class="panel-body">
Hier tauchen dann die Teilnehmerdaten auf
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock page-container-fluid %}

View File

@@ -45,6 +45,10 @@
<li class="active">
<a href="{% url 'dav_events:update' event.pk %}">{% trans 'Ändern' %}</a>
</li>
<li class="{% if not event.registration_required %}disabled{% endif %}">
<a class="{% if not event.registration_required %}disabled{% endif %}"
href="{% url 'dav_events:registrations' event.pk %}">{% trans 'Anmeldungen' %}</a>
</li>
</ul>
</div>
<form action="" method="post">

View File

@@ -7,6 +7,7 @@ urlpatterns = [
url(r'^$', views.events.EventListView.as_view(), name='list'),
url(r'^export$', views.events.EventListExportView.as_view(), name='list_export'),
url(r'^create$', views.events.EventCreateView.as_view(), name='create'),
url(r'^(?P<pk>\d+)/registrations', views.events.EventRegistrationsView.as_view(), name='registrations'),
url(r'^(?P<pk>\d+)/status/(?P<status>[a-z0-9._-][a-z0-9._-]*)',
views.events.EventUpdateStatusView.as_view(), name='updatestatus'),
url(r'^(?P<pk>\d+)/edit', views.events.EventUpdateView.as_view(), name='update'),

View File

@@ -150,6 +150,7 @@ class EventDetailView(EventPermissionMixin, generic.DetailView):
context['is_publishing'] = obj.workflow.has_reached_status('publishing')
context['is_publishing_any'] = obj.workflow.has_reached_status('publishing*')
context['is_published'] = obj.workflow.has_reached_status('published')
context['is_published_any'] = obj.workflow.has_reached_status('published*')
return context
@method_decorator(login_required)
@@ -157,6 +158,63 @@ class EventDetailView(EventPermissionMixin, generic.DetailView):
return super(EventDetailView, self).dispatch(request, *args, **kwargs)
class EventRegistrationsView(EventPermissionMixin, generic.DetailView):
permission = 'update-registration'
model = models.Event
template_name = 'dav_events/event_registrations.html'
def get_object(self, queryset=None):
obj = super(EventRegistrationsView, self).get_object(queryset=queryset)
self.enforce_permission(obj)
return obj
def get_context_data(self, **kwargs):
context = super(EventRegistrationsView, self).get_context_data(**kwargs)
obj = context.get('event')
context['has_permission_update'] = self.has_permission('update', obj)
context['is_published_any'] = obj.workflow.has_reached_status('published*')
context['is_done'] = obj.workflow.has_reached_status('expired')
class MockParticipant(object):
def __init__(self, id, name):
self.id = id
self.name = name
@property
def position(self):
return self.id
def get_full_name(self):
return self.name
participants = [MockParticipant(1, 'Erika Musterfrau'), MockParticipant(2, 'Max Mustermann')]
context['participants'] = participants
return context
def post(self, request, *args, **kwargs):
event = self.get_object()
action = request.POST.get('action')
if action == 'close-registration':
event.registration_closed = True
event.save()
messages.success(request, _(u'Die Anmeldung wurde geschlossen'))
elif action == 'open-registration':
event.registration_closed = False
event.save()
messages.success(request, _(u'Die Anmeldung wurde geöffnet'))
elif action == 'kill-deadline':
event.deadline = None
event.registration_closed = False
event.save()
messages.success(request, _(u'Der Anmeldeschluss wurde gelöscht'))
return HttpResponseRedirect(reverse('dav_events:registrations', kwargs={'pk': event.pk}))
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(EventRegistrationsView, self).dispatch(request, *args, **kwargs)
class EventUpdateStatusView(EventPermissionMixin, generic.DetailView):
model = models.Event

View File

@@ -436,6 +436,15 @@ class BasicWorkflow(object):
return True
elif has_role(user, 'publisher'):
return True
elif permission == 'update-registration':
if user == event.owner:
return True
if has_role(user, 'manager_super'):
return True
if has_role(user, 'manager_{}'.format(event.sport.lower())):
return True
if has_role(user, 'publisher'):
return True
return False
# TODO: is a class method a good idea?

View File

@@ -5,7 +5,14 @@
{% block head-title %}{{ event.number }} - {{ block.super }}{% endblock head-title %}
{% block messages %}
{% if event.is_deadline_expired %}
{% if event.registration_closed %}
<div class="container-fluid">
<div class="alert alert-danger">
Die Teilnehmerliste ist bereits voll!<br />
Eine Anmeldung ist nicht mehr möglich.
</div>
</div>
{% elif event.is_deadline_expired %}
<div class="container-fluid">
<div class="alert alert-danger">
Der Anmeldeschluss ist bereits abgelaufen!<br />
@@ -45,7 +52,7 @@
{% endblocktrans %}
</div>
{% endif %}
{% if not event.is_deadline_expired %}
{% if not event.registration_closed and not event.is_deadline_expired %}
<a class="btn btn-primary" href="{% url 'dav_registration:register' event.pk %}">{% trans 'Anmeldung' %}</a>
{% endif %}
<a class="btn btn-danger" href="{% url 'dav_registration:events' %}">{% trans 'Zurück' %}</a>

View File

@@ -24,9 +24,12 @@
</a>
</div>
<div class="pull-right" style="margin-left: 1em;">
{% if event.is_deadline_expired %}
<span class="text-danger"><span class="glyphicon glyphicon-exclamation-sign"></span></span>
<span class="text-danger">{% trans 'Anmeldeschluss abgelaufen' %}</span>
{% if event.registration_closed %}
<span class="text-primary"><span class="glyphicon glyphicon-tent"></span></span>
<span class="text-primary"><strong>{% trans 'Teilnehmerliste voll' %}</strong></span>
{% elif event.is_deadline_expired %}
<span class="text-danger"><span class="glyphicon glyphicon-time"></span></span>
<span class="text-danger"><strong>{% trans 'Anmeldeschluss abgelaufen' %}</strong></span>
{% else %}
<a class="btn btn-primary" href="{% url 'dav_registration:event' event.pk %}">{% trans 'Details & Anmeldung' %}</a>
{% endif %}
@@ -58,7 +61,7 @@
<li class="list-group-item">
{% render_event_facts event %}
</li>
{% if not event.is_deadline_expired %}
{% if not event.registration_closed and not event.is_deadline_expired %}
<li class="list-group-item">
<a class="btn btn-primary" href="{% url 'dav_registration:event' event.pk %}">{% trans 'zur Anmeldung' %}</a>
</li>

View File

@@ -110,7 +110,7 @@
<div class="well well-sm">
<p><small>
<strong>
Die erfolgreiche Anmeldung bedeutet nicht, dass du auf jeden Fall
Die erfolgreiche Anmeldung hier im Portal bedeutet nicht, dass du auf jeden Fall
an der Tour oder dem Kurs teilnehmen kannst.
</strong>
<br />

View File

@@ -37,6 +37,7 @@ class EventListView(generic.ListView):
'published', 'published_web', 'published_facebook'))
filter &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
filter &= Q(first_day__gte=today)
# filter &= Q(registration_closed=False)
# filter &= Q(deadline__isnull=True) | Q(deadline__gte=today)
qs = self.model.objects.filter(filter).order_by('first_day', 'number').distinct()
@@ -55,6 +56,7 @@ class EventDetailView(generic.DetailView):
'published', 'published_web', 'published_facebook'))
filter &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
filter &= Q(first_day__gte=today)
# filter &= Q(registration_closed=False)
# filter &= Q(deadline__isnull=True) | Q(deadline__gte=today)
qs = self.model.objects.filter(filter).distinct()
@@ -76,6 +78,7 @@ class RegistrationView(generic.CreateView):
'published', 'published_web', 'published_facebook'))
filter &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
filter &= Q(first_day__gte=today)
filter &= Q(registration_closed=False)
filter &= Q(deadline__isnull=True) | Q(deadline__gte=today)
qs = Event.objects.filter(filter).distinct()