poulpe/event/models.py
2018-08-26 21:18:51 +02:00

323 lines
9.9 KiB
Python

from django.contrib.auth import get_user_model
from django.core.exceptions import FieldDoesNotExist, FieldError, ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _
from communication.models import SubscriptionMixin
from .validators import ColorValidator
User = get_user_model()
class Event(SubscriptionMixin, models.Model):
title = models.CharField(
_("nom de l'évènement"),
max_length=200,
)
slug = models.SlugField(
_("identificateur"),
unique=True,
help_text=_(
"Seulement des lettres, des chiffres ou les caractères '_' ou '-'."
),
)
created_by = models.ForeignKey(
User,
verbose_name=_("créé par"),
on_delete=models.SET_NULL,
related_name="created_events",
editable=False, null=True,
)
created_at = models.DateTimeField(
_('date de création'),
auto_now_add=True,
)
description = models.TextField(_('description'))
beginning_date = models.DateTimeField(
_('date de début'),
help_text=_("date publique de l'évènement"),
)
ending_date = models.DateTimeField(
_('date de fin'),
help_text=_("date publique de l'évènement"),
)
class Meta:
verbose_name = _("évènement")
verbose_name_plural = _("évènements")
def __str__(self):
return self.title
class EventSpecificMixin(models.Model):
"""Mixin allowing for event-specific models instances
or not (depending on whether the event field is null)"""
event = models.ForeignKey(
Event,
verbose_name=_("évènement"),
help_text=_(
"Si spécifié, l'instance du modèle est spécifique à l'évènement "
"en question."
),
on_delete=models.CASCADE,
blank=True, null=True,
)
class Meta:
abstract = True
class Place(EventSpecificMixin, models.Model):
name = models.CharField(
_("nom du lieu"),
max_length=200,
)
description = models.TextField(blank=True)
class Meta:
verbose_name = _("lieu")
verbose_name_plural = _("lieux")
def __str__(self):
return self.name
class ActivityTag(EventSpecificMixin, models.Model):
name = models.CharField(
_("nom du tag"),
max_length=200,
)
is_public = models.BooleanField(
_("est public"),
help_text=_(
"Sert à faire une distinction dans l'affichage selon que le tag "
"soit destiné au public ou à l'organisation."
),
)
color = models.CharField(
_('couleur'),
max_length=7,
validators=[ColorValidator],
help_text=_("Rentrer une couleur en hexadécimal (#XXX ou #XXXXXX)."),
)
class Meta:
verbose_name = _("tag")
verbose_name_plural = _("tags")
def __str__(self):
return self.name
class AbstractActivityTemplate(SubscriptionMixin, models.Model):
title = models.CharField(
_("nom de l'activité"),
max_length=200,
blank=True, null=True,
)
# FIXME: voir comment on traite l'héritage de `event`
event = models.ForeignKey(
Event,
verbose_name=_("évènement"),
on_delete=models.CASCADE,
)
is_public = models.NullBooleanField(
_("est public"),
blank=True,
)
has_perm = models.NullBooleanField(
_("inscription de permanents"),
blank=True,
)
min_perm = models.PositiveSmallIntegerField(
_('nombre minimum de permanents'),
blank=True, null=True,
)
max_perm = models.PositiveSmallIntegerField(
_('nombre maximum de permanents'),
blank=True, null=True,
)
description = models.TextField(
_('description'),
help_text=_("Visible par tout le monde si l'événément est public."),
blank=True, null=True,
)
remarks = models.TextField(
_('remarques'),
help_text=_("Visible uniquement par les organisateurs."),
blank=True, null=True,
)
tags = models.ManyToManyField(
ActivityTag,
verbose_name=_('tags'),
blank=True,
)
places = models.ManyToManyField(
Place,
verbose_name=_('lieux'),
blank=True,
)
class Meta:
abstract = True
class ActivityTemplate(AbstractActivityTemplate):
name = models.CharField(
_("Nom du template"),
max_length=200,
help_text=_("Ne sera pas affiché"),
)
class Meta:
verbose_name = _("template activité")
verbose_name_plural = _("templates activité")
def __str__(self):
return self.name
def clean(self):
errors = []
# On clean les nombre de permanents
if not self.has_perm:
self.max_perm = None
self.min_perm = None
else:
if self.min_perm > self.max_perm:
errors.append(ValidationError(
_("Nombres de permanents incompatibles"),
code='wrong-nb-perm',
))
if errors != []:
raise ValidationError(errors)
class Activity(AbstractActivityTemplate):
parent = models.ForeignKey(
ActivityTemplate,
verbose_name=_("template"),
on_delete=models.PROTECT,
related_name="children",
blank=True, null=True,
)
staff = models.ManyToManyField(
User,
verbose_name=_("permanents"),
related_name="in_perm_activities",
blank=True,
)
beginning = models.DateTimeField(_("heure de début"))
end = models.DateTimeField(_("heure de fin"))
def clean(self):
errors = []
# On clean les nombre de permanents
if not self.get_herited('has_perm'):
self.max_perm = None
self.min_perm = None
else:
if self.get_herited('min_perm') > self.get_herited('max_perm'):
errors.append(ValidationError(
_("Nombres de permanents incompatibles"),
code='wrong-nb-perm',
))
# On valide l'héritage
for f in self._meta.get_fields():
try:
# On réccupère le field du parent
attrname = f.name
tpl_field = ActivityTemplate._meta.get_field(attrname)
# Peut-être que ce n'est pas un field
# concerné par l'héritage
except FieldDoesNotExist:
continue
# Y'a certains champs dont on se moque
if attrname in ['id', 'staff', 'tags', ]:
continue
# C'est plus compliqué que ça pour les nb_perm
if attrname in ['max_perm', 'min_perm', ]:
if not self.get_herited('has_perm'):
continue
# On a un Many to Many, on lit différement
if tpl_field.many_to_many:
pass
# # On a pas spécifié
# if not value.exists():
# # On a pas de parent
# if self.parent is None:
# errors.append(ValidationError(
# _("N'hérite pas d'un template, spécifier le champs : %(attr)s"),
# code='bad-overriding',
# params={'attr': f.verbose_name},
# ))
# else:
# pvalue = getattr(self.parent, attrname)
# # On a un parent qui ne dit rien
# if not pvalue.exists():
# errors.append(ValidationError(
# _("Champs non précisé chez le parent, spécifier : %(attr)s"),
# code='bad-overriding',
# params={'attr': f.verbose_name},
# ))
else:
value = getattr(self, attrname)
# On a pas spécifié
if value is None:
# On a pas de parent
if self.parent is None:
errors.append(ValidationError(
_("N'hérite pas d'un template, spécifier le champs : %(attr)s"),
code='bad-overriding',
params={'attr': f.verbose_name},
))
else:
pvalue = getattr(self.parent, attrname)
# On a un parent qui ne dit rien
if pvalue is None:
errors.append(ValidationError(
_("Champs non précisé chez le parent, spécifier : %(attr)s"),
code='bad-overriding',
params={'attr': f.verbose_name},
))
if errors != []:
raise ValidationError(errors)
def get_herited(self, attrname):
try:
tpl_field = ActivityTemplate._meta.get_field(attrname)
except FieldDoesNotExist:
raise FieldError(
"%(attrname)s field can't be herited.",
params={'attrname': attrname},
)
value = getattr(self, attrname)
if tpl_field.many_to_many:
if value.exists():
return value
elif self.parent is not None:
return getattr(self.parent, attrname)
elif value is None and self.parent is not None:
return getattr(self.parent, attrname)
else:
return value
class Meta:
verbose_name = _("activité")
verbose_name_plural = _("activités")
def __str__(self):
return self.get_herited('title')