diff --git a/apps/participation/forms.py b/apps/participation/forms.py index b2ac9d3..66d7965 100644 --- a/apps/participation/forms.py +++ b/apps/participation/forms.py @@ -4,7 +4,7 @@ from django import forms from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ -from .models import Participation, Team +from .models import Participation, Team, Video class TeamForm(forms.ModelForm): @@ -42,3 +42,9 @@ class ParticipationForm(forms.ModelForm): class Meta: model = Participation fields = ('problem',) + + +class UploadVideoForm(forms.ModelForm): + class Meta: + model = Video + fields = ('link',) diff --git a/apps/participation/models.py b/apps/participation/models.py index 029f6f9..6867bb2 100644 --- a/apps/participation/models.py +++ b/apps/participation/models.py @@ -1,3 +1,6 @@ +import re + +from django.core.exceptions import ObjectDoesNotExist from django.core.validators import RegexValidator from django.db import models from django.db.models import Index @@ -63,10 +66,10 @@ class Participation(models.Model): verbose_name=_("problem number"), ) - solution = models.ForeignKey( + solution = models.OneToOneField( "participation.Video", on_delete=models.SET_NULL, - related_name="+", + related_name="participation_solution", null=True, default=None, verbose_name=_("solution video"), @@ -81,10 +84,10 @@ class Participation(models.Model): verbose_name=_("received participation"), ) - synthesis = models.ForeignKey( + synthesis = models.OneToOneField( "participation.Video", on_delete=models.SET_NULL, - related_name="+", + related_name="participation_synthesis", null=True, default=None, verbose_name=_("synthesis video"), @@ -99,12 +102,6 @@ class Participation(models.Model): class Video(models.Model): - participation = models.ForeignKey( - "participation.Participation", - on_delete=models.CASCADE, - verbose_name=_("participation"), - ) - link = models.URLField( verbose_name=_("link"), help_text=_("The full video link."), @@ -117,6 +114,24 @@ class Video(models.Model): 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 __str__(self): return _("Video of team {name} ({trigram})")\ .format(name=self.participation.team.name, trigram=self.participation.team.trigram) diff --git a/apps/participation/signals.py b/apps/participation/signals.py index 16a52af..2f8f222 100644 --- a/apps/participation/signals.py +++ b/apps/participation/signals.py @@ -1,5 +1,10 @@ -from participation.models import Participation +from participation.models import Participation, Video def create_team_participation(instance, **_): - Participation.objects.get_or_create(team=instance) + participation = Participation.objects.get_or_create(team=instance)[0] + if not participation.solution: + participation.solution = Video.objects.create() + if not participation.synthesis: + participation.synthesis = Video.objects.create() + participation.save() diff --git a/apps/participation/templates/participation/participation_detail.html b/apps/participation/templates/participation/participation_detail.html new file mode 100644 index 0000000..65ac9b9 --- /dev/null +++ b/apps/participation/templates/participation/participation_detail.html @@ -0,0 +1,61 @@ +{% extends "base.html" %} + +{% load i18n %} + +{% block content %} +{% trans "any" as any %} +
+
+
+
+

{% trans "Participation of team" %} {{ participation.team.name }} ({{ participation.team.trigram }})

+
+
+
+
{% trans "Chosen problem:" %}
+
{{ participation.get_problem_display }}
+
+
+ +
+
+ +
+
+
+

{% trans "Participation of team" %} {{ participation.team.name }} ({{ participation.team.trigram }})

+
+
+ {% trans "No video sent" as novideo %} + {% trans "Video link:" %} {{ participation.solution.link|default:novideo }} + + {% if participation.solution.platform == "youtube" %} + {% include "participation/youtube_iframe.html" with youtube_code=participation.solution.youtube_code %} + {% else %} +
+ {% trans "This video platform is not supported yet." %} +
+ {% endif %} +
+
+
+
+ +{% trans "Upload video" as modal_title %} +{% trans "Upload" as modal_button %} +{% url "participation:upload_video" pk=participation.solution_id as modal_action %} +{% include "base_modal.html" with modal_id="uploadVideo" %} +{% endblock %} + +{% block extrajavascript %} + +{% endblock %} diff --git a/apps/participation/templates/participation/upload_video.html b/apps/participation/templates/participation/upload_video.html new file mode 100644 index 0000000..5271644 --- /dev/null +++ b/apps/participation/templates/participation/upload_video.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% load crispy_forms_filters i18n %} + +{% block content %} +
+
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+{% endblock content %} + diff --git a/apps/participation/templates/participation/youtube_iframe.html b/apps/participation/templates/participation/youtube_iframe.html new file mode 100644 index 0000000..4c577d2 --- /dev/null +++ b/apps/participation/templates/participation/youtube_iframe.html @@ -0,0 +1,8 @@ +
+ +
diff --git a/apps/participation/urls.py b/apps/participation/urls.py index 7334b0f..b5a8c36 100644 --- a/apps/participation/urls.py +++ b/apps/participation/urls.py @@ -1,6 +1,7 @@ from django.urls import path -from .views import CreateTeamView, JoinTeamView, MyTeamDetailView, TeamDetailView, TeamUpdateView +from .views import CreateTeamView, JoinTeamView, MyParticipationDetailView, MyTeamDetailView, ParticipationDetailView,\ + TeamDetailView, TeamUpdateView, UploadVideoView app_name = "participation" @@ -11,4 +12,7 @@ urlpatterns = [ path("team/", MyTeamDetailView.as_view(), name="my_team_detail"), path("team//", TeamDetailView.as_view(), name="team_detail"), path("team//update/", TeamUpdateView.as_view(), name="update_team"), + path("detail/", MyParticipationDetailView.as_view(), name="my_participation_detail"), + path("detail//", ParticipationDetailView.as_view(), name="participation_detail"), + path("detail/upload-video//", UploadVideoView.as_view(), name="upload_video"), ] diff --git a/apps/participation/views.py b/apps/participation/views.py index bebfa9c..a79c8f1 100644 --- a/apps/participation/views.py +++ b/apps/participation/views.py @@ -5,8 +5,8 @@ from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, DetailView, FormView, RedirectView, UpdateView -from .forms import JoinTeamForm, ParticipationForm, TeamForm -from .models import Team +from .forms import JoinTeamForm, ParticipationForm, TeamForm, UploadVideoForm +from .models import Participation, Team, Video class CreateTeamView(LoginRequiredMixin, CreateView): @@ -99,3 +99,27 @@ class TeamUpdateView(LoginRequiredMixin, UpdateView): def get_success_url(self): return reverse_lazy("participation:team_detail", args=(self.object.pk,)) + + +class MyParticipationDetailView(LoginRequiredMixin, RedirectView): + def get_redirect_url(self, *args, **kwargs): + user = self.request.user + registration = user.registration + if registration.participates: + if registration.team: + return reverse_lazy("participation:participation_detail", args=(registration.team.participation.id,)) + raise PermissionDenied(_("You are not in a team.")) + raise PermissionDenied(_("You don't participate, so you don't have any team.")) + + +class ParticipationDetailView(LoginRequiredMixin, DetailView): + model = Participation + + +class UploadVideoView(LoginRequiredMixin, UpdateView): + model = Video + form_class = UploadVideoForm + template_name = "participation/upload_video.html" + + def get_success_url(self): + return reverse_lazy("participation:participation_detail", args=(self.object.participation.pk,)) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 959680f..39c561a 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Corres2math\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-27 12:35+0200\n" +"POT-Creation-Date: 2020-09-27 14:31+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: LANGUAGE \n" @@ -38,7 +38,7 @@ msgstr "Fermer" msgid "Logs" msgstr "Logs" -#: apps/logs/models.py:22 apps/registration/models.py:18 +#: apps/logs/models.py:22 apps/registration/models.py:16 msgid "user" msgstr "utilisateur" @@ -99,7 +99,7 @@ msgstr "changelogs" msgid "Changelog of type \"{action}\" for model {model} at {timestamp}" msgstr "Changelog de type \"{action}\" pour le modèle {model} le {timestamp}" -#: apps/participation/forms.py:14 apps/participation/models.py:19 +#: apps/participation/forms.py:14 apps/participation/models.py:22 msgid "The trigram must be composed of three uppercase letters." msgstr "Le trigramme doit être composé de trois lettres majuscules." @@ -107,27 +107,27 @@ msgstr "Le trigramme doit être composé de trois lettres majuscules." msgid "No team was found with this access code." msgstr "Aucune équipe n'a été trouvée avec ce code d'accès." -#: apps/participation/models.py:12 +#: apps/participation/models.py:15 msgid "name" msgstr "nom" -#: apps/participation/models.py:18 +#: apps/participation/models.py:21 msgid "trigram" msgstr "trigramme" -#: apps/participation/models.py:26 +#: apps/participation/models.py:29 msgid "access code" msgstr "code d'accès" -#: apps/participation/models.py:27 +#: apps/participation/models.py:30 msgid "The access code let other people to join the team." msgstr "Le code d'accès permet aux autres participants de rejoindre l'équipe." -#: apps/participation/models.py:31 +#: apps/participation/models.py:34 msgid "Grant Animath to publish my video" msgstr "Autoriser Animath à publier ma vidéo" -#: apps/participation/models.py:32 +#: apps/participation/models.py:35 msgid "" "Give the authorisation to publish the video on the main website to promote " "the action." @@ -135,98 +135,135 @@ msgstr "" "Donner l'autorisation de publier la vidéo sur le site principal pour " "promouvoir les Correspondances." -#: apps/participation/models.py:42 +#: apps/participation/models.py:45 #, python-brace-format msgid "Team {name} ({trigram})" msgstr "Équipe {name} ({trigram})" -#: apps/participation/models.py:45 apps/participation/models.py:56 -#: apps/registration/models.py:83 apps/registration/models.py:129 +#: apps/participation/models.py:48 apps/participation/models.py:59 +#: apps/registration/models.py:81 apps/registration/models.py:127 msgid "team" msgstr "équipe" -#: apps/participation/models.py:46 +#: apps/participation/models.py:49 msgid "teams" msgstr "équipes" -#: apps/participation/models.py:60 +#: apps/participation/models.py:63 #, python-brace-format msgid "Problem #{problem:d}" msgstr "Problème n°{problem:d}" -#: apps/participation/models.py:63 +#: apps/participation/models.py:66 msgid "problem number" msgstr "numéro de problème" -#: apps/participation/models.py:72 +#: apps/participation/models.py:75 msgid "solution video" msgstr "vidéo de solution" -#: apps/participation/models.py:81 +#: apps/participation/models.py:84 msgid "received participation" msgstr "participation reçue" -#: apps/participation/models.py:90 +#: apps/participation/models.py:93 msgid "synthesis video" msgstr "vidéo de synthèse" -#: apps/participation/models.py:94 +#: apps/participation/models.py:97 #, python-brace-format msgid "Participation of the team {name} ({trigram})" msgstr "Participation de l'équipe {name} ({trigram})" -#: apps/participation/models.py:97 apps/participation/models.py:105 +#: apps/participation/models.py:100 msgid "participation" msgstr "participation" -#: apps/participation/models.py:98 +#: apps/participation/models.py:101 msgid "participations" msgstr "participations" -#: apps/participation/models.py:109 +#: apps/participation/models.py:106 msgid "link" msgstr "lien" -#: apps/participation/models.py:110 +#: apps/participation/models.py:107 msgid "The full video link." msgstr "Le lien complet de la vidéo." -#: apps/participation/models.py:116 +#: apps/participation/models.py:113 msgid "valid" msgstr "valide" -#: apps/participation/models.py:117 +#: apps/participation/models.py:114 msgid "The video got the validation of the administrators." msgstr "La vidéo a été validée par les administrateurs." -#: apps/participation/models.py:121 +#: apps/participation/models.py:136 #, python-brace-format msgid "Video of team {name} ({trigram})" msgstr "Vidéo de l'équipe {name} ({trigram})" -#: apps/participation/models.py:125 +#: apps/participation/models.py:140 msgid "video" msgstr "vidéo" -#: apps/participation/models.py:126 +#: apps/participation/models.py:141 msgid "videos" msgstr "vidéos" #: apps/participation/templates/participation/create_team.html:11 -#: templates/base.html:202 +#: templates/base.html:207 msgid "Create" msgstr "Créer" #: apps/participation/templates/participation/join_team.html:11 -#: templates/base.html:198 +#: templates/base.html:203 msgid "Join" msgstr "Rejoindre" +#: apps/participation/templates/participation/participation_detail.html:6 #: apps/participation/templates/participation/team_detail.html:6 #: apps/registration/templates/registration/user_detail.html:6 msgid "any" msgstr "aucun" +#: apps/participation/templates/participation/participation_detail.html:11 +#: apps/participation/templates/participation/participation_detail.html:27 +#, fuzzy +#| msgid "participation" +msgid "Participation of team" +msgstr "participation" + +#: apps/participation/templates/participation/participation_detail.html:15 +#: apps/participation/templates/participation/team_detail.html:29 +msgid "Chosen problem:" +msgstr "Problème choisi :" + +#: apps/participation/templates/participation/participation_detail.html:30 +msgid "No video sent" +msgstr "Pas de vidéo envoyée" + +#: apps/participation/templates/participation/participation_detail.html:31 +msgid "Video link:" +msgstr "Lien de la vidéo :" + +#: apps/participation/templates/participation/participation_detail.html:32 +#: apps/participation/templates/participation/participation_detail.html:46 +#: apps/participation/templates/participation/upload_video.html:11 +#: apps/registration/templates/registration/upload_photo_authorization.html:18 +#: apps/registration/templates/registration/user_detail.html:60 +msgid "Upload" +msgstr "Téléverser" + +#: apps/participation/templates/participation/participation_detail.html:37 +msgid "This video platform is not supported yet." +msgstr "La plateforme de cette vidéo n'est pas encore supportée." + +#: apps/participation/templates/participation/participation_detail.html:45 +msgid "Upload video" +msgstr "Envoyer la vidéo" + #: apps/participation/templates/participation/team_detail.html:14 msgid "Name:" msgstr "Nom :" @@ -247,10 +284,6 @@ msgstr "Encadrants :" msgid "Participants:" msgstr "Participants :" -#: apps/participation/templates/participation/team_detail.html:29 -msgid "Chosen problem:" -msgstr "Problème choisi :" - #: apps/participation/templates/participation/team_detail.html:32 msgid "Grant Animath to publish our video:" msgstr "Autoriser Animath à publier notre vidéo :" @@ -277,7 +310,7 @@ msgid "Update team" msgstr "Modifier l'équipe" #: apps/participation/views.py:15 templates/base.html:70 -#: templates/base.html:201 +#: templates/base.html:206 msgid "Create team" msgstr "Créer une équipe" @@ -290,15 +323,15 @@ msgid "You are already in a team." msgstr "Vous êtes déjà dans une équipe." #: apps/participation/views.py:41 templates/base.html:75 -#: templates/base.html:197 +#: templates/base.html:202 msgid "Join team" msgstr "Rejoindre une équipe" -#: apps/participation/views.py:72 +#: apps/participation/views.py:72 apps/participation/views.py:111 msgid "You are not in a team." msgstr "Vous n'êtes pas dans une équipe." -#: apps/participation/views.py:73 +#: apps/participation/views.py:73 apps/participation/views.py:112 msgid "You don't participate, so you don't have any team." msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe." @@ -310,7 +343,7 @@ msgstr "rôle" msgid "participant" msgstr "participant" -#: apps/registration/forms.py:16 apps/registration/models.py:138 +#: apps/registration/forms.py:16 apps/registration/models.py:136 msgid "coach" msgstr "encadrant" @@ -318,88 +351,88 @@ msgstr "encadrant" msgid "The uploaded file must be a PDF, PNG of JPEG file." msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG." -#: apps/registration/models.py:23 +#: apps/registration/models.py:21 msgid "Grant Animath to contact me in the future about other actions" msgstr "" "Autoriser Animath à me recontacter à l'avenir à propos d'autres actions" -#: apps/registration/models.py:28 +#: apps/registration/models.py:26 msgid "email confirmed" msgstr "email confirmé" -#: apps/registration/models.py:32 +#: apps/registration/models.py:30 msgid "Activate your Correspondances account" msgstr "Activez votre compte des Correspondances" -#: apps/registration/models.py:68 +#: apps/registration/models.py:66 msgid "registration" msgstr "inscription" -#: apps/registration/models.py:69 +#: apps/registration/models.py:67 msgid "registrations" msgstr "inscriptions" -#: apps/registration/models.py:88 +#: apps/registration/models.py:86 msgid "12th grade" msgstr "Terminale" -#: apps/registration/models.py:89 +#: apps/registration/models.py:87 msgid "11th grade" msgstr "Première" -#: apps/registration/models.py:90 +#: apps/registration/models.py:88 msgid "10th grade or lower" msgstr "Seconde ou inférieur" -#: apps/registration/models.py:92 +#: apps/registration/models.py:90 msgid "student class" msgstr "classe" -#: apps/registration/models.py:97 +#: apps/registration/models.py:95 msgid "school" msgstr "école" -#: apps/registration/models.py:102 +#: apps/registration/models.py:100 msgid "photo authorization" msgstr "autorisation de droit à l'image" -#: apps/registration/models.py:110 +#: apps/registration/models.py:108 msgid "student" msgstr "étudiant" -#: apps/registration/models.py:118 +#: apps/registration/models.py:116 msgid "student registration" msgstr "inscription d'élève" -#: apps/registration/models.py:119 +#: apps/registration/models.py:117 msgid "student registrations" msgstr "inscriptions d'élève" -#: apps/registration/models.py:133 +#: apps/registration/models.py:131 msgid "professional activity" msgstr "activité professionnelle" -#: apps/registration/models.py:146 +#: apps/registration/models.py:144 msgid "coach registration" msgstr "inscription d'encadrant" -#: apps/registration/models.py:147 +#: apps/registration/models.py:145 msgid "coach registrations" msgstr "inscriptions d'encadrants" -#: apps/registration/models.py:152 +#: apps/registration/models.py:150 msgid "role of the administrator" msgstr "rôle de l'administrateur" -#: apps/registration/models.py:157 +#: apps/registration/models.py:155 msgid "admin" msgstr "admin" -#: apps/registration/models.py:165 +#: apps/registration/models.py:163 msgid "admin registration" msgstr "inscription d'administrateur" -#: apps/registration/models.py:166 +#: apps/registration/models.py:164 msgid "admin registrations" msgstr "inscriptions d'administrateur" @@ -495,7 +528,7 @@ msgid "Your password has been set. You may go ahead and log in now." msgstr "Votre mot de passe a été changé. Vous pouvez désormais vous connecter." #: apps/registration/templates/registration/password_reset_complete.html:10 -#: templates/base.html:108 templates/base.html:192 templates/base.html:193 +#: templates/base.html:113 templates/base.html:197 templates/base.html:198 #: templates/registration/login.html:7 templates/registration/login.html:8 #: templates/registration/login.html:25 msgid "Log in" @@ -570,11 +603,6 @@ msgstr "Majeur" msgid "Child" msgstr "Mineur" -#: apps/registration/templates/registration/upload_photo_authorization.html:18 -#: apps/registration/templates/registration/user_detail.html:60 -msgid "Upload" -msgstr "Téléverser" - #: apps/registration/templates/registration/user_detail.html:14 msgid "Last name:" msgstr "Nom de famille :" @@ -635,27 +663,27 @@ msgstr "Modifier l'utilisateur" msgid "Upload photo authorization" msgstr "Téléverser l'autorisation de droit à l'image" -#: apps/registration/views.py:63 +#: apps/registration/views.py:60 msgid "Email validation" msgstr "Validation de l'adresse mail" -#: apps/registration/views.py:65 +#: apps/registration/views.py:62 msgid "Validate email" msgstr "Valider l'adresse mail" -#: apps/registration/views.py:104 +#: apps/registration/views.py:101 msgid "Email validation unsuccessful" msgstr "Échec de la validation de l'adresse mail" -#: apps/registration/views.py:115 +#: apps/registration/views.py:112 msgid "Email validation email sent" msgstr "Mail de confirmation de l'adresse mail envoyé" -#: apps/registration/views.py:123 +#: apps/registration/views.py:120 msgid "Resend email validation link" msgstr "Renvoyé le lien de validation de l'adresse mail" -#: apps/registration/views.py:195 +#: apps/registration/views.py:192 #, python-brace-format msgid "Photo authorization of {student}.{ext}" msgstr "Autorisation de droit à l'image de {student}.{ext}" @@ -729,31 +757,35 @@ msgstr "Accueil" msgid "My team" msgstr "Mon équipe" -#: templates/base.html:88 +#: templates/base.html:86 +msgid "My participation" +msgstr "Ma participation" + +#: templates/base.html:93 msgid "Make a gift" msgstr "Faire un don" -#: templates/base.html:92 +#: templates/base.html:97 msgid "Administration" msgstr "Administration" -#: templates/base.html:99 +#: templates/base.html:104 msgid "Return to admin view" msgstr "Retourner à l'interface administrateur" -#: templates/base.html:104 +#: templates/base.html:109 msgid "Register" msgstr "S'inscrire" -#: templates/base.html:120 +#: templates/base.html:125 msgid "My account" msgstr "Mon compte" -#: templates/base.html:123 +#: templates/base.html:128 msgid "Log out" msgstr "Déconnexion" -#: templates/base.html:139 +#: templates/base.html:144 #, python-format msgid "" "Your email address is not validated. Please click on the link you received " @@ -764,7 +796,7 @@ msgstr "" "avez reçu par mail. Vous pouvez renvoyer un mail en cliquant sur ce lien." -#: templates/base.html:162 +#: templates/base.html:167 msgid "Contact us" msgstr "Nous contacter" diff --git a/templates/base.html b/templates/base.html index a0d8e3b..b455b36 100644 --- a/templates/base.html +++ b/templates/base.html @@ -81,6 +81,11 @@ {% trans "My team" %} + {% endif %} {% endif %}