plateforme-tfjm2/apps/member/models.py

400 lines
11 KiB
Python
Raw Normal View History

2020-04-29 13:29:01 +00:00
import os
from datetime import date
2020-04-29 02:06:02 +00:00
from django.contrib.auth.models import AbstractUser
2020-09-20 19:24:52 +00:00
from django.contrib.sites.models import Site
2020-04-29 02:06:02 +00:00
from django.db import models
2020-09-20 19:24:52 +00:00
from django.template import loader
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
2020-04-29 02:06:02 +00:00
from django.utils.translation import gettext_lazy as _
from polymorphic.models import PolymorphicModel
from tournament.models import Team, Tournament
2020-09-20 19:24:52 +00:00
from .tokens import email_validation_token
2020-04-29 02:06:02 +00:00
class TFJMUser(AbstractUser):
2020-05-11 12:08:19 +00:00
"""
The model of registered users (organizers/juries/admins/coachs/participants)
"""
2020-04-29 02:06:02 +00:00
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
email = models.EmailField(
unique=True,
verbose_name=_("email"),
2020-05-11 12:08:19 +00:00
help_text=_("This should be valid and will be controlled."),
2020-04-29 02:06:02 +00:00
)
team = models.ForeignKey(
Team,
null=True,
on_delete=models.SET_NULL,
related_name="users",
verbose_name=_("team"),
2020-05-11 12:08:19 +00:00
help_text=_("Concerns only coaches and participants."),
2020-04-29 02:06:02 +00:00
)
birth_date = models.DateField(
null=True,
default=None,
verbose_name=_("birth date"),
)
gender = models.CharField(
max_length=16,
null=True,
default=None,
choices=[
("male", _("Male")),
("female", _("Female")),
("non-binary", _("Non binary")),
],
2020-04-29 13:29:01 +00:00
verbose_name=_("gender"),
2020-04-29 02:06:02 +00:00
)
address = models.CharField(
max_length=255,
null=True,
default=None,
verbose_name=_("address"),
)
2020-05-05 05:20:01 +00:00
postal_code = models.PositiveIntegerField(
2020-04-29 02:06:02 +00:00
null=True,
default=None,
verbose_name=_("postal code"),
)
city = models.CharField(
max_length=255,
null=True,
default=None,
verbose_name=_("city"),
)
country = models.CharField(
max_length=255,
default="France",
null=True,
verbose_name=_("country"),
)
phone_number = models.CharField(
max_length=20,
null=True,
blank=True,
default=None,
verbose_name=_("phone number"),
)
school = models.CharField(
max_length=255,
null=True,
default=None,
verbose_name=_("school"),
)
student_class = models.CharField(
max_length=16,
choices=[
('seconde', _("Seconde or less")),
('première', _("Première")),
('terminale', _("Terminale")),
],
null=True,
default=None,
verbose_name="class",
)
responsible_name = models.CharField(
max_length=255,
null=True,
default=None,
verbose_name=_("responsible name"),
)
responsible_phone = models.CharField(
max_length=20,
null=True,
default=None,
verbose_name=_("responsible phone"),
)
responsible_email = models.EmailField(
null=True,
default=None,
verbose_name=_("responsible email"),
)
description = models.TextField(
null=True,
default=None,
verbose_name=_("description"),
)
role = models.CharField(
max_length=16,
choices=[
2020-04-30 19:07:12 +00:00
("0admin", _("Admin")),
("1volunteer", _("Organizer")),
("2coach", _("Coach")),
("3participant", _("Participant")),
2020-04-29 02:06:02 +00:00
]
)
year = models.PositiveIntegerField(
2020-04-29 13:29:01 +00:00
default=os.getenv("TFJM_YEAR", date.today().year),
2020-04-29 02:06:02 +00:00
verbose_name=_("year"),
)
2020-09-20 19:24:52 +00:00
email_confirmed = models.BooleanField(
verbose_name=_("email confirmed"),
default=False,
)
2020-04-29 05:39:52 +00:00
@property
def participates(self):
2020-05-11 12:08:19 +00:00
"""
Return True iff this user is a participant or a coach, ie. if the user is a member of a team that worked
for the tournament.
"""
2020-05-04 22:56:34 +00:00
return self.role == "3participant" or self.role == "2coach"
2020-04-29 05:39:52 +00:00
@property
def organizes(self):
2020-05-11 12:08:19 +00:00
"""
Return True iff this user is a local or global organizer of the tournament. This includes juries.
"""
2020-04-29 14:26:52 +00:00
return self.role == "1volunteer" or self.role == "0admin"
2020-04-29 05:39:52 +00:00
@property
def admin(self):
2020-05-11 12:08:19 +00:00
"""
Return True iff this user is a global organizer, ie. an administrator. This should be equivalent to be
a superuser.
"""
2020-04-29 14:26:52 +00:00
return self.role == "0admin"
2020-04-29 05:39:52 +00:00
2020-04-29 02:06:02 +00:00
class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
2020-04-29 02:51:25 +00:00
def save(self, *args, **kwargs):
2020-05-11 12:08:19 +00:00
# We ensure that the username is the email of the user.
2020-04-29 02:51:25 +00:00
self.username = self.email
super().save(*args, **kwargs)
def __str__(self):
return self.first_name + " " + self.last_name
2020-09-20 19:24:52 +00:00
def send_email_validation_link(self):
subject = "[TFJM²] " + str(_("Activate your Note Kfet account"))
token = email_validation_token.make_token(self)
uid = urlsafe_base64_encode(force_bytes(self.pk))
site = Site.objects.first()
message = loader.render_to_string('registration/mails/email_validation_email.txt',
{
'user': self,
'domain': site.domain,
'token': token,
'uid': uid,
})
html = loader.render_to_string('registration/mails/email_validation_email.html',
{
'user': self,
'domain': site.domain,
'token': token,
'uid': uid,
})
self.email_user(subject, message, html_message=html)
2020-04-29 02:06:02 +00:00
2020-04-29 04:52:39 +00:00
class Document(PolymorphicModel):
2020-05-11 12:08:19 +00:00
"""
Abstract model of any saved document (solution, synthesis, motivation letter, authorization)
"""
2020-04-29 02:06:02 +00:00
file = models.FileField(
unique=True,
verbose_name=_("file"),
)
2020-04-29 04:52:39 +00:00
uploaded_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("uploaded at"),
2020-04-29 02:06:02 +00:00
)
2020-04-29 04:52:39 +00:00
class Meta:
verbose_name = _("document")
verbose_name_plural = _("documents")
2020-05-04 21:37:21 +00:00
def delete(self, *args, **kwargs):
self.file.delete(True)
return super().delete(*args, **kwargs)
2020-04-29 04:52:39 +00:00
class Authorization(Document):
2020-05-11 12:08:19 +00:00
"""
Model for authorization papers (parental consent, photo consent, sanitary plug, ...)
"""
2020-04-29 04:52:39 +00:00
user = models.ForeignKey(
TFJMUser,
2020-04-29 02:06:02 +00:00
on_delete=models.CASCADE,
2020-04-29 04:52:39 +00:00
related_name="authorizations",
verbose_name=_("user"),
2020-04-29 02:06:02 +00:00
)
type = models.CharField(
max_length=32,
choices=[
("parental_consent", _("Parental consent")),
("photo_consent", _("Photo consent")),
("sanitary_plug", _("Sanitary plug")),
("scholarship", _("Scholarship")),
],
verbose_name=_("type"),
)
class Meta:
2020-04-29 04:52:39 +00:00
verbose_name = _("authorization")
verbose_name_plural = _("authorizations")
def __str__(self):
return _("{authorization} for user {user}").format(authorization=self.type, user=str(self.user))
2020-04-29 02:06:02 +00:00
2020-04-29 04:52:39 +00:00
class MotivationLetter(Document):
2020-05-11 12:08:19 +00:00
"""
Model for motivation letters of a team.
"""
2020-04-29 04:52:39 +00:00
team = models.ForeignKey(
Team,
on_delete=models.CASCADE,
related_name="motivation_letters",
verbose_name=_("team"),
)
2020-04-29 02:06:02 +00:00
class Meta:
2020-04-29 04:52:39 +00:00
verbose_name = _("motivation letter")
verbose_name_plural = _("motivation letters")
def __str__(self):
return _("Motivation letter of team {team} ({trigram})").format(team=self.team.name, trigram=self.team.trigram)
2020-04-29 02:06:02 +00:00
class Solution(Document):
2020-05-11 12:08:19 +00:00
"""
Model for solutions of team for a given problem, for the regional or final tournament.
"""
2020-04-29 04:52:39 +00:00
team = models.ForeignKey(
Team,
on_delete=models.CASCADE,
related_name="solutions",
verbose_name=_("team"),
)
2020-04-29 02:06:02 +00:00
problem = models.PositiveSmallIntegerField(
verbose_name=_("problem"),
)
2020-05-04 21:37:21 +00:00
final = models.BooleanField(
default=False,
verbose_name=_("final solution"),
)
2020-04-29 02:06:02 +00:00
2020-05-04 22:11:38 +00:00
@property
def tournament(self):
2020-05-11 12:08:19 +00:00
"""
Get the concerned tournament of a solution.
Generally the local tournament of a team, but it can be the final tournament if this is a solution for the
final tournament.
"""
2020-05-04 22:11:38 +00:00
return Tournament.get_final() if self.final else self.team.tournament
2020-04-29 02:06:02 +00:00
class Meta:
verbose_name = _("solution")
verbose_name_plural = _("solutions")
2020-05-04 21:37:21 +00:00
unique_together = ('team', 'problem', 'final',)
2020-04-29 02:06:02 +00:00
2020-04-29 04:52:39 +00:00
def __str__(self):
2020-05-11 12:21:55 +00:00
if self.final:
return _("Solution of team {trigram} for problem {problem} for final")\
.format(trigram=self.team.trigram, problem=self.problem)
else:
return _("Solution of team {trigram} for problem {problem}")\
.format(trigram=self.team.trigram, problem=self.problem)
2020-04-29 04:52:39 +00:00
2020-04-29 02:06:02 +00:00
class Synthesis(Document):
2020-05-11 12:08:19 +00:00
"""
Model for syntheses of a team for a given round and for a given role, for the regional or final tournament.
"""
2020-04-29 04:52:39 +00:00
team = models.ForeignKey(
Team,
on_delete=models.CASCADE,
related_name="syntheses",
verbose_name=_("team"),
)
2020-05-04 22:11:38 +00:00
source = models.CharField(
2020-04-29 04:52:39 +00:00
max_length=16,
choices=[
("opponent", _("Opponent")),
("rapporteur", _("Rapporteur")),
],
2020-05-04 22:11:38 +00:00
verbose_name=_("source"),
2020-04-29 02:06:02 +00:00
)
2020-04-29 23:20:50 +00:00
round = models.PositiveSmallIntegerField(
choices=[
(1, _("Round 1")),
(2, _("Round 2")),
],
verbose_name=_("round"),
)
2020-05-04 21:37:21 +00:00
final = models.BooleanField(
default=False,
verbose_name=_("final synthesis"),
)
2020-04-29 02:06:02 +00:00
2020-05-04 22:11:38 +00:00
@property
def tournament(self):
2020-05-11 12:08:19 +00:00
"""
Get the concerned tournament of a solution.
Generally the local tournament of a team, but it can be the final tournament if this is a solution for the
final tournament.
"""
2020-05-04 22:11:38 +00:00
return Tournament.get_final() if self.final else self.team.tournament
2020-04-29 02:06:02 +00:00
class Meta:
verbose_name = _("synthesis")
verbose_name_plural = _("syntheses")
2020-05-04 22:11:38 +00:00
unique_together = ('team', 'source', 'round', 'final',)
2020-04-29 04:52:39 +00:00
def __str__(self):
return _("Synthesis of team {trigram} that is {source} for the round {round} of tournament {tournament}")\
.format(trigram=self.team.trigram, source=self.get_source_display().lower(), round=self.round,
tournament=self.tournament)
2020-04-29 05:39:52 +00:00
class Config(models.Model):
2020-05-11 12:08:19 +00:00
"""
Dictionary of configuration variables.
"""
2020-04-29 05:39:52 +00:00
key = models.CharField(
max_length=255,
primary_key=True,
verbose_name=_("key"),
)
value = models.TextField(
default="",
verbose_name=_("value"),
)
class Meta:
verbose_name = _("configuration")
verbose_name_plural = _("configurations")