From 18b4fa81d39edcca236ef81cb461c644d1289471 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 28 Dec 2020 13:47:05 +0100 Subject: [PATCH] Prepare the data structure for the participations --- apps/participation/models.py | 266 +++++++++++++++++++---------------- 1 file changed, 145 insertions(+), 121 deletions(-) diff --git a/apps/participation/models.py b/apps/participation/models.py index aa69e5f..ef66e45 100644 --- a/apps/participation/models.py +++ b/apps/participation/models.py @@ -4,6 +4,7 @@ import os import re +from registration.models import VolunteerRegistration from tfjm.lists import get_sympa_client from tfjm.matrix import Matrix, RoomPreset, RoomVisibility from django.core.exceptions import ObjectDoesNotExist @@ -43,12 +44,6 @@ class Team(models.Model): help_text=_("The access code let other people to join the team."), ) - grant_animath_access_videos = models.BooleanField( - verbose_name=_("Grant Animath to publish my video"), - help_text=_("Give the authorisation to publish the video on the main website to promote the action."), - default=False, - ) - @property def email(self): """ @@ -117,6 +112,95 @@ class Team(models.Model): ] +class Tournament(models.Model): + name = models.CharField( + max_length=255, + verbose_name=_("name"), + unique=True, + ) + + date_start = models.DateField( + verbose_name=_("start"), + default=timezone.now, + ) + + date_end = models.DateField( + verbose_name=_("start"), + default=timezone.now, + ) + + inscription_limit = models.DateTimeField( + verbose_name=_("limit date for registrations"), + default=timezone.now, + ) + + solution_limit = models.DateTimeField( + verbose_name=_("limit date to upload solutions"), + default=timezone.now, + ) + + syntheses_first_phase_limit = models.DateTimeField( + verbose_name=_("limit date to upload the syntheses for the first phase"), + default=timezone.now, + ) + + syntheses_second_phase_limit = models.DateTimeField( + verbose_name=_("limit date to upload the syntheses for the second phase"), + default=timezone.now, + ) + + description = models.TextField( + verbose_name=_("description"), + blank=True, + ) + + organizers = models.ManyToManyField( + VolunteerRegistration, + verbose_name=_("organizers"), + related_name="organized_tournaments", + ) + + final = models.BooleanField( + verbose_name=_("final"), + default=False, + ) + + @staticmethod + def final_tournament(): + qs = Tournament.objects.filter(final=True) + if qs.exists(): + return qs.get() + + class Meta: + verbose_name = _("tournament") + verbose_name_plural = _("tournaments") + indexes = [ + Index(fields=("name", "date_start", "date_end", )), + ] + + +class Pool(models.Model): + tournament = models.ForeignKey( + Tournament, + on_delete=models.CASCADE, + verbose_name=_("tournament"), + ) + + round = models.PositiveSmallIntegerField( + verbose_name=_("round"), + ) + + juries = models.ManyToManyField( + VolunteerRegistration, + verbose_name=_("juries"), + related_name="jury_in", + ) + + class Meta: + verbose_name = _("pool") + verbose_name_plural = _("pools") + + class Participation(models.Model): """ The Participation model contains all data that are related to the participation: @@ -128,11 +212,13 @@ class Participation(models.Model): verbose_name=_("team"), ) - problem = models.IntegerField( - choices=[(i, format_lazy(_("Problem #{problem:d}"), problem=i)) for i in range(1, 4)], + tournament = models.ForeignKey( + Tournament, + on_delete=models.SET_NULL, null=True, + blank=True, default=None, - verbose_name=_("problem number"), + verbose_name=_("tournament"), ) valid = models.BooleanField( @@ -180,128 +266,66 @@ class Participation(models.Model): verbose_name_plural = _("participations") -class Video(models.Model): - """ - The Video model only contains a link and a validity status. - """ - link = models.URLField( - verbose_name=_("link"), - help_text=_("The full video link."), - ) - - valid = models.BooleanField( - null=True, - default=None, - verbose_name=_("valid"), - help_text=_("The video got the validation of the administrators."), - ) - - @property - def participation(self): - """ - Retrives the participation that is associated to this video, - whatever it is a solution or a synthesis. - """ - try: - # If this is a solution - return self.participation_solution - except ObjectDoesNotExist: - # If this is a synthesis - return self.participation_synthesis - - @property - def platform(self): - """ - According to the link, retrieve the platform that is used to upload the video. - """ - if "youtube.com" in self.link or "youtu.be" in self.link: - return "youtube" - return "unknown" - - @property - def youtube_code(self): - """ - If the video is uploaded on Youtube, search in the URL the video code. - """ - return re.compile("(https?://|)(www\\.|)(youtube\\.com/watch\\?v=|youtu\\.be/)([a-zA-Z0-9-_]*)?.*?")\ - .match(self.link).group(4) - - def as_iframe(self): - """ - Generate the HTML code to embed the video in an iframe, according to the type of the host platform. - """ - if self.platform == "youtube": - return render_to_string("participation/youtube_iframe.html", context=dict(youtube_code=self.youtube_code)) - return None - - def __str__(self): - return _("Video of team {name} ({trigram})")\ - .format(name=self.participation.team.name, trigram=self.participation.team.trigram) - - class Meta: - verbose_name = _("video") - verbose_name_plural = _("videos") - - -class Question(models.Model): - """ - Question to ask to the team that sent a solution. - """ +class Solution(models.Model): participation = models.ForeignKey( Participation, on_delete=models.CASCADE, verbose_name=_("participation"), - related_name="questions", + related_name="solutions", ) - question = models.TextField( - verbose_name=_("question"), + problem = models.PositiveSmallIntegerField( + verbose_name=_("problem"), ) - def __str__(self): - return self.question + final_solution = models.BooleanField( + verbose_name=_("solution for the final tournament"), + default=False, + ) - -class Phase(models.Model): - """ - The Phase model corresponds to the dates of the phase. - """ - phase_number = models.AutoField( - primary_key=True, + file = models.FileField( + verbose_name=_("file"), + upload_to="solutions/", unique=True, - verbose_name=_("phase number"), + blank=True, + default="", ) - description = models.CharField( - max_length=255, - verbose_name=_("phase description"), - ) - - start = models.DateTimeField( - verbose_name=_("start date of the given phase"), - default=timezone.now, - ) - - end = models.DateTimeField( - verbose_name=_("end date of the given phase"), - default=timezone.now, - ) - - @classmethod - def current_phase(cls): - """ - Retrieve the current phase of this day - """ - qs = Phase.objects.filter(start__lte=timezone.now(), end__gte=timezone.now()) - if qs.exists(): - return qs.get() - qs = Phase.objects.filter(start__lte=timezone.now()).order_by("phase_number").all() - return qs.last() if qs.exists() else None - - def __str__(self): - return _("Phase {phase_number:d} starts on {start:%Y-%m-%d %H:%M} and ends on {end:%Y-%m-%d %H:%M}")\ - .format(phase_number=self.phase_number, start=self.start, end=self.end) - class Meta: - verbose_name = _("phase") - verbose_name_plural = _("phases") + verbose_name = _("solution") + verbose_name_plural = _("solutions") + unique_by = (('participation', 'problem', 'final_solution', ), ) + + +class Synthesis(models.Model): + participation = models.ForeignKey( + Participation, + on_delete=models.CASCADE, + verbose_name=_("participation"), + ) + + pool = models.ForeignKey( + Pool, + on_delete=models.CASCADE, + verbose_name=_("pool"), + ) + + type = models.PositiveSmallIntegerField( + choices=[ + (1, _("opponent"), ), + (2, _("reporter"), ), + ] + ) + + file = models.FileField( + verbose_name=_("file"), + upload_to="syntheses/", + unique=True, + blank=True, + default="", + ) + + class Meta: + verbose_name = _("synthesis") + verbose_name_plural = _("syntheses") + unique_by = (('participation', 'pool', 'type', ), )