mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-11-04 10:22:11 +01:00 
			
		
		
		
	Prepare the data structure for the participations
This commit is contained in:
		@@ -4,6 +4,7 @@
 | 
				
			|||||||
import os
 | 
					import os
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from registration.models import VolunteerRegistration
 | 
				
			||||||
from tfjm.lists import get_sympa_client
 | 
					from tfjm.lists import get_sympa_client
 | 
				
			||||||
from tfjm.matrix import Matrix, RoomPreset, RoomVisibility
 | 
					from tfjm.matrix import Matrix, RoomPreset, RoomVisibility
 | 
				
			||||||
from django.core.exceptions import ObjectDoesNotExist
 | 
					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."),
 | 
					        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
 | 
					    @property
 | 
				
			||||||
    def email(self):
 | 
					    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):
 | 
					class Participation(models.Model):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    The Participation model contains all data that are related to the participation:
 | 
					    The Participation model contains all data that are related to the participation:
 | 
				
			||||||
@@ -128,11 +212,13 @@ class Participation(models.Model):
 | 
				
			|||||||
        verbose_name=_("team"),
 | 
					        verbose_name=_("team"),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    problem = models.IntegerField(
 | 
					    tournament = models.ForeignKey(
 | 
				
			||||||
        choices=[(i, format_lazy(_("Problem #{problem:d}"), problem=i)) for i in range(1, 4)],
 | 
					        Tournament,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
        null=True,
 | 
					        null=True,
 | 
				
			||||||
 | 
					        blank=True,
 | 
				
			||||||
        default=None,
 | 
					        default=None,
 | 
				
			||||||
        verbose_name=_("problem number"),
 | 
					        verbose_name=_("tournament"),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    valid = models.BooleanField(
 | 
					    valid = models.BooleanField(
 | 
				
			||||||
@@ -180,128 +266,66 @@ class Participation(models.Model):
 | 
				
			|||||||
        verbose_name_plural = _("participations")
 | 
					        verbose_name_plural = _("participations")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Video(models.Model):
 | 
					class Solution(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.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    participation = models.ForeignKey(
 | 
					    participation = models.ForeignKey(
 | 
				
			||||||
        Participation,
 | 
					        Participation,
 | 
				
			||||||
        on_delete=models.CASCADE,
 | 
					        on_delete=models.CASCADE,
 | 
				
			||||||
        verbose_name=_("participation"),
 | 
					        verbose_name=_("participation"),
 | 
				
			||||||
        related_name="questions",
 | 
					        related_name="solutions",
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    question = models.TextField(
 | 
					    problem = models.PositiveSmallIntegerField(
 | 
				
			||||||
        verbose_name=_("question"),
 | 
					        verbose_name=_("problem"),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    final_solution = models.BooleanField(
 | 
				
			||||||
        return self.question
 | 
					        verbose_name=_("solution for the final tournament"),
 | 
				
			||||||
 | 
					        default=False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    file = models.FileField(
 | 
				
			||||||
class Phase(models.Model):
 | 
					        verbose_name=_("file"),
 | 
				
			||||||
    """
 | 
					        upload_to="solutions/",
 | 
				
			||||||
    The Phase model corresponds to the dates of the phase.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    phase_number = models.AutoField(
 | 
					 | 
				
			||||||
        primary_key=True,
 | 
					 | 
				
			||||||
        unique=True,
 | 
					        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:
 | 
					    class Meta:
 | 
				
			||||||
        verbose_name = _("phase")
 | 
					        verbose_name = _("solution")
 | 
				
			||||||
        verbose_name_plural = _("phases")
 | 
					        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', ), )
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user