import asyncio import os import re from django.template.loader import render_to_string from nio import RoomVisibility, RoomPreset from corres2math.lists import get_sympa_client from django.core.exceptions import ObjectDoesNotExist from django.core.validators import RegexValidator from django.db import models from django.db.models import Index from django.urls import reverse_lazy from django.utils import timezone from django.utils.crypto import get_random_string from django.utils.text import format_lazy from django.utils.translation import gettext_lazy as _ from corres2math.matrix import Matrix class Team(models.Model): name = models.CharField( max_length=255, verbose_name=_("name"), unique=True, ) trigram = models.CharField( max_length=3, verbose_name=_("trigram"), help_text=_("The trigram must be composed of three uppercase letters."), unique=True, validators=[RegexValidator("[A-Z]{3}")], ) access_code = models.CharField( max_length=6, verbose_name=_("access code"), 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): return f"equipe-{self.trigram.lower()}@{os.getenv('SYMPA_HOST', 'localhost')}" def create_mailing_list(self): get_sympa_client().create_list( f"equipe-{self.trigram.lower()}", f"Équipe {self.name} ({self.trigram})", "hotline", # TODO Use a custom sympa template f"Liste de diffusion pour contacter l'équipe {self.name} des Correspondances", "education", raise_error=False, ) def delete_mailing_list(self): get_sympa_client().delete_list(f"equipe-{self.trigram}") def save(self, *args, **kwargs): if not self.access_code: self.access_code = get_random_string(6) self.create_mailing_list() Matrix.create_room( visibility=RoomVisibility.private, name=f"#équipe-{self.trigram.lower()}", alias=f"team-{self.trigram.lower()}", topic=f"Discussion de l'équipe {self.name}", preset=RoomPreset.private_chat, ) return super().save(*args, **kwargs) def get_absolute_url(self): return reverse_lazy("participation:team_detail", args=(self.pk,)) def __str__(self): return _("Team {name} ({trigram})").format(name=self.name, trigram=self.trigram) class Meta: verbose_name = _("team") verbose_name_plural = _("teams") indexes = [ Index(fields=("trigram", )), ] class Participation(models.Model): team = models.OneToOneField( Team, on_delete=models.CASCADE, verbose_name=_("team"), ) problem = models.IntegerField( choices=[(i, format_lazy(_("Problem #{problem:d}"), problem=i)) for i in range(1, 5)], null=True, default=None, verbose_name=_("problem number"), ) valid = models.BooleanField( null=True, default=None, verbose_name=_("valid"), help_text=_("The video got the validation of the administrators."), ) solution = models.OneToOneField( "participation.Video", on_delete=models.SET_NULL, related_name="participation_solution", null=True, default=None, verbose_name=_("solution video"), ) received_participation = models.OneToOneField( "participation.Participation", on_delete=models.PROTECT, related_name="sent_participation", null=True, default=None, verbose_name=_("received participation"), ) synthesis = models.OneToOneField( "participation.Video", on_delete=models.SET_NULL, related_name="participation_synthesis", null=True, default=None, verbose_name=_("synthesis video"), ) def get_absolute_url(self): return reverse_lazy("participation:participation_detail", args=(self.pk,)) def __str__(self): return _("Participation of the team {name} ({trigram})").format(name=self.team.name, trigram=self.team.trigram) class Meta: verbose_name = _("participation") verbose_name_plural = _("participations") class Video(models.Model): 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): try: return self.participation_solution except ObjectDoesNotExist: return self.participation_synthesis @property def platform(self): if "youtube.com" in self.link or "youtu.be" in self.link: return "youtube" return "unknown" @property def youtube_code(self): return re.compile("(https?://|)(www\\.|)(youtube\\.com/watch\\?v=|youtu\\.be/)([a-zA-Z0-9-_]*)?.*?")\ .match("https://www.youtube.com/watch?v=73nsrixx7eI").group(4) def as_iframe(self): 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 Phase(models.Model): phase_number = models.AutoField( primary_key=True, unique=True, verbose_name=_("phase number"), ) 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): qs = Phase.objects.filter(start__lte=timezone.now(), end__gte=timezone.now()) if qs.exists(): return qs.get() qs = Phase.objects.order_by("phase_number").all() if timezone.now() < qs.first().start: return qs.first() return qs.last() 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")