2019-12-22 23:37:20 +01:00
|
|
|
"""
|
|
|
|
Event framework for GestioCOF and GestioBDS.
|
|
|
|
|
2020-05-08 16:34:19 +02:00
|
|
|
The events implemented in this module provide two types of customisations to event
|
|
|
|
creators (the COF and BDS staff): options and extra (text) fields.
|
2019-12-22 23:37:20 +01:00
|
|
|
|
|
|
|
Options
|
|
|
|
-------
|
|
|
|
|
|
|
|
An option is an extra field in the registration form with a predefined list of available
|
|
|
|
choices. Any number of options can be added to an event.
|
|
|
|
|
2020-05-08 16:34:19 +02:00
|
|
|
For instance, a typical use-case is events where meals are served to participants
|
2019-12-22 23:37:20 +01:00
|
|
|
with different possible menus, say: vegeterian / vegan / without pork / etc. This
|
|
|
|
example can be implemented with an `Option(name="menu")` and an `OptionChoice` for each
|
|
|
|
available menu.
|
|
|
|
|
|
|
|
In this example, the choice was exclusive: participants can only chose one menu. For
|
|
|
|
situations, where multiple choices can be made at the same time, use the `multi_choices`
|
|
|
|
flag.
|
|
|
|
|
|
|
|
Extra fields
|
|
|
|
------------
|
|
|
|
|
|
|
|
Extra fields can also be added to the registration form that can receive arbitrary text.
|
|
|
|
Typically, this can be a "remark" field (prefer the LONGTEXT option in this case) or
|
|
|
|
small form entries such as "phone number" or "emergency contact" (prefer the SHORTTEXT
|
|
|
|
option in this case).
|
|
|
|
"""
|
|
|
|
|
2019-10-05 15:04:35 +02:00
|
|
|
from django.contrib.auth import get_user_model
|
2020-05-08 16:34:35 +02:00
|
|
|
from django.core.exceptions import ValidationError
|
2019-10-05 14:34:30 +02:00
|
|
|
from django.db import models
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
|
2019-10-05 15:04:35 +02:00
|
|
|
User = get_user_model()
|
|
|
|
|
2019-10-05 14:34:30 +02:00
|
|
|
|
|
|
|
class Event(models.Model):
|
|
|
|
title = models.CharField(_("titre"), max_length=200)
|
|
|
|
location = models.CharField(_("lieu"), max_length=200)
|
|
|
|
start_date = models.DateTimeField(_("date de début"), blank=True, null=True)
|
|
|
|
end_date = models.DateTimeField(_("date de fin"), blank=True, null=True)
|
|
|
|
description = models.TextField(_("description"), blank=True)
|
|
|
|
image = models.ImageField(
|
|
|
|
_("image"), blank=True, null=True, upload_to="imgs/events/"
|
|
|
|
)
|
|
|
|
registration_open = models.BooleanField(_("inscriptions ouvertes"), default=True)
|
|
|
|
old = models.BooleanField(_("archiver (événement fini)"), default=False)
|
2019-12-22 21:27:28 +01:00
|
|
|
subscribers = models.ManyToManyField(
|
|
|
|
User, through="Registration", verbose_name=_("inscrit⋅e⋅s")
|
|
|
|
)
|
2019-10-05 14:34:30 +02:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _("événement")
|
|
|
|
verbose_name_plural = _("événements")
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.title
|
|
|
|
|
|
|
|
|
2019-12-22 21:27:28 +01:00
|
|
|
class Option(models.Model):
|
2019-12-22 23:37:20 +01:00
|
|
|
"""Extra form fields with a limited set of available choices.
|
2019-12-22 21:27:28 +01:00
|
|
|
|
2019-12-22 23:37:20 +01:00
|
|
|
The available choices are given by `OptionChoice`s (see below). A typical use-case
|
2020-05-08 16:34:19 +02:00
|
|
|
is events where the participants have the choice between different menus (e.g.
|
|
|
|
vegan / vegetarian / without-pork / etc).
|
2019-12-22 21:27:28 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="options")
|
|
|
|
name = models.CharField(_("option d'événement"), max_length=200)
|
|
|
|
multi_choices = models.BooleanField(_("choix multiples"), default=False)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _("option d'événement")
|
|
|
|
verbose_name_plural = _("options d'événement")
|
2020-05-08 16:34:35 +02:00
|
|
|
unique_together = [["event", "name"]]
|
2019-12-22 21:27:28 +01:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
|
|
|
class OptionChoice(models.Model):
|
2019-12-22 23:37:20 +01:00
|
|
|
"""A possible choice for an event option."""
|
2019-12-22 21:27:28 +01:00
|
|
|
|
|
|
|
option = models.ForeignKey(Option, on_delete=models.CASCADE, related_name="choices")
|
|
|
|
choice = models.CharField(_("choix"), max_length=200)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _("choix d'option d'événement")
|
|
|
|
verbose_name_plural = _("choix d'option d'événement")
|
2020-05-08 16:34:35 +02:00
|
|
|
unique_together = [["option", "choice"]]
|
2019-12-22 21:27:28 +01:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.choice
|
|
|
|
|
|
|
|
|
|
|
|
class ExtraField(models.Model):
|
2019-12-22 23:37:20 +01:00
|
|
|
"""Extra event field receiving arbitrary text.
|
2019-12-22 21:27:28 +01:00
|
|
|
|
|
|
|
Extra text field that can be added by event creators to the event registration form.
|
2019-12-22 23:37:20 +01:00
|
|
|
Typical examples are "remarks" fields (of type LONGTEXT) or more specific fields
|
|
|
|
such as "emergency contact" (of type SHORTTEXT probably?).
|
2019-12-22 21:27:28 +01:00
|
|
|
"""
|
2019-10-05 14:34:30 +02:00
|
|
|
|
2019-12-22 21:27:28 +01:00
|
|
|
LONGTEXT = "longtext"
|
|
|
|
SHORTTEXT = "shorttext"
|
|
|
|
|
|
|
|
FIELD_TYPE = [
|
|
|
|
(SHORTTEXT, _("texte court (une ligne)")),
|
|
|
|
(LONGTEXT, _("texte long (plusieurs lignes)")),
|
|
|
|
]
|
|
|
|
|
|
|
|
event = models.ForeignKey(
|
|
|
|
Event, on_delete=models.CASCADE, related_name="extra_fields"
|
|
|
|
)
|
|
|
|
name = models.CharField(_("champ d'événement supplémentaire"), max_length=200)
|
|
|
|
field_type = models.CharField(_("type de champ"), max_length=9, choices=FIELD_TYPE)
|
|
|
|
|
2020-05-08 16:34:35 +02:00
|
|
|
class Meta:
|
|
|
|
unique_together = [["event", "name"]]
|
|
|
|
|
2019-12-22 21:27:28 +01:00
|
|
|
|
|
|
|
class ExtraFieldContent(models.Model):
|
2019-12-22 23:37:20 +01:00
|
|
|
"""Value entered in an extra field."""
|
|
|
|
|
2019-12-22 21:27:28 +01:00
|
|
|
field = models.ForeignKey(ExtraField, on_delete=models.CASCADE)
|
|
|
|
registration = models.ForeignKey(
|
|
|
|
"Registration", on_delete=models.CASCADE, related_name="extra_info"
|
|
|
|
)
|
|
|
|
content = models.TextField(_("contenu du champ"))
|
|
|
|
|
2020-05-08 16:34:35 +02:00
|
|
|
def clean(self):
|
|
|
|
if self.registration.event != self.field.event:
|
|
|
|
raise ValidationError(
|
|
|
|
_("Inscription et champ texte incohérents pour ce commentaire")
|
|
|
|
)
|
|
|
|
|
2019-12-22 21:27:28 +01:00
|
|
|
class Meta:
|
|
|
|
verbose_name = _("contenu d'un champ événement supplémentaire")
|
|
|
|
verbose_name_plural = _("contenus d'un champ événement supplémentaire")
|
2020-05-08 16:34:35 +02:00
|
|
|
unique_together = [["field", "registration"]]
|
2019-12-22 21:27:28 +01:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
max_length = 50
|
|
|
|
if len(self.content) > max_length:
|
|
|
|
return self.content[: max_length - 1] + "…"
|
|
|
|
else:
|
|
|
|
return self.content
|
|
|
|
|
|
|
|
|
|
|
|
class Registration(models.Model):
|
2019-12-22 23:37:20 +01:00
|
|
|
"""A user registration to an event."""
|
|
|
|
|
2019-12-22 21:27:28 +01:00
|
|
|
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
|
|
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
|
|
options_choices = models.ManyToManyField(OptionChoice)
|
|
|
|
|
2020-05-08 16:40:18 +02:00
|
|
|
def clean(self):
|
|
|
|
if not all((ch.option.event == self.event for ch in self.options_choices)):
|
|
|
|
raise ValidationError(
|
|
|
|
_("Choix d'options incohérents avec l'événement pour cette inscription")
|
|
|
|
)
|
|
|
|
|
2019-12-22 21:27:28 +01:00
|
|
|
class Meta:
|
|
|
|
verbose_name = _("inscription à un événement")
|
|
|
|
verbose_name_plural = _("inscriptions à un événement")
|
2020-05-08 16:34:35 +02:00
|
|
|
unique_together = [["event", "user"]]
|
2019-12-22 21:27:28 +01:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "inscription de {} à {}".format(self.user, self.event)
|