💚 Install libmagic in CI

This commit is contained in:
Yohann D'ANELLO 2020-09-27 14:32:05 +02:00
parent 972902eb23
commit 2d62bec690
11 changed files with 271 additions and 97 deletions

View File

@ -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',)

View File

@ -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)

View File

@ -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()

View File

@ -0,0 +1,61 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
{% trans "any" as any %}
<div class="row mt-4">
<div class="col-xl-4">
<div class="card bg-light shadow">
<div class="card-header text-center">
<h4>{% trans "Participation of team" %} {{ participation.team.name }} ({{ participation.team.trigram }})</h4>
</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-6 text-right">{% trans "Chosen problem:" %}</dt>
<dd class="col-sm-6">{{ participation.get_problem_display }}</dd>
</dl>
</div>
<div class="card-footer text-center">
</div>
</div>
</div>
<div class="col-xl-8">
<div class="card bg-light shadow">
<div class="card-header text-center">
<h4>{% trans "Participation of team" %} {{ participation.team.name }} ({{ participation.team.trigram }})</h4>
</div>
<div class="card-body">
{% trans "No video sent" as novideo %}
{% trans "Video link:" %} <a href="{{ participation.solution.link|default:"#" }}" target="_blank">{{ participation.solution.link|default:novideo }}</a>
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadVideoModal">{% trans "Upload" %}</button>
{% if participation.solution.platform == "youtube" %}
{% include "participation/youtube_iframe.html" with youtube_code=participation.solution.youtube_code %}
{% else %}
<div class="alert alert-danger">
{% trans "This video platform is not supported yet." %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% 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 %}
<script>
$(document).ready(function() {
$('button[data-target="#uploadVideoModal"]').click(function() {
let modalBody = $("#uploadVideoModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "participation:upload_video" pk=participation.solution_id %} #form-content");
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% load crispy_forms_filters i18n %}
{% block content %}
<form method="post">
<div id="form-content">
{% csrf_token %}
{{ form|crispy }}
</div>
<button class="btn btn-success" type="submit">{% trans "Upload" %}</button>
</form>
{% endblock content %}

View File

@ -0,0 +1,8 @@
<div style="position: relative; width: 100%; padding-bottom: 56.25%;">
<iframe src="https://www.youtube.com/embed/{{ youtube_code }}"
frameborder="0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
</div>

View File

@ -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/<int:pk>/", TeamDetailView.as_view(), name="team_detail"),
path("team/<int:pk>/update/", TeamUpdateView.as_view(), name="update_team"),
path("detail/", MyParticipationDetailView.as_view(), name="my_participation_detail"),
path("detail/<int:pk>/", ParticipationDetailView.as_view(), name="participation_detail"),
path("detail/upload-video/<int:pk>/", UploadVideoView.as_view(), name="upload_video"),
]

View File

@ -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,))

View File

@ -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 <yohann.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 <a href="
"\"%(send_email_url)s\">ce lien</a>."
#: templates/base.html:162
#: templates/base.html:167
msgid "Contact us"
msgstr "Nous contacter"

View File

@ -81,6 +81,11 @@
<i class="fas fa-users"></i> {% trans "My team" %}
</a>
</li>
<li class="nav-item active">
<a href="{% url "participation:my_participation_detail" %}" class="nav-link">
<i class="fas fa-video"></i> {% trans "My participation" %}
</a>
</li>
{% endif %}
{% endif %}
<li class="nav-item active">

View File

@ -10,7 +10,7 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body"></div>
<div class="modal-body">{{ modal_content }}</div>
<div class="modal-footer">
<button type="submit" class="btn btn-{{ modal_button_type|default:"primary" }}">{{ modal_button }}</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">{% trans "Close" %}</button>