Skip to content
Snippets Groups Projects
models.py 6.66 KiB
Newer Older
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
# SPDX-License-Identifier: GPL-3.0-or-later
from datetime import timedelta, datetime
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

from django.contrib.auth.models import User
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
from django.db import models
ynerant's avatar
ynerant committed
from django.db.models import Q
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
from django.utils.translation import gettext_lazy as _
ynerant's avatar
ynerant committed
from rest_framework.exceptions import ValidationError
ynerant's avatar
ynerant committed
from note.models import NoteUser, Transaction
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed


class ActivityType(models.Model):
    Type of Activity, (e.g "Pot", "Soirée Club") and associated properties.

    Activity Type are used as a search field for Activity, and determine how
    some rules about the activity:
     - Can people be invited
     - What is the entrance fee.
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    name = models.CharField(
        verbose_name=_('name'),
        max_length=255,
    )
    can_invite = models.BooleanField(
        verbose_name=_('can invite'),
    )
    guest_entry_fee = models.PositiveIntegerField(
        verbose_name=_('guest entry fee'),
    )

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    class Meta:
        verbose_name = _("activity type")
        verbose_name_plural = _("activity types")

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    def __str__(self):
        return self.name

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed

class Activity(models.Model):
    An IRL event organized by a club for other club.

    By default the invited clubs should be the Club containing all the active accounts.
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    name = models.CharField(
        verbose_name=_('name'),
        max_length=255,
    )
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    description = models.TextField(
        verbose_name=_('description'),
    )
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    activity_type = models.ForeignKey(
        ActivityType,
        on_delete=models.PROTECT,
        related_name='+',
        verbose_name=_('type'),
    )
    creater = models.ForeignKey(
        User,
        on_delete=models.PROTECT,
        verbose_name=_("user"),
    )

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    organizer = models.ForeignKey(
        'member.Club',
        on_delete=models.PROTECT,
        related_name='+',
        verbose_name=_('organizer'),
    )
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    attendees_club = models.ForeignKey(
        'member.Club',
        on_delete=models.PROTECT,
        related_name='+',
        verbose_name=_('attendees club'),
    )
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    date_start = models.DateTimeField(
        verbose_name=_('start date'),
    )
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    date_end = models.DateTimeField(
        verbose_name=_('end date'),
    )

ynerant's avatar
ynerant committed
    valid = models.BooleanField(
        default=False,
        verbose_name=_('valid'),
    )

    open = models.BooleanField(
        default=False,
        verbose_name=_('open'),
    )

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    class Meta:
        verbose_name = _("activity")
        verbose_name_plural = _("activities")

ynerant's avatar
ynerant committed
class Entry(models.Model):
    """
    Register the entry of someone:
    - a member with a :model:`note.NoteUser`
    - or a :model:`activity.Guest`
    In the case of a Guest Entry, the inviter note is also save.
    """
ynerant's avatar
ynerant committed
    activity = models.ForeignKey(
        Activity,
        on_delete=models.PROTECT,
ynerant's avatar
ynerant committed
        verbose_name=_("activity"),
    )

ynerant's avatar
ynerant committed
    time = models.DateTimeField(
ynerant's avatar
ynerant committed
        auto_now_add=True,
ynerant's avatar
ynerant committed
        verbose_name=_("entry time"),
    )

    note = models.ForeignKey(
        NoteUser,
        on_delete=models.PROTECT,
        verbose_name=_("note"),
    )

ynerant's avatar
ynerant committed
    guest = models.OneToOneField(
        'activity.Guest',
        on_delete=models.PROTECT,
        null=True,
    )

    class Meta:
        unique_together = (('activity', 'note', 'guest', ), )

    def save(self, *args,**kwargs):
ynerant's avatar
ynerant committed

        qs = Entry.objects.filter(~Q(pk=self.pk), activity=self.activity, note=self.note, guest=self.guest)
        if qs.exists():
            raise ValidationError(_("Already entered on ") + _("{:%Y-%m-%d %H:%M:%S}").format(qs.get().time, ))

        if self.guest:
            self.note = self.guest.inviter

        insert = not self.pk
        if insert:
            if self.note.balance < 0:
                raise ValidationError(_("The balance is negative."))

        ret = super().save(*args,**kwargs)
ynerant's avatar
ynerant committed

        if insert and self.guest:
            GuestTransaction.objects.create(
                source=self.note,
                destination=self.activity.organizer.note,
ynerant's avatar
ynerant committed
                quantity=1,
                amount=self.activity.activity_type.guest_entry_fee,
                reason="Invitation " + self.activity.name + " " + self.guest.first_name + " " + self.guest.last_name,
                valid=True,
                guest=self.guest,
            ).save()

        return ret

ynerant's avatar
ynerant committed

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
class Guest(models.Model):
    People who are not current members of any clubs, and are invited by someone who is a current member.
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    activity = models.ForeignKey(
        Activity,
        on_delete=models.PROTECT,
        related_name='+',
    )
ynerant's avatar
ynerant committed

    last_name = models.CharField(
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
        max_length=255,
ynerant's avatar
ynerant committed
        verbose_name=_("last name"),
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    )
ynerant's avatar
ynerant committed

    first_name = models.CharField(
        max_length=255,
        verbose_name=_("first name"),
    )

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    inviter = models.ForeignKey(
ynerant's avatar
ynerant committed
        NoteUser,
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
        on_delete=models.PROTECT,
ynerant's avatar
ynerant committed
        verbose_name=_("inviter"),
me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    )
ynerant's avatar
ynerant committed

ynerant's avatar
ynerant committed
    @property
    def has_entry(self):
        try:
            if self.entry:
                return True
            return False
        except AttributeError:
            return False
ynerant's avatar
ynerant committed

    def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
        one_year = timedelta(days=365)

        if not force_insert:
            if self.activity.date_start > datetime.now():
                raise ValidationError(_("You can't invite someone once the activity is started."))

            if not self.activity.valid:
                raise ValidationError(_("This activity is not validated yet."))

            qs = Guest.objects.filter(
                first_name=self.first_name,
                last_name=self.last_name,
                activity__date_start__gte=self.activity.date_start - one_year,
            )
            if len(qs) >= 5:
                raise ValidationError(_("This person has been already invited 5 times this year."))

            qs = qs.filter(activity=self.activity)
            if qs.exists():
                raise ValidationError(_("This person is already invited."))

            qs = Guest.objects.filter(inviter=self.inviter, activity=self.activity)
            if len(qs) >= 3:
                raise ValidationError(_("You can't invite more than 3 people to this activity."))

        return super().save(force_insert, force_update, using, update_fields)

me5na7qbjqbrp's avatar
me5na7qbjqbrp committed
    class Meta:
        verbose_name = _("guest")
        verbose_name_plural = _("guests")
        unique_together = ("activity", "last_name", "first_name", )
ynerant's avatar
ynerant committed


class GuestTransaction(Transaction):
    guest = models.OneToOneField(
        Guest,
        on_delete=models.PROTECT,
    )

    @property
    def type(self):
        return _('Invitation')