kpsul/gestion/models.py
2017-10-25 23:36:18 +02:00

432 lines
12 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from django.contrib.auth.models import Group, Permission, User
from django.db import models
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from cof.petits_cours_models import choices_length
# ---
# User management
# ---
OCCUPATION_CHOICES = (
('exterieur', _("Extérieur")),
('1A', _("1A")),
('2A', _("2A")),
('3A', _("3A")),
('4A', _("4A")),
('archicube', _("Archicube")),
('doctorant', _("Doctorant")),
('CST', _("CST")),
)
class Profile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
related_name="profile",
verbose_name=_("utilisateur"),
)
login_clipper = models.CharField(
_("login clipper"),
max_length=8,
blank=True,
)
phone = models.CharField(_("téléphone"), max_length=20, blank=True)
occupation = models.CharField(_("occupation"),
default="1A",
choices=OCCUPATION_CHOICES,
max_length=choices_length(
OCCUPATION_CHOICES))
departement = models.CharField(_("département"), max_length=50,
blank=True)
comments = models.TextField(
_("commentaires visibles par l'utilisateur"), blank=True
)
class Meta:
verbose_name = _("profil")
verbose_name_plural = _("profils")
def __str__(self):
return self.user.username
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.get_or_create(user=instance)
@receiver(post_delete, sender=Profile)
def post_delete_user(sender, instance, *args, **kwargs):
instance.user.delete()
class AssociationManager(models.Manager):
def get_by_natural_key(self, name):
return self.get(name=name)
class Association(models.Model):
objects = AssociationManager()
name = models.CharField(
_("nom de l'association"),
unique=True,
max_length=30
)
staff_group = models.ForeignKey(
Group,
on_delete=models.PROTECT,
related_name="staff_group_of",
blank=True, null=True,
verbose_name=_("groupe des membres du bureau"),
)
members_group = models.ForeignKey(
Group,
on_delete=models.PROTECT,
related_name="member_group_of",
blank=True, null=True,
verbose_name=_("groupe des membres"),
)
def natural_key(self):
return [self.name]
class Meta:
verbose_name = _("association")
verbose_name_plural = _("associations")
def __str__(self):
return self.name
def setup_perms(self, buro_of_apps=[], perms=[]):
"""
Setup permissions of the staff and members groups.
Permission '<assoc_label>.member' is added to the 'members_group' of
the association.
Permission '<assoc_label>.buro' is added to the 'staff_group' of the
association. All permissions of applications from 'buro_of_apps' are
also added to this group.
Arguments
buro_of_apps (list of 'app_label', optional)
perms (list of permission codes, optional)
Should be used in receiver of 'post_migrate' signal, after permissions
creation.
"""
def try_add_perm(group, app_label, codename):
try:
perm = Permission.objects.get(
content_type__app_label=app_label,
codename=codename,
)
except Permission.DoesNotExist:
pass
else:
group.permissions.add(perm)
assoc_app_label = self.name.lower()
# Buro group has perm '<assoc>.buro'.
try_add_perm(self.staff_group, assoc_app_label, 'buro')
# Add all permissions of applications given 'buro_of_apps'.
apps_perms = Permission.objects.filter(
content_type__app_label__in=buro_of_apps,
)
self.staff_group.permissions.add(*apps_perms)
# Add extra permissions from 'perms'
for perm in perms:
app_label, codename = perm.split('.', maxsplit=1)
try_add_perm(self.staff_group, app_label, codename)
# Members have perm '<assoc>.member'.
try_add_perm(self.members_group, assoc_app_label, 'member')
# ---
# Clubs
# ---
class Club(models.Model):
ANNUAL = "ANN"
SEMESTER = "SEM"
COURSE = "COU"
COTISATION_FREQUENCY_CHOICES = [
(ANNUAL, _("Annuel")),
(SEMESTER, _("Semestriel")),
(COURSE, _("Au cours"))
]
association = models.ForeignKey(
Association,
on_delete=models.PROTECT,
related_name="clubs",
verbose_name=_("association"),
)
name = models.CharField(_("nom"), max_length=200, unique=True)
description = models.TextField(_("description"), blank=True)
members = models.ManyToManyField(
User,
through="ClubUser",
related_name="in_clubs",
blank=True,
verbose_name=_("membres du club"),
)
price = models.DecimalField(
_("cotisation (€)"),
decimal_places=2,
max_digits=5,
blank=True,
default=0
)
cotisation_frequency = models.CharField(
_("fréquence de la cotisation"),
default=ANNUAL,
choices=COTISATION_FREQUENCY_CHOICES,
max_length=3,
blank=True
)
class Meta:
verbose_name = _("club")
verbose_name_plural = _("clubs")
def __str__(self):
template = (
self.needs_cotiz and
"%(name)s (%(price).2f€ / %(cotisation_frequency)s)" or
"%(name)s"
)
return template % vars(self)
def get_absolute_url(self):
return reverse('gestion:club_detail', args=[str(self.pk)])
@property
def needs_cotiz(self):
return bool(self.price)
def is_respo(self, user):
return self.clubuser_set.filter(user=user, is_respo=True).exists()
class ClubUser(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
club = models.ForeignKey(Club, on_delete=models.CASCADE)
is_respo = models.BooleanField(_("est responsable du club"))
has_paid = models.BooleanField(_("a payé sa cotisation"))
class Meta:
verbose_name = _("inscription au club")
verbose_name_plural = _("inscriptions aux clubs")
# ---
# Events
# ---
class Location(models.Model):
name = models.CharField(_("nom du lieu"), max_length=200)
class Meta:
verbose_name = _("lieu")
verbose_name_plural = _("lieux")
def __str__(self):
return self.name
class Event(models.Model):
associations = models.ManyToManyField(
Association,
related_name="events",
verbose_name=_("associations"),
)
title = models.CharField(_("titre"), max_length=200)
location = models.ForeignKey(
Location,
blank=True, null=True,
on_delete=models.PROTECT,
verbose_name=_("lieux"),
)
start_date = models.DateTimeField(
_("début de l'événement"),
blank=True, null=True,
)
end_date = models.DateTimeField(
_("fin de l'événement"),
blank=True, null=True,
)
description = models.TextField(_("description"), blank=True)
image = models.ImageField(
_("image"),
blank=True, null=True,
upload_to="public/imgs/events/",
)
registration_open = models.NullBooleanField(
_("les inscriptions sont ouvertes"),
help_text=_("Indéfini signifie « l'inscription n'est pas requise »"),
default=True,
)
old = models.BooleanField(_("archiver (événement fini)"), default=False)
class Meta:
verbose_name = _("événement")
verbose_name_plural = _("événements")
def __str__(self):
title, location = self.title, self.location
start = self.start_date.date()
end = self.end_date.date()
if not self.start_date.date():
return "{} @ {}".format(title, location)
elif start == end:
return "{} @ {} ({})".format(title, location, start)
else:
return "{} @ {} ({}{})".format(title, location, start, end)
class EventCommentField(models.Model):
TEXT = "text"
CHAR = "char"
FIELD_TYPE = [
(TEXT, _("Texte long")),
(CHAR, _("Texte court")),
]
event = models.ForeignKey(
Event,
on_delete=models.CASCADE,
related_name="commentfields",
verbose_name=_("événement"),
)
name = models.CharField(_("nom du champ"), max_length=200)
fieldtype = models.CharField(
_("type de champ"),
max_length=10,
choices=FIELD_TYPE, default=TEXT,
)
default = models.TextField(_("valeur par défaut"), blank=True)
ordering = models.IntegerField(
_("ordre des champs"),
default=0,
help_text=_(
"plus petit en premier, ordre alphabétique sur le nom si "
"ambiguïté"
),
)
class Meta:
verbose_name = _("champ")
verbose_name_plural = _("champs")
def __str__(self):
return self.name
class EventCommentValue(models.Model):
commentfield = models.ForeignKey(
EventCommentField,
on_delete=models.CASCADE,
related_name="values"
)
registration = models.ForeignKey(
"EventRegistration",
on_delete=models.CASCADE,
related_name="comments"
)
content = models.TextField(_("contenu"), blank=True)
class Meta:
unique_together = ("commentfield", "registration")
def __str__(self):
return (
_("Commentaire de %(field_name)s")
% {"field_name": self.commentfield}
)
class EventOption(models.Model):
event = models.ForeignKey(
Event,
on_delete=models.CASCADE,
related_name="options",
verbose_name=_("événement"),
)
name = models.CharField(_("option"), max_length=200)
multi_choices = models.BooleanField(_("choix multiples"), default=False)
class Meta:
verbose_name = _("option des événements")
verbose_name_plural = _("options des événements")
def __str__(self):
return self.name
class EventOptionChoice(models.Model):
event_option = models.ForeignKey(
EventOption,
on_delete=models.CASCADE,
related_name="choices",
verbose_name=_("événement"),
)
value = models.CharField("Valeur", max_length=200)
class Meta:
verbose_name = _("choix")
verbose_name_plural = _("choix")
def __str__(self):
return self.value
class EventRegistration(models.Model):
user = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
verbose_name=_("utilisateur"),
)
event = models.ForeignKey(
Event,
on_delete=models.CASCADE,
verbose_name=_("événement"),
)
options = models.ManyToManyField(
EventOptionChoice,
verbose_name=_("choix"),
)
filledcomments = models.ManyToManyField(
EventCommentField,
through=EventCommentValue,
verbose_name=_("commentaires"),
)
paid = models.BooleanField(_("a payé"), default=False)
class Meta:
verbose_name = _("inscription")
verbose_name_plural = _("inscriptions")
unique_together = ("user", "event")
def __str__(self):
return (
_("Inscription de %(user)s à %(event)s")
% {"user": self.user, "event": self.event}
)