Commit f71ea9c0 authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg

Extended the events registration to support fields

parent 2aec63a9
# -*- coding: utf-8 -*-
from django import forms
from django.urls import reverse
from activemembers.models import Committee
from django.contrib import admin
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils import timezone
from django.utils.http import is_safe_url
from django.utils.html import format_html
from django.utils.http import is_safe_url
from django.utils.translation import ugettext_lazy as _
from utils.translation import TranslatedModelAdmin
from activemembers.models import Committee
from members.models import Member
from utils.translation import TranslatedModelAdmin
from . import forms
from . import models
......@@ -32,17 +32,8 @@ class DoNextModelAdmin(TranslatedModelAdmin):
return _do_next(request, res)
class RegistrationInformationFieldForm(forms.ModelForm):
order = forms.IntegerField(label=_('order'), initial=0)
class Meta:
fields = '__all__'
model = models.RegistrationInformationField
class RegistrationInformationFieldInline(admin.StackedInline):
form = RegistrationInformationFieldForm
form = forms.RegistrationInformationFieldForm
extra = 0
model = models.RegistrationInformationField
ordering = ('_order',)
......
from django import forms
from django.utils.translation import ugettext_lazy as _
from .models import RegistrationInformationField
class RegistrationInformationFieldForm(forms.ModelForm):
order = forms.IntegerField(label=_('order'), initial=0)
class Meta:
fields = '__all__'
model = RegistrationInformationField
widgets = {
'type': forms.Select,
}
class FieldsForm(forms.Form):
def __init__(self, *args, **kwargs):
registration = kwargs.pop('registration')
super(FieldsForm, self).__init__(*args, **kwargs)
self.information_fields = registration.registration_information()
for information_field in self.information_fields:
field = information_field['field']
key = "info_field_{}".format(field.id)
if field.type == RegistrationInformationField.BOOLEAN_FIELD:
self.fields[key] = forms.BooleanField(
required=False
)
elif field.type == RegistrationInformationField.INTEGER_FIELD:
self.fields[key] = forms.IntegerField()
elif field.type == RegistrationInformationField.TEXT_FIELD:
self.fields[key] = forms.CharField()
self.fields[key].label = field.name
self.fields[key].help_text = field.description
self.fields[key].initial = information_field['value']
if not field.type == RegistrationInformationField.BOOLEAN_FIELD:
self.fields[key].required = field.required
def field_values(self):
for information_field in self.information_fields:
key = "info_field_{}".format(information_field['field'].id)
field = {
"field": information_field["field"],
"value": self.cleaned_data[key]
}
yield field
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-10-09 21:52
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0006_auto_20160921_2201'),
]
operations = [
migrations.AddField(
model_name='registrationinformationfield',
name='required',
field=models.BooleanField(default=True, verbose_name='required'),
preserve_default=False,
),
migrations.AlterField(
model_name='registrationinformationfield',
name='type',
field=models.CharField(choices=[('boolean', 'Checkbox'), ('text', 'Text'), ('integer', 'Integer')], max_length=10, verbose_name='field type'),
),
]
......@@ -2,9 +2,9 @@ from django.core import validators
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Q
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _, string_concat
from django.urls import reverse
from utils.translation import MultilingualField, ModelTranslateMeta
......@@ -120,6 +120,7 @@ class Event(models.Model, metaclass=ModelTranslateMeta):
def reached_participants_limit(self):
return self.max_participants <= self.registration_set.count()
@property
def status(self):
now = timezone.now()
if bool(self.registration_start) or bool(self.registration_end):
......@@ -188,9 +189,13 @@ class Event(models.Model, metaclass=ModelTranslateMeta):
class RegistrationInformationField(models.Model):
"""Field description to ask for when registering"""
FIELD_TYPES = (('checkbox', _('checkbox')),
('charfield', _('text field')),
('intfield', _('integer field')))
BOOLEAN_FIELD = 'boolean'
INTEGER_FIELD = 'integer'
TEXT_FIELD = 'text'
FIELD_TYPES = ((BOOLEAN_FIELD, _('Checkbox')),
(TEXT_FIELD, _('Text')),
(INTEGER_FIELD, _('Integer')),)
event = models.ForeignKey(Event, models.CASCADE)
......@@ -211,13 +216,18 @@ class RegistrationInformationField(models.Model):
blank=True,
)
required = models.BooleanField(
_('required'),
)
def get_value_for(self, registration):
if self.type == 'charfield':
if self.type == self.TEXT_FIELD:
value_set = self.textregistrationinformation_set
elif self.type == 'checkbox':
elif self.type == self.BOOLEAN_FIELD:
value_set = self.booleanregistrationinformation_set
elif self.type == 'intfield':
elif self.type == self.INTEGER_FIELD:
value_set = self.integerregistrationinformation_set
try:
return value_set.get(registration=registration).value
except (TextRegistrationInformation.DoesNotExist,
......@@ -225,6 +235,31 @@ class RegistrationInformationField(models.Model):
IntegerRegistrationInformation.DoesNotExist):
return None
def set_value_for(self, registration, value):
if self.type == self.TEXT_FIELD:
value_set = self.textregistrationinformation_set
elif self.type == self.BOOLEAN_FIELD:
value_set = self.booleanregistrationinformation_set
elif self.type == self.INTEGER_FIELD:
value_set = self.integerregistrationinformation_set
try:
field_value = value_set.get(registration=registration)
except BooleanRegistrationInformation.DoesNotExist:
field_value = BooleanRegistrationInformation()
except TextRegistrationInformation.DoesNotExist:
field_value = TextRegistrationInformation()
except IntegerRegistrationInformation.DoesNotExist:
field_value = IntegerRegistrationInformation()
field_value.registration = registration
field_value.field = self
field_value.value = value
field_value.save()
def __str__(self):
return "{} ({})".format(self.name, dict(self.FIELD_TYPES)[self.type])
class Meta:
order_with_respect_to = 'event'
......@@ -278,8 +313,8 @@ class Registration(models.Model):
self.event.registration_set.filter(
(Q(date_cancelled__gte=self.date_cancelled) |
Q(date_cancelled=None)) &
Q(date_lte=self.date)
) < self.event.max_participants)
Q(date__lte=self.date)
).count() < self.event.max_participants)
def is_registered(self):
return self.date_cancelled is None
......
......@@ -10,10 +10,14 @@
{% endfor %}
<th>{% trans "present"|capfirst %}</th>
<th>{% trans "paid"|capfirst %}</th>
{% if registrations|length > 0 and registrations.0.date_cancelled is not None %}
<th>{% trans "late"|capfirst %}</th>
{% endif %}
<th>
{% if addlink != 0 %}
<a class="addlink" href="{% url 'admin:events_registration_add' %}?event_pk={{ event.pk }}&next={{ request.get_full_path | urlencode }}">{% trans "add"|capfirst %}</a></th>
<a class="addlink" href="{% url 'admin:events_registration_add' %}?event_pk={{ event.pk }}&next={{ request.get_full_path | urlencode }}">{% trans "add"|capfirst %}</a>
{% endif %}
</th>
</tr>
</thead>
<tbody>
......@@ -36,6 +40,9 @@
{% endfor %}
<td>{{ registration.present|yesno }}</td>
<td>{{ registration.paid|yesno }}</td>
{% if registration.date_cancelled is not None %}
<td>{{ registration.is_late_cancellation|yesno }}</td>
{% endif %}
<td><a class="changelink" href="{% url 'admin:events_registration_change' registration.pk %}?next={{ request.get_full_path|urlencode }}">{% trans "change" %}</a></td>
</tr>
{% empty %}
......
......@@ -128,9 +128,15 @@
<td></td>
<td>
{% if request.user.is_authenticated %}
{% if event.status == event.REGISTRATION_OPEN or event.status == event.REGISTRATION_CLOSED_CANCEL_ONLY %}
{% if event.status == event.REGISTRATION_OPEN or event.status == event.REGISTRATION_OPEN_NO_CANCEL %}
{% if registration is not None and registration.date_cancelled is None and event.has_fields %}
<a class="btn btn-style1" href="{% url 'events:registration' event.id 'update' %}">{% trans "Update registration" %}</a>
<br /><br />
{% endif %}
{% endif %}
{% if event.status == event.REGISTRATION_OPEN or event.status == event.REGISTRATION_CLOSED_CANCEL_ONLY or event.status == event.REGISTRATION_OPEN_NO_CANCEL %}
{% if registration is None or registration.date_cancelled is not None %}
{% if event.status == event.REGISTRATION_OPEN %}
{% if event.status == event.REGISTRATION_OPEN or event.status == event.REGISTRATION_OPEN_NO_CANCEL %}
<a class="btn btn-style1" href="{% url 'events:registration' event.id 'register' %}">
{% if event.reached_participants_limit %}
{% trans "Put me on the waiting list" %}
......@@ -144,11 +150,6 @@
<a class="btn btn-style1" href="{% url 'events:registration' event.id 'cancel' %}" onclick="return confirm('{% trans "Are you sure you want to cancel your registration?" %}');">{% trans "Cancel registration" %}</a>
{% endif %}
{% endif %}
{% if event.status == event.REGISTRATION_OPEN or event.status == event.REGISTRATION_OPEN_NO_CANCEL %}
{% if registration is not None and registration.date_cancelled is None and event.has_fields %}
<a class="btn btn-style1" href="{% url 'events:registration' event.id 'update' %}">{% trans "Update information" %}</a>
{% endif %}
{% endif %}
{% else %}
<a class="btn btn-style1" href="{% url 'login' %}?next={{ request.path }}">{% trans "Login" %}</a>
{% endif %}
......@@ -163,8 +164,10 @@
{% trans "You have to log in before you can register for this event." %}
{% elif event.status == event.REGISTRATION_NOT_YET_OPEN %}
{% blocktrans with datetime=event.registration_start %}Registration will open {{ datetime }}{% endblocktrans %}
{% elif event.status == event.REGISTRATION_OPEN_NO_CANCEL %}
{% blocktrans with costs=event.costs %}Cancellation isn't possible anymore without having to pay the full costs of {{ costs }}{% endblocktrans %}
{% elif event.status == event.REGISTRATION_OPEN_NO_CANCEL and registration.date_cancelled is None %}
{% if event.costs > 0 %}
{% blocktrans with costs=event.costs %}Cancellation isn't possible anymore without having to pay the full costs of {{ costs }}{% endblocktrans %}
{% endif %}
{% elif event.status == event.REGISTRATION_CLOSED or event.status == event.REGISTRATION_CLOSED_CANCEL_ONLY %}
{% blocktrans %}Registration is not possible anymore.{% endblocktrans %}
{% elif status == event.REGISTRATION_NOT_NEEDED %}
......
{% extends "base.html" %}
{% load i18n static %}
{% load i18n static fieldtype %}
{% block title %}{{ event.title }} — {% trans "Calendar"|capfirst %} — {{ block.super }}{% endblock %}
{% block title %}{% if action == 'update' %}{% trans "Update registration" %}{% else %}{% trans "Complete registration" %}{% endif %} — {{ event.title }} — {% trans "Calendar" %} — {{ block.super }}{% endblock %}
{% block body %}
<h1>{{ event.title }}</h1>
{% if action == 'update' %}
<h1>{% blocktrans with title=event.title %}Update registration for {{ title }}{% endblocktrans %}</h1>
{% else %}
<h1>{% blocktrans with title=event.title %}Completing registration for {{ title }}{% endblocktrans %}</h1>
{% endif %}
<form method="post" class="form-horizontal span8 offset2">
{% csrf_token %}
{% for field in form %}
<div class="control-group row {% if field.errors %}error{% endif %}">
<label class="control-label" for="{{ field.name }}">{{ field.label_tag }}</label>
<div class="controls">
{{ field }}
{% for error in field.errors %}
<span class="help-block">{{ error|escape }}</span>
{% endfor %}
<span class="help-block">{{ field.help_text|safe }}</span>
</div>
</div>
{% endfor %}
{% if action == 'update' %}
<input type="submit" value="{% trans "Update registration" %}" class="btn btn-style1 pull-right login" />
{% else %}
<input type="submit" value="{% trans "Complete registration" %}" class="btn btn-style1 pull-right login" />
{% endif %}
<a href="{% url "events:event" event.id %}" class="forgot btn pull-right btn-style2">{% trans 'Cancel' %}</a>
</form>
{% endblock %}
\ No newline at end of file
......@@ -11,6 +11,7 @@ from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _
from .models import Event, Registration
from .forms import FieldsForm
@staff_member_required
......@@ -106,7 +107,8 @@ def event(request, event_id):
Event.objects.filter(published=True),
pk=event_id
)
registrations = event.registration_set.filter(date_cancelled=None)
registrations = event.registration_set.filter(
date_cancelled=None)[:event.max_participants]
context = {
'event': event,
......@@ -137,73 +139,82 @@ def registration(request, event_id, action=None):
pk=event_id
)
if event.registration_required():
if (event.registration_required() and
event.status != Event.REGISTRATION_NOT_NEEDED):
try:
registration = Registration.objects.get(
obj = Registration.objects.get(
event=event,
member=request.user.member
)
except Registration.DoesNotExist:
registration = None
obj = None
success_message = None
error_message = None
show_fields = False
if action == 'register':
if action == 'register' and (
event.status == Event.REGISTRATION_OPEN or
event.status == Event.REGISTRATION_OPEN_NO_CANCEL
):
if event.has_fields():
show_fields = True
if registration is None:
registration = Registration()
registration.event = event
registration.member = request.user.member
elif registration.date_cancelled is not None:
registration.date = timezone.now()
registration.date_cancelled = None
if obj is None:
obj = Registration()
obj.event = event
obj.member = request.user.member
elif obj.date_cancelled is not None:
if obj.is_late_cancellation():
error_message = _("You cannot re-register anymore since "
"you've cancelled after the deadline.")
else:
obj.date = timezone.now()
obj.date_cancelled = None
else:
error_message = _("You were already registered.")
if error_message is None:
success_message = _("Registration successful")
elif (action == 'update' and event.has_fields() and
registration is not None):
elif (action == 'update'
and event.has_fields()
and obj is not None and
event.status == Event.REGISTRATION_OPEN or
event.status == Event.REGISTRATION_OPEN_NO_CANCEL):
show_fields = True
elif action == 'cancel':
if (registration is not None and
registration.date_cancelled is None):
registration.date_cancelled = timezone.now()
success_message = _("Registration successfully updated")
elif action == 'cancel' and (
event.status == Event.REGISTRATION_OPEN or
event.status == Event.REGISTRATION_CLOSED_CANCEL_ONLY
):
if (obj is not None and
obj.date_cancelled is None):
# Note that this doesn't remove the values for the
# information fields that the user entered upon registering.
# But this is regarded as a feature, not a bug. Especially
# since the values will still appear in the backend.
obj.date_cancelled = timezone.now()
success_message = _("Registration successfully cancelled")
else:
error_message = _("You were not registered for this event.")
if show_fields:
# saved = False
#
# if request.POST:
# form = AddExamForm(request.POST, request.FILES)
# if form.is_valid():
# saved = True
# obj = form.save(commit=False)
# obj.uploader = request.user
# obj.uploader_date = datetime.now()
# obj.save()
#
# form = AddExamForm()
# form.exam_date = datetime.now()
# else:
# obj = Exam()
# if id is not None:
# obj.course = Course.objects.get(id=id)
# form = AddExamForm(instance=obj)
# form.exam_date = datetime.now()
context = {'event': event}
return render(request, 'events/event_fields.html', context)
else:
if success_message is not None:
messages.success(request, success_message)
elif error_message is not None:
messages.error(request, error_message)
registration.save()
if request.POST:
form = FieldsForm(request.POST, registration=obj)
if form.is_valid():
form_field_values = form.field_values()
for field in form_field_values:
field['field'].set_value_for(obj,
field['value'])
obj.save()
else:
form = FieldsForm(registration=obj)
context = {'event': event, 'form': form, 'action': action}
return render(request, 'events/event_fields.html', context)
if success_message is not None:
messages.success(request, success_message)
elif error_message is not None:
messages.error(request, error_message)
obj.save()
return redirect(event)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment