From 7ef602c6cd2816a3bba84c05be8605581612f199 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 28 Dec 2020 18:52:50 +0100 Subject: [PATCH] Drop a lot of Corres2math content --- .gitignore | 2 + apps/participation/admin.py | 44 +-- apps/participation/apps.py | 3 +- apps/participation/forms.py | 115 +------ .../management/commands/setup_third_phase.py | 44 --- apps/participation/migrations/0001_initial.py | 138 -------- apps/participation/models.py | 39 +-- apps/participation/search_indexes.py | 12 +- apps/participation/signals.py | 12 +- apps/participation/tables.py | 20 +- .../participation/participation_detail.html | 282 ---------------- .../templates/participation/team_detail.html | 3 - apps/participation/templatetags/__init__.py | 2 - apps/participation/templatetags/calendar.py | 15 - apps/participation/tests.py | 308 +----------------- apps/participation/urls.py | 17 +- apps/participation/views.py | 157 +-------- apps/registration/migrations/0001_initial.py | 74 ----- apps/registration/models.py | 4 +- .../templatetags/search_results_tables.py | 4 +- apps/registration/tests.py | 17 +- apps/registration/views.py | 10 - ...{Fiche synthèse.pdf => Fiche_synthèse.pdf} | Bin ...{Fiche synthèse.tex => Fiche_synthèse.tex} | 0 tfjm/templates/base.html | 25 +- tfjm/views.py | 2 - tfjm/whoosh_index/MAIN_3c7ed6y4sljdfo5s.seg | Bin 201663 -> 0 bytes tfjm/whoosh_index/_MAIN_11.toc | Bin 3787 -> 0 bytes 28 files changed, 63 insertions(+), 1286 deletions(-) delete mode 100644 apps/participation/management/commands/setup_third_phase.py delete mode 100644 apps/participation/migrations/0001_initial.py delete mode 100644 apps/participation/templatetags/__init__.py delete mode 100644 apps/participation/templatetags/calendar.py delete mode 100644 apps/registration/migrations/0001_initial.py rename tfjm/static/{Fiche synthèse.pdf => Fiche_synthèse.pdf} (100%) rename tfjm/static/{Fiche synthèse.tex => Fiche_synthèse.tex} (100%) delete mode 100644 tfjm/whoosh_index/MAIN_3c7ed6y4sljdfo5s.seg delete mode 100644 tfjm/whoosh_index/_MAIN_11.toc diff --git a/.gitignore b/.gitignore index 1299f3e..61d9bad 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ db.sqlite3 # Don't git index whoosh_index/ + +migrations/ diff --git a/apps/participation/admin.py b/apps/participation/admin.py index 25bbdcc..acef641 100644 --- a/apps/participation/admin.py +++ b/apps/participation/admin.py @@ -4,19 +4,14 @@ from django.contrib import admin from django.utils.translation import gettext_lazy as _ -from .models import Participation, Phase, Question, Team, Video +from .models import Participation, Pool, Solution, Synthesis, Team, Tournament @admin.register(Team) class TeamAdmin(admin.ModelAdmin): - list_display = ('name', 'trigram', 'problem', 'valid',) + list_display = ('name', 'trigram', 'valid',) search_fields = ('name', 'trigram',) - list_filter = ('participation__problem', 'participation__valid',) - - def problem(self, team): - return team.participation.get_problem_display() - - problem.short_description = _('problem number') + list_filter = ('participation__valid',) def valid(self, team): return team.participation.valid @@ -26,24 +21,29 @@ class TeamAdmin(admin.ModelAdmin): @admin.register(Participation) class ParticipationAdmin(admin.ModelAdmin): - list_display = ('team', 'problem', 'valid',) + list_display = ('team', 'valid',) search_fields = ('team__name', 'team__trigram',) - list_filter = ('problem', 'valid',) + list_filter = ('valid',) -@admin.register(Video) -class VideoAdmin(admin.ModelAdmin): - list_display = ('participation', 'link',) - search_fields = ('participation__team__name', 'participation__team__trigram', 'link',) +@admin.register(Pool) +class PoolAdmin(admin.ModelAdmin): + search_fields = ('participations__team__name', 'participations__team__trigram',) -@admin.register(Question) -class QuestionAdmin(admin.ModelAdmin): - list_display = ('participation', 'question',) - search_fields = ('participation__team__name', 'participation__team__trigram', 'question',) +@admin.register(Solution) +class SolutionAdmin(admin.ModelAdmin): + list_display = ('participation',) + search_fields = ('participation__team__name', 'participation__team__trigram',) -@admin.register(Phase) -class PhaseAdmin(admin.ModelAdmin): - list_display = ('phase_number', 'start', 'end',) - ordering = ('phase_number', 'start',) +@admin.register(Synthesis) +class SynthesisAdmin(admin.ModelAdmin): + list_display = ('participation',) + search_fields = ('participation__team__name', 'participation__team__trigram',) + + +@admin.register(Tournament) +class TournamentAdmin(admin.ModelAdmin): + list_display = ('name',) + search_fields = ('name',) diff --git a/apps/participation/apps.py b/apps/participation/apps.py index e76d270..bb84d56 100644 --- a/apps/participation/apps.py +++ b/apps/participation/apps.py @@ -12,7 +12,6 @@ class ParticipationConfig(AppConfig): name = 'participation' def ready(self): - from participation.signals import create_team_participation, delete_related_videos, update_mailing_list + from participation.signals import create_team_participation, update_mailing_list pre_save.connect(update_mailing_list, "participation.Team") - pre_delete.connect(delete_related_videos, "participation.Participation") post_save.connect(create_team_participation, "participation.Team") diff --git a/apps/participation/forms.py b/apps/participation/forms.py index 6ec054f..7d17e5f 100644 --- a/apps/participation/forms.py +++ b/apps/participation/forms.py @@ -3,13 +3,11 @@ import re -from bootstrap_datepicker_plus import DateTimePickerInput from django import forms -from django.core.exceptions import ObjectDoesNotExist, ValidationError -from django.db.models import Q +from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ -from .models import Participation, Phase, Question, Team, Video +from .models import Participation, Team class TeamForm(forms.ModelForm): @@ -25,7 +23,7 @@ class TeamForm(forms.ModelForm): class Meta: model = Team - fields = ('name', 'trigram', 'grant_animath_access_videos',) + fields = ('name', 'trigram',) class JoinTeamForm(forms.ModelForm): @@ -56,7 +54,7 @@ class ParticipationForm(forms.ModelForm): """ class Meta: model = Participation - fields = ('problem',) + fields = ('tournament',) class RequestValidationForm(forms.Form): @@ -87,108 +85,3 @@ class ValidateParticipationForm(forms.Form): label=_("Message to address to the team:"), widget=forms.Textarea(), ) - - -class UploadVideoForm(forms.ModelForm): - """ - Form to upload a video, for a solution or a synthesis. - """ - class Meta: - model = Video - fields = ('link',) - - def clean(self): - if Phase.current_phase().phase_number != 1 and Phase.current_phase().phase_number != 4 and self.instance.link: - self.add_error("link", _("You can't upload your video after the deadline.")) - return super().clean() - - -class ReceiveParticipationForm(forms.ModelForm): - """ - Update the received participation of a participation. - """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields["received_participation"].queryset = Participation.objects.filter( - ~Q(pk=self.instance.pk) & Q(problem=self.instance.problem, valid=True) - ) - - class Meta: - model = Participation - fields = ('received_participation',) - - -class SendParticipationForm(forms.ModelForm): - """ - Update the sent participation of a participation. - """ - sent_participation = forms.ModelChoiceField( - queryset=Participation.objects, - label=lambda: _("Send to team"), - ) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - try: - self.fields["sent_participation"].initial = self.instance.sent_participation - except ObjectDoesNotExist: # No sent participation - pass - self.fields["sent_participation"].queryset = Participation.objects.filter( - ~Q(pk=self.instance.pk) & Q(problem=self.instance.problem, valid=True) - ) - - def clean(self, commit=True): - cleaned_data = super().clean() - if "sent_participation" in cleaned_data: - participation = cleaned_data["sent_participation"] - participation.received_participation = self.instance - self.instance = participation - return cleaned_data - - class Meta: - model = Participation - fields = ('sent_participation',) - - -class QuestionForm(forms.ModelForm): - """ - Create or update a question. - """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields["question"].widget.attrs.update({"placeholder": _("How did you get the idea to ...?")}) - - def clean(self): - if Phase.current_phase().phase_number != 2: - self.add_error(None, _("You can only create or update a question during the second phase.")) - return super().clean() - - class Meta: - model = Question - fields = ('question',) - - -class PhaseForm(forms.ModelForm): - """ - Form to update the calendar of a phase. - """ - class Meta: - model = Phase - fields = ('start', 'end',) - widgets = { - 'start': DateTimePickerInput(format='%d/%m/%Y %H:%M'), - 'end': DateTimePickerInput(format='%d/%m/%Y %H:%M'), - } - - def clean(self): - # Ensure that dates are in a right order - cleaned_data = super().clean() - start = cleaned_data["start"] - end = cleaned_data["end"] - if end <= start: - self.add_error("end", _("Start date must be before the end date.")) - if Phase.objects.filter(phase_number__lt=self.instance.phase_number, end__gt=start).exists(): - self.add_error("start", _("This phase must start after the previous phases.")) - if Phase.objects.filter(phase_number__gt=self.instance.phase_number, start__lt=end).exists(): - self.add_error("end", _("This phase must end after the next phases.")) - return cleaned_data diff --git a/apps/participation/management/commands/setup_third_phase.py b/apps/participation/management/commands/setup_third_phase.py deleted file mode 100644 index c0e1f8b..0000000 --- a/apps/participation/management/commands/setup_third_phase.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (C) 2020 by Animath -# SPDX-License-Identifier: GPL-3.0-or-later - -from tfjm.matrix import Matrix, RoomVisibility -from django.core.management import BaseCommand -from participation.models import Participation - - -class Command(BaseCommand): - def handle(self, *args, **options): - for participation in Participation.objects.filter(valid=True).all(): - for i, question in enumerate(participation.questions.order_by("id").all()): - solution_author = participation.received_participation.team - alias = f"equipe-{solution_author.trigram.lower()}-question-{i}" - room_id = f"#{alias}:tfjm.org" - Matrix.create_room( - visibility=RoomVisibility.public, - alias=alias, - name=f"Solution équipe {solution_author.trigram} - question {i+1}", - topic=f"Échange entre l'équipe {solution_author.name} ({solution_author.trigram}) " - f"et l'équipe {participation.team.name} ({participation.team.trigram}) " - f"autour de la question {i+1} sur le problème {participation.problem}", - federate=False, - invite=[f"@{registration.matrix_username}:tfjm.org" for registration in - list(participation.team.students.all()) + list(participation.team.coachs.all()) + - list(solution_author.students.all()) + list(solution_author.coachs.all())], - ) - Matrix.set_room_power_level_event(room_id, "events_default", 21) - for registration in solution_author.students.all(): - Matrix.set_room_power_level(room_id, - f"@{registration.matrix_username}:tfjm.org", 42) - - Matrix.send_message(room_id, "Bienvenue dans la troisième phase du TFJM² !") - Matrix.send_message(room_id, f"L'équipe {participation.team.name} a visionné la vidéo de l'équipe " - f"{solution_author.name} sur le problème {participation.problem}, et a posé " - "une série de questions.") - Matrix.send_message(room_id, "L'équipe ayant composé la vidéo doit maintenant proposer une réponse.") - Matrix.send_message(room_id, "Une fois la réponse apportée, vous pourrez ensuite échanger plus " - "librement autour de la question, au travers de ce canal.") - Matrix.send_message(room_id, "**Question posée :**", formatted_body="Question posée :") - Matrix.send_message(room_id, question.question, - formatted_body=f"{question.question}") - - # TODO Setup the bot the set the power level of all members of the room to 42 diff --git a/apps/participation/migrations/0001_initial.py b/apps/participation/migrations/0001_initial.py deleted file mode 100644 index 903bf86..0000000 --- a/apps/participation/migrations/0001_initial.py +++ /dev/null @@ -1,138 +0,0 @@ -# Generated by Django 3.1.3 on 2020-11-04 12:05 - -import django.core.validators -from django.db import migrations, models -import django.utils.timezone - - -def register_phases(apps, _): - """ - Import the different phases of the action - """ - Phase = apps.get_model("participation", "phase") - Phase.objects.get_or_create( - phase_number=1, - description="Soumission des vidéos", - ) - Phase.objects.get_or_create( - phase_number=2, - description="Phase de questions", - ) - Phase.objects.get_or_create( - phase_number=3, - description="Phase d'échanges entre les équipes", - ) - Phase.objects.get_or_create( - phase_number=4, - description="Synthèse de l'échange", - ) - - -def reverse_phase_registering(apps, _): # pragma: no cover - """ - Drop all phases in order to unapply this migration. - """ - Phase = apps.get_model("participation", "phase") - Phase.objects.all().delete() - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Participation', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('problem', models.IntegerField(choices=[(1, 'Problem #1'), (2, 'Problem #2'), (3, 'Problem #3')], default=None, null=True, verbose_name='problem number')), - ('valid', models.BooleanField(default=None, help_text='The video got the validation of the administrators.', null=True, verbose_name='valid')), - ], - options={ - 'verbose_name': 'participation', - 'verbose_name_plural': 'participations', - }, - ), - migrations.CreateModel( - name='Phase', - fields=[ - ('phase_number', models.AutoField(primary_key=True, serialize=False, unique=True, verbose_name='phase number')), - ('description', models.CharField(max_length=255, verbose_name='phase description')), - ('start', models.DateTimeField(default=django.utils.timezone.now, verbose_name='start date of the given phase')), - ('end', models.DateTimeField(default=django.utils.timezone.now, verbose_name='end date of the given phase')), - ], - options={ - 'verbose_name': 'phase', - 'verbose_name_plural': 'phases', - }, - ), - migrations.CreateModel( - name='Question', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('question', models.TextField(verbose_name='question')), - ], - ), - migrations.CreateModel( - name='Team', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, unique=True, verbose_name='name')), - ('trigram', models.CharField(help_text='The trigram must be composed of three uppercase letters.', max_length=3, unique=True, validators=[django.core.validators.RegexValidator('[A-Z]{3}')], verbose_name='trigram')), - ('access_code', models.CharField(help_text='The access code let other people to join the team.', max_length=6, verbose_name='access code')), - ('grant_animath_access_videos', models.BooleanField(default=False, help_text='Give the authorisation to publish the video on the main website to promote the action.', verbose_name='Grant Animath to publish my video')), - ], - options={ - 'verbose_name': 'team', - 'verbose_name_plural': 'teams', - }, - ), - migrations.CreateModel( - name='Video', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('link', models.URLField(help_text='The full video link.', verbose_name='link')), - ('valid', models.BooleanField(default=None, help_text='The video got the validation of the administrators.', null=True, verbose_name='valid')), - ], - options={ - 'verbose_name': 'video', - 'verbose_name_plural': 'videos', - }, - ), - migrations.AddIndex( - model_name='team', - index=models.Index(fields=['trigram'], name='participati_trigram_239255_idx'), - ), - migrations.AddField( - model_name='question', - name='participation', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='participation.participation', verbose_name='participation'), - ), - migrations.AddField( - model_name='participation', - name='received_participation', - field=models.OneToOneField(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sent_participation', to='participation.participation', verbose_name='received participation'), - ), - migrations.AddField( - model_name='participation', - name='solution', - field=models.OneToOneField(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='participation_solution', to='participation.video', verbose_name='solution video'), - ), - migrations.AddField( - model_name='participation', - name='synthesis', - field=models.OneToOneField(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='participation_synthesis', to='participation.video', verbose_name='synthesis video'), - ), - migrations.AddField( - model_name='participation', - name='team', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='participation.team', verbose_name='team'), - ), - migrations.RunPython( - register_phases, - reverse_code=reverse_phase_registering, - ) - ] diff --git a/apps/participation/models.py b/apps/participation/models.py index 9d1583b..f2b7123 100644 --- a/apps/participation/models.py +++ b/apps/participation/models.py @@ -44,6 +44,14 @@ class Team(models.Model): help_text=_("The access code let other people to join the team."), ) + @property + def students(self): + return self.participants.filter(studentregistration__isnull=False) + + @property + def coachs(self): + return self.participants.filter(coachregistration__isnull=False) + @property def email(self): """ @@ -224,33 +232,6 @@ class Participation(models.Model): 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,)) @@ -324,7 +305,7 @@ class Solution(models.Model): class Meta: verbose_name = _("solution") verbose_name_plural = _("solutions") - unique_by = (('participation', 'problem', 'final_solution', ), ) + unique_together = (('participation', 'problem', 'final_solution', ), ) class Synthesis(models.Model): @@ -359,4 +340,4 @@ class Synthesis(models.Model): class Meta: verbose_name = _("synthesis") verbose_name_plural = _("syntheses") - unique_by = (('participation', 'pool', 'type', ), ) + unique_together = (('participation', 'pool', 'type', ), ) diff --git a/apps/participation/search_indexes.py b/apps/participation/search_indexes.py index 22e722a..1512a71 100644 --- a/apps/participation/search_indexes.py +++ b/apps/participation/search_indexes.py @@ -3,7 +3,7 @@ from haystack import indexes -from .models import Participation, Team, Video +from .models import Participation, Team class TeamIndex(indexes.ModelSearchIndex, indexes.Indexable): @@ -24,13 +24,3 @@ class ParticipationIndex(indexes.ModelSearchIndex, indexes.Indexable): class Meta: model = Participation - - -class VideoIndex(indexes.ModelSearchIndex, indexes.Indexable): - """ - Index all teams by their team name and team trigram. - """ - text = indexes.NgramField(document=True, use_template=True) - - class Meta: - model = Video diff --git a/apps/participation/signals.py b/apps/participation/signals.py index 45dc5de..14f3830 100644 --- a/apps/participation/signals.py +++ b/apps/participation/signals.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from tfjm.lists import get_sympa_client -from participation.models import Participation, Team, Video +from participation.models import Participation, Team def create_team_participation(instance, created, **_): @@ -10,10 +10,6 @@ def create_team_participation(instance, created, **_): When a team got created, create an associated team and create Video objects. """ 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() if not created: participation.team.create_mailing_list() @@ -38,9 +34,3 @@ def update_mailing_list(instance: Team, **_): get_sympa_client().subscribe(coach.user.email, f"equipe-{instance.trigram.lower()}", False, f"{coach.user.first_name} {coach.user.last_name}") - -def delete_related_videos(instance: Participation, **_): - if instance.solution: - instance.solution.delete() - if instance.synthesis: - instance.synthesis.delete() diff --git a/apps/participation/tables.py b/apps/participation/tables.py index cfd121f..d38cd90 100644 --- a/apps/participation/tables.py +++ b/apps/participation/tables.py @@ -1,28 +1,10 @@ # Copyright (C) 2020 by Animath # SPDX-License-Identifier: GPL-3.0-or-later -from django.utils import timezone from django.utils.translation import gettext_lazy as _ import django_tables2 as tables -from .models import Phase, Team - - -class CalendarTable(tables.Table): - class Meta: - attrs = { - 'class': 'table table condensed table-striped', - } - row_attrs = { - 'class': lambda record: 'bg-success' if timezone.now() > record.end else - 'bg-warning' if timezone.now() > record.start else - 'bg-danger', - 'data-id': lambda record: str(record.phase_number), - } - model = Phase - fields = ('phase_number', 'description', 'start', 'end',) - template_name = 'django_tables2/bootstrap4.html' - order_by = ('phase_number',) +from .models import Team # noinspection PyTypeChecker diff --git a/apps/participation/templates/participation/participation_detail.html b/apps/participation/templates/participation/participation_detail.html index ae0a2a2..d4393eb 100644 --- a/apps/participation/templates/participation/participation_detail.html +++ b/apps/participation/templates/participation/participation_detail.html @@ -12,289 +12,7 @@
{% trans "Team:" %}
{{ participation.team }}
- -
{% trans "Chosen problem:" %}
-
{{ participation.get_problem_display }}
- -
-
- {% trans "No video sent" as novideo %} -
{% trans "Proposed solution:" %}
-
- {{ participation.solution.link|default:novideo }} - {% if current_phase.phase_number == 1 or participation.solution.link == "" %} - - {% endif %} - {% if participation.solution.link %} - - {% endif %} -
-
-
- - {% if user.registration.is_admin or current_phase.phase_number >= 2 %} -
- -
-
-
-
-

{% trans "Sent solution" %}

-
-
-
-
{% trans "Team that received your solution:" %}
-
{{ participation.sent_participation.team|default:any }}
- {% if user.registration.is_admin %} -
- -
- {% endif %} -
- - {% if current_phase.phase_number == 2 %} -
- {% blocktrans trimmed %} - The mentioned team received your video. They are now watching your video, - and formulating questions. You would be able to exchange with the other phase during - the next phase. - {% endblocktrans %} -
- {% elif current_phase.phase_number == 3 %} -
- {% blocktrans trimmed with user_id=user.pk %} - The other team sent you questions about your solution. Your are now able to answer them, - then to exchange freely with the other team. You can click on the Chat button, or to - connect to your dedicated Matrix account: - @tfjm_{{ user_id }}:tfjm.org. - You can use your own Matrix client, or use the dedicated Element client: - element.correpondances-maths.fr - {% endblocktrans %} -
- {% elif current_phase.phase_number == 4 %} -
-
{% trans "Synthesis from the other team:" %}
-
- {{ participation.received_participation.synthesis.link|default:novideo }} - {% if participation.received_participation.synthesis.link %} - - {% endif %} -
-
- {% endif %} -
-
-
-
-
-
-

{% trans "Received solution" %}

-
-
-
-
{% trans "Team that sent you their solution:" %}
-
{{ participation.received_participation.team|default:any }}
- {% if user.registration.is_admin %} -
- -
- {% endif %} - -
{% trans "Proposed solution:" %}
-
- {{ participation.received_participation.solution.link|default:novideo }} - {% if participation.received_participation.solution.link %} - - {% endif %} -
- - {% if current_phase.phase_number == 2 %} -
- {% blocktrans trimmed %} - You received a solution about the same problem that you treated from another team. - You are now encouraged to see the video, then to ask from 3 to 6 questions about the video. - After that, you will be invited to exchange with the other team about the solution. - {% endblocktrans %} -
- - {% for question in participation.questions.all %} -
{{ question.question }}
-
- -
-
- {% endfor %} - - {% if user.registration.participates %} - - {% endif %} - {% elif current_phase.phase_number == 3 %} -
- {% blocktrans trimmed with user_id=user.pk %} - You sent your questions to the other team about their solution. When they answer to - your questions, you will be able to exchange freely with the other team. - You can click on the Chat button, or to connect to your dedicated Matrix account: - @tfjm_{{ user_id }}:tfjm.org. - You can use your own Matrix client, or use the dedicated Element client: - element.correpondances-maths.fr - {% endblocktrans %} -
- {% elif current_phase.phase_number == 4 %} -
-
- {% trans "No video sent" as novideo %} -
{% trans "Your synthesis of the exchange:" %}
-
- {{ participation.synthesis.link|default:novideo }} - - {% if participation.synthesis.link %} - - {% endif %} -
-
-
- {% endif %} -
-
-
-
-
- {% endif %} - -{% if user.registration.is_admin %} - {% trans "Define received video" as modal_title %} - {% trans "Update" as modal_button %} - {% url "participation:participation_receive_participation" pk=participation.pk as modal_action %} - {% include "base_modal.html" with modal_id="defineReceivedParticipation" %} - - {% trans "Define team that receives your video" as modal_title %} - {% trans "Update" as modal_button %} - {% url "participation:participation_send_participation" pk=participation.pk as modal_action %} - {% include "base_modal.html" with modal_id="defineSentParticipation" %} -{% 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="uploadSolution" %} - -{% trans "Display solution" as modal_title %} -{% trans "This video platform is not supported yet." as unsupported_platform %} -{% include "base_modal.html" with modal_id="displaySolution" modal_action="" modal_button="" modal_additional_class="modal-lg" modal_content=participation.solution.as_iframe|default:unsupported_platform %} - - -{% if user.registration.is_admin or current_phase.phase_number >= 2 %} - {% if participation.received_participation.solution.link %} - {% trans "Display solution" as modal_title %} - {% trans "This video platform is not supported yet." as unsupported_platform %} - {% include "base_modal.html" with modal_id="displayOtherSolution" modal_action="" modal_button="" modal_additional_class="modal-lg" modal_content=participation.received_participation.solution.as_iframe|default:unsupported_platform %} - {% endif %} -{% endif %} - -{% if user.registration.participates and current_phase.phase_number == 2 %} - {% trans "Add question" as modal_title %} - {% trans "Add" as modal_button %} - {% url "participation:add_question" pk=participation.pk as modal_action %} - {% include "base_modal.html" with modal_id="addQuestion" modal_button_type="success" %} - {% for question in participation.questions.all %} - {% with number_str=forloop.counter|stringformat:"d"%} - {% with modal_id="updateQuestion"|add:number_str %} - {% trans "Delete" as delete %} - {% with extra_modal_button='"|safe %} - {% trans "Update question" as modal_title %} - {% trans "Update" as modal_button %} - {% url "participation:update_question" pk=question.pk as modal_action %} - {% include "base_modal.html" %} - {% endwith %} - {% endwith %} - - {% with modal_id="deleteQuestion"|add:number_str %} - {% trans "Delete question" as modal_title %} - {% trans "Delete" as modal_button %} - {% url "participation:delete_question" pk=question.pk as modal_action %} - {% include "base_modal.html" with modal_button_type="danger" %} - {% endwith %} - {% endwith %} - {% endfor %} -{% endif %} - -{% if current_phase.phase_number >= 4 %} - {% if participation.received_participation.synthesis.link %} - {% trans "Display synthesis" as modal_title %} - {% trans "This video platform is not supported yet." as unsupported_platform %} - {% include "base_modal.html" with modal_id="displayOtherSynthesis" modal_action="" modal_button="" modal_additional_class="modal-lg" modal_content=participation.received_participation.synthesis.as_iframe|default:unsupported_platform %} - {% endif %} - - {% trans "Upload video" as modal_title %} - {% trans "Upload" as modal_button %} - {% url "participation:upload_video" pk=participation.synthesis_id as modal_action %} - {% include "base_modal.html" with modal_id="uploadSynthesis" %} - - {% if participation.synthesis.link %} - {% trans "Display synthesis" as modal_title %} - {% trans "This video platform is not supported yet." as unsupported_platform %} - {% include "base_modal.html" with modal_id="displaySynthesis" modal_action="" modal_button="" modal_additional_class="modal-lg" modal_content=participation.synthesis.as_iframe|default:unsupported_platform %} - {% endif %} -{% endif %} -{% endblock %} - -{% block extrajavascript %} - {% endblock %} diff --git a/apps/participation/templates/participation/team_detail.html b/apps/participation/templates/participation/team_detail.html index f6fcf21..c4a8ffe 100644 --- a/apps/participation/templates/participation/team_detail.html +++ b/apps/participation/templates/participation/team_detail.html @@ -45,9 +45,6 @@ {% trans "any" as any %}
{{ team.participation.get_problem_display|default:any }}
-
{% trans "Grant Animath to publish our video:" %}
-
{{ team.grant_animath_access_videos|yesno }}
-
{% trans "Authorizations:" %}
{% for student in team.students.all %} diff --git a/apps/participation/templatetags/__init__.py b/apps/participation/templatetags/__init__.py deleted file mode 100644 index dfc9706..0000000 --- a/apps/participation/templatetags/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (C) 2020 by Animath -# SPDX-License-Identifier: GPL-3.0-or-later diff --git a/apps/participation/templatetags/calendar.py b/apps/participation/templatetags/calendar.py deleted file mode 100644 index 0a8477b..0000000 --- a/apps/participation/templatetags/calendar.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2020 by Animath -# SPDX-License-Identifier: GPL-3.0-or-later - -from django import template - -from ..models import Phase - - -def current_phase(nb): - phase = Phase.current_phase() - return phase is not None and phase.phase_number == nb - - -register = template.Library() -register.filter("current_phase", current_phase) diff --git a/apps/participation/tests.py b/apps/participation/tests.py index 8c30e45..c62b90a 100644 --- a/apps/participation/tests.py +++ b/apps/participation/tests.py @@ -1,18 +1,15 @@ # Copyright (C) 2020 by Animath # SPDX-License-Identifier: GPL-3.0-or-later -from datetime import timedelta - from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.sites.models import Site from django.core.management import call_command from django.test import TestCase from django.urls import reverse -from django.utils import timezone from registration.models import CoachRegistration, StudentRegistration -from .models import Participation, Phase, Question, Team +from .models import Participation, Team class TestStudentParticipation(TestCase): @@ -40,10 +37,7 @@ class TestStudentParticipation(TestCase): name="Super team", trigram="AAA", access_code="azerty", - grant_animath_access_videos=True, ) - self.question = Question.objects.create(participation=self.team.participation, - question="Pourquoi l'existence précède l'essence ?") self.client.force_login(self.user) self.second_user = User.objects.create( @@ -63,7 +57,6 @@ class TestStudentParticipation(TestCase): name="Poor team", trigram="FFF", access_code="qwerty", - grant_animath_access_videos=True, ) self.coach = User.objects.create( @@ -108,29 +101,6 @@ class TestStudentParticipation(TestCase): self.assertRedirects(response, "http://" + Site.objects.get().domain + str(self.team.participation.get_absolute_url()), 302, 200) - # Test video pages - response = self.client.get(reverse("admin:index") + "participation/video/") - self.assertEqual(response.status_code, 200) - - response = self.client.get(reverse("admin:index") - + f"participation/video/{self.team.participation.solution.pk}/change/") - self.assertEqual(response.status_code, 200) - - # Test question pages - response = self.client.get(reverse("admin:index") + "participation/question/") - self.assertEqual(response.status_code, 200) - - response = self.client.get(reverse("admin:index") - + f"participation/question/{self.question.pk}/change/") - self.assertEqual(response.status_code, 200) - - # Test phase pages - response = self.client.get(reverse("admin:index") + "participation/phase/") - self.assertEqual(response.status_code, 200) - - response = self.client.get(reverse("admin:index") + "participation/phase/1/change/") - self.assertEqual(response.status_code, 200) - def test_create_team(self): """ Try to create a team. @@ -141,14 +111,12 @@ class TestStudentParticipation(TestCase): response = self.client.post(reverse("participation:create_team"), data=dict( name="Test team", trigram="123", - grant_animath_access_videos=False, )) self.assertEqual(response.status_code, 200) response = self.client.post(reverse("participation:create_team"), data=dict( name="Test team", trigram="TES", - grant_animath_access_videos=False, )) self.assertTrue(Team.objects.filter(trigram="TES").exists()) team = Team.objects.get(trigram="TES") @@ -158,7 +126,6 @@ class TestStudentParticipation(TestCase): response = self.client.post(reverse("participation:create_team"), data=dict( name="Test team 2", trigram="TET", - grant_animath_access_videos=False, )) self.assertEqual(response.status_code, 403) @@ -286,13 +253,6 @@ class TestStudentParticipation(TestCase): self.user.registration.photo_authorization = "authorization/photo/ananas" self.user.registration.save() - resp = self.client.get(reverse("participation:team_detail", args=(self.team.pk,))) - self.assertEqual(resp.status_code, 200) - self.assertFalse(resp.context["can_validate"]) - - self.team.participation.problem = 2 - self.team.participation.save() - resp = self.client.get(reverse("participation:team_detail", args=(self.team.pk,))) self.assertEqual(resp.status_code, 200) self.assertTrue(resp.context["can_validate"]) @@ -383,23 +343,12 @@ class TestStudentParticipation(TestCase): response = self.client.get(reverse("participation:update_team", args=(self.team.pk,))) self.assertEqual(response.status_code, 200) - # Form is invalid response = self.client.post(reverse("participation:update_team", args=(self.team.pk,)), data=dict( name="Updated team name", trigram="BBB", - grant_animath_access_videos=True, - problem=42, - )) - self.assertEqual(response.status_code, 200) - - response = self.client.post(reverse("participation:update_team", args=(self.team.pk,)), data=dict( - name="Updated team name", - trigram="BBB", - grant_animath_access_videos=True, - problem=3, )) self.assertRedirects(response, reverse("participation:team_detail", args=(self.team.pk,)), 302, 200) - self.assertTrue(Team.objects.filter(trigram="BBB", participation__problem=3).exists()) + self.assertTrue(Team.objects.filter(trigram="BBB").exists()) def test_leave_team(self): """ @@ -471,199 +420,6 @@ class TestStudentParticipation(TestCase): response = self.client.get(reverse("participation:participation_detail", args=(self.team.participation.pk,))) self.assertEqual(response.status_code, 403) - def test_upload_video(self): - """ - Try to send a solution video link. - """ - self.user.registration.team = self.team - self.user.registration.save() - - self.team.participation.valid = True - self.team.participation.save() - - response = self.client.get(reverse("participation:upload_video", args=(self.team.participation.solution.pk,))) - self.assertEqual(response.status_code, 200) - - response = self.client.post(reverse("participation:upload_video", args=(self.team.participation.solution.pk,)), - data=dict(link="https://youtube.com/watch?v=73nsrixx7eI")) - self.assertRedirects(response, - reverse("participation:participation_detail", args=(self.team.participation.id,)), - 302, 200) - self.team.participation.refresh_from_db() - self.assertEqual(self.team.participation.solution.platform, "youtube") - self.assertEqual(self.team.participation.solution.youtube_code, "73nsrixx7eI") - - response = self.client.get(reverse("participation:participation_detail", args=(self.team.participation.pk,))) - self.assertEqual(response.status_code, 200) - - # Set the second phase - for i in range(1, 5): - Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=i - 2), - end=timezone.now() + timedelta(days=i - 1)) - self.assertEqual(Phase.current_phase().phase_number, 2) - - # Can't update the link during the second phase - response = self.client.post(reverse("participation:upload_video", args=(self.team.participation.solution.pk,)), - data=dict(link="https://youtube.com/watch?v=73nsrixx7eI")) - self.assertEqual(response.status_code, 200) - - def test_questions(self): - """ - Ensure that creating/updating/deleting a question is working. - """ - self.user.registration.team = self.team - self.user.registration.save() - - self.team.participation.valid = True - self.team.participation.save() - - response = self.client.get(reverse("participation:add_question", args=(self.team.participation.pk,))) - self.assertEqual(response.status_code, 200) - - # We are not in second phase - response = self.client.post(reverse("participation:add_question", args=(self.team.participation.pk,)), - data=dict(question="I got censored!")) - self.assertEqual(response.status_code, 200) - - # Set the second phase - for i in range(1, 5): - Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=i - 2), - end=timezone.now() + timedelta(days=i - 1)) - self.assertEqual(Phase.current_phase().phase_number, 2) - - # Create a question - response = self.client.post(reverse("participation:add_question", args=(self.team.participation.pk,)), - data=dict(question="I asked a question!")) - self.assertRedirects(response, reverse("participation:participation_detail", - args=(self.team.participation.pk,)), 302, 200) - qs = Question.objects.filter(participation=self.team.participation, question="I asked a question!") - self.assertTrue(qs.exists()) - question = qs.get() - - # Update a question - response = self.client.get(reverse("participation:update_question", args=(question.pk,))) - self.assertEqual(response.status_code, 200) - response = self.client.post(reverse("participation:update_question", args=(question.pk,)), data=dict( - question="The question changed!", - )) - self.assertRedirects(response, reverse("participation:participation_detail", - args=(self.team.participation.pk,)), 302, 200) - question.refresh_from_db() - self.assertEqual(question.question, "The question changed!") - - # Delete the question - response = self.client.get(reverse("participation:delete_question", args=(question.pk,))) - self.assertEqual(response.status_code, 200) - response = self.client.post(reverse("participation:delete_question", args=(question.pk,))) - self.assertRedirects(response, reverse("participation:participation_detail", - args=(self.team.participation.pk,)), 302, 200) - self.assertFalse(Question.objects.filter(pk=question.pk).exists()) - - # Non-authenticated users are redirected to login page - self.client.logout() - response = self.client.get(reverse("participation:add_question", args=(self.team.participation.pk,))) - self.assertRedirects(response, reverse("login") + "?next=" + - reverse("participation:add_question", args=(self.team.participation.pk,)), 302, 200) - response = self.client.get(reverse("participation:update_question", args=(self.question.pk,))) - self.assertRedirects(response, reverse("login") + "?next=" + - reverse("participation:update_question", args=(self.question.pk,)), 302, 200) - response = self.client.get(reverse("participation:delete_question", args=(self.question.pk,))) - self.assertRedirects(response, reverse("login") + "?next=" + - reverse("participation:delete_question", args=(self.question.pk,)), 302, 200) - - def test_current_phase(self): - """ - Ensure that the current phase is the good one. - """ - # We are before the beginning - for i in range(1, 5): - Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=2 * i), - end=timezone.now() + timedelta(days=2 * i + 1)) - self.assertEqual(Phase.current_phase(), None) - - # We are after the end - for i in range(1, 5): - Phase.objects.filter(phase_number=i).update(start=timezone.now() - timedelta(days=2 * i), - end=timezone.now() - timedelta(days=2 * i + 1)) - self.assertEqual(Phase.current_phase().phase_number, Phase.objects.count()) - - # First phase - for i in range(1, 5): - Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=i - 1), - end=timezone.now() + timedelta(days=i)) - self.assertEqual(Phase.current_phase().phase_number, 1) - - # Second phase - for i in range(1, 5): - Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=i - 2), - end=timezone.now() + timedelta(days=i - 1)) - self.assertEqual(Phase.current_phase().phase_number, 2) - - # Third phase - for i in range(1, 5): - Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=i - 3), - end=timezone.now() + timedelta(days=i - 2)) - self.assertEqual(Phase.current_phase().phase_number, 3) - - # Fourth phase - for i in range(1, 5): - Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=i - 4), - end=timezone.now() + timedelta(days=i - 3)) - self.assertEqual(Phase.current_phase().phase_number, 4) - - response = self.client.get(reverse("participation:calendar")) - self.assertEqual(response.status_code, 200) - - response = self.client.get(reverse("participation:update_phase", args=(4,))) - self.assertEqual(response.status_code, 403) - - response = self.client.post(reverse("participation:update_phase", args=(4,)), data=dict( - start=timezone.now(), - end=timezone.now() + timedelta(days=3), - )) - self.assertEqual(response.status_code, 403) - - self.client.force_login(self.superuser) - response = self.client.get(reverse("participation:update_phase", args=(4,))) - self.assertEqual(response.status_code, 200) - - response = self.client.post(reverse("participation:update_phase", args=(4,)), data=dict( - start=timezone.now(), - end=timezone.now() + timedelta(days=3), - )) - self.assertRedirects(response, reverse("participation:calendar"), 302, 200) - fourth_phase = Phase.objects.get(phase_number=4) - self.assertEqual((fourth_phase.end - fourth_phase.start).days, 3) - - # First phase must be before the other phases - response = self.client.post(reverse("participation:update_phase", args=(1,)), data=dict( - start=timezone.now() + timedelta(days=8), - end=timezone.now() + timedelta(days=9), - )) - self.assertEqual(response.status_code, 200) - - # Fourth phase must be after the other phases - response = self.client.post(reverse("participation:update_phase", args=(4,)), data=dict( - start=timezone.now() - timedelta(days=9), - end=timezone.now() - timedelta(days=8), - )) - self.assertEqual(response.status_code, 200) - - # End must be after start - response = self.client.post(reverse("participation:update_phase", args=(4,)), data=dict( - start=timezone.now() + timedelta(days=3), - end=timezone.now(), - )) - self.assertEqual(response.status_code, 200) - - # Unauthenticated user can't update the calendar - self.client.logout() - response = self.client.get(reverse("participation:calendar")) - self.assertEqual(response.status_code, 200) - response = self.client.get(reverse("participation:update_phase", args=(2,))) - self.assertRedirects(response, reverse("login") + "?next=" + - reverse("participation:update_phase", args=(2,)), 302, 200) - def test_forbidden_access(self): """ Load personal pages and ensure that these are protected. @@ -679,20 +435,6 @@ class TestStudentParticipation(TestCase): self.assertEqual(resp.status_code, 403) resp = self.client.get(reverse("participation:participation_detail", args=(self.second_team.pk,))) self.assertEqual(resp.status_code, 403) - resp = self.client.get(reverse("participation:upload_video", - args=(self.second_team.participation.solution.pk,))) - self.assertEqual(resp.status_code, 403) - resp = self.client.get(reverse("participation:upload_video", - args=(self.second_team.participation.synthesis.pk,))) - self.assertEqual(resp.status_code, 403) - resp = self.client.get(reverse("participation:add_question", args=(self.second_team.pk,))) - self.assertEqual(resp.status_code, 403) - question = Question.objects.create(participation=self.second_team.participation, - question=self.question.question) - resp = self.client.get(reverse("participation:update_question", args=(question.pk,))) - self.assertEqual(resp.status_code, 403) - resp = self.client.get(reverse("participation:delete_question", args=(question.pk,))) - self.assertEqual(resp.status_code, 403) def test_cover_matrix(self): """ @@ -707,7 +449,6 @@ class TestStudentParticipation(TestCase): self.team.participation.save() call_command('fix_matrix_channels') - call_command('setup_third_phase') class TestAdmin(TestCase): @@ -765,50 +506,6 @@ class TestAdmin(TestCase): self.assertEqual(response.status_code, 200) self.assertTrue(response.context["object_list"]) - def test_set_received_video(self): - """ - Try to define the received video of a participation. - """ - response = self.client.get(reverse("participation:participation_receive_participation", - args=(self.team1.participation.pk,))) - self.assertEqual(response.status_code, 200) - - response = self.client.post(reverse("participation:participation_receive_participation", - args=(self.team1.participation.pk,)), - data=dict(received_participation=self.team2.participation.pk)) - self.assertRedirects(response, reverse("participation:participation_detail", - args=(self.team1.participation.pk,)), 302, 200) - - response = self.client.get(reverse("participation:participation_receive_participation", - args=(self.team1.participation.pk,))) - self.assertEqual(response.status_code, 200) - - response = self.client.post(reverse("participation:participation_send_participation", - args=(self.team1.participation.pk,)), - data=dict(sent_participation=self.team3.participation.pk)) - self.assertRedirects(response, reverse("participation:participation_detail", - args=(self.team1.participation.pk,)), 302, 200) - - self.team1.participation.refresh_from_db() - self.team2.participation.refresh_from_db() - self.team3.participation.refresh_from_db() - - self.assertEqual(self.team1.participation.received_participation.pk, self.team2.participation.pk) - self.assertEqual(self.team1.participation.sent_participation.pk, self.team3.participation.pk) - self.assertEqual(self.team2.participation.sent_participation.pk, self.team1.participation.pk) - self.assertEqual(self.team3.participation.received_participation.pk, self.team1.participation.pk) - - # The other team didn't work on the same problem - response = self.client.post(reverse("participation:participation_receive_participation", - args=(self.team1.participation.pk,)), - data=dict(received_participation=self.other_team.participation.pk)) - self.assertEqual(response.status_code, 200) - - response = self.client.post(reverse("participation:participation_send_participation", - args=(self.team1.participation.pk,)), - data=dict(sent_participation=self.other_team.participation.pk)) - self.assertEqual(response.status_code, 200) - def test_create_team_forbidden(self): """ Ensure that an admin can't create a team. @@ -816,7 +513,6 @@ class TestAdmin(TestCase): response = self.client.post(reverse("participation:create_team"), data=dict( name="Test team", trigram="TES", - grant_animath_access_videos=False, )) self.assertEqual(response.status_code, 403) diff --git a/apps/participation/urls.py b/apps/participation/urls.py index 7e6d3fe..4bb1766 100644 --- a/apps/participation/urls.py +++ b/apps/participation/urls.py @@ -4,10 +4,9 @@ from django.urls import path from django.views.generic import TemplateView -from .views import CalendarView, CreateQuestionView, CreateTeamView, DeleteQuestionView, JoinTeamView, \ - MyParticipationDetailView, MyTeamDetailView, ParticipationDetailView, PhaseUpdateView, \ - SetParticipationReceiveParticipationView, SetParticipationSendParticipationView, TeamAuthorizationsView, \ - TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, UpdateQuestionView, UploadVideoView +from .views import CreateTeamView, JoinTeamView, \ + MyParticipationDetailView, MyTeamDetailView, ParticipationDetailView, TeamAuthorizationsView, \ + TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView app_name = "participation" @@ -23,15 +22,5 @@ urlpatterns = [ path("team/leave/", TeamLeaveView.as_view(), name="team_leave"), 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"), - path("detail//receive-participation/", SetParticipationReceiveParticipationView.as_view(), - name="participation_receive_participation"), - path("detail//send-participation/", SetParticipationSendParticipationView.as_view(), - name="participation_send_participation"), - path("detail//add-question/", CreateQuestionView.as_view(), name="add_question"), - path("update-question//", UpdateQuestionView.as_view(), name="update_question"), - path("delete-question//", DeleteQuestionView.as_view(), name="delete_question"), - path("calendar/", CalendarView.as_view(), name="calendar"), - path("calendar//", PhaseUpdateView.as_view(), name="update_phase"), path("chat/", TemplateView.as_view(template_name="participation/chat.html"), name="chat") ] diff --git a/apps/participation/views.py b/apps/participation/views.py index dcb15f2..efd923c 100644 --- a/apps/participation/views.py +++ b/apps/participation/views.py @@ -17,17 +17,15 @@ from django.shortcuts import redirect from django.template.loader import render_to_string from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ -from django.views.generic import CreateView, DeleteView, DetailView, FormView, RedirectView, TemplateView, UpdateView +from django.views.generic import CreateView, DetailView, FormView, RedirectView, TemplateView, UpdateView from django.views.generic.edit import FormMixin, ProcessFormView from django_tables2 import SingleTableView from magic import Magic from registration.models import AdminRegistration -from .forms import JoinTeamForm, ParticipationForm, PhaseForm, QuestionForm, \ - ReceiveParticipationForm, RequestValidationForm, SendParticipationForm, TeamForm, \ - UploadVideoForm, ValidateParticipationForm -from .models import Participation, Phase, Question, Team, Video -from .tables import CalendarTable, TeamTable +from .forms import JoinTeamForm, ParticipationForm, RequestValidationForm, TeamForm, ValidateParticipationForm +from .models import Participation, Team +from .tables import TeamTable class CreateTeamView(LoginRequiredMixin, CreateView): @@ -177,8 +175,7 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView) # and confirmed their email address context["can_validate"] = team.students.count() >= 3 and \ all(r.email_confirmed for r in team.students.all()) and \ - all(r.photo_authorization for r in team.students.all()) and \ - team.participation.problem + all(r.photo_authorization for r in team.students.all()) return context @@ -243,8 +240,6 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView) get_sympa_client().subscribe(self.object.email, "equipes", False, f"Equipe {self.object.name}") get_sympa_client().unsubscribe(self.object.email, "equipes-non-valides", False) - get_sympa_client().subscribe(self.object.email, f"probleme-{self.object.participation.problem}", False, - f"Equipe {self.object.name}") elif "invalidate" in self.request.POST: self.object.participation.valid = None self.object.participation.save() @@ -318,7 +313,7 @@ class TeamAuthorizationsView(LoginRequiredMixin, DetailView): team = self.get_object() output = BytesIO() zf = ZipFile(output, "w") - for student in team.students.all(): + for student in team.participants.all(): magic = Magic(mime=True) mime_type = magic.from_file("media/" + student.photo_authorization.name) ext = mime_type.split("/")[1].replace("jpeg", "jpg") @@ -402,145 +397,5 @@ class ParticipationDetailView(LoginRequiredMixin, DetailView): context = super().get_context_data(**kwargs) context["title"] = lambda: _("Participation of team {trigram}").format(trigram=self.object.team.trigram) - context["current_phase"] = Phase.current_phase() return context - - -class SetParticipationReceiveParticipationView(AdminMixin, UpdateView): - """ - Define the solution that a team will receive. - """ - model = Participation - form_class = ReceiveParticipationForm - template_name = "participation/receive_participation_form.html" - - def get_success_url(self): - return reverse_lazy("participation:participation_detail", args=(self.kwargs["pk"],)) - - -class SetParticipationSendParticipationView(AdminMixin, UpdateView): - """ - Define the team where the solution will be sent. - """ - model = Participation - form_class = SendParticipationForm - template_name = "participation/send_participation_form.html" - - def get_success_url(self): - return reverse_lazy("participation:participation_detail", args=(self.kwargs["pk"],)) - - -class CreateQuestionView(LoginRequiredMixin, CreateView): - """ - Ask a question to another team. - """ - participation: Participation - model = Question - form_class = QuestionForm - extra_context = dict(title=_("Create question")) - - def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated: - return self.handle_no_permission() - self.participation = Participation.objects.get(pk=kwargs["pk"]) - if request.user.registration.is_admin or \ - request.user.registration.participates and \ - self.participation.valid and \ - request.user.registration.team.pk == self.participation.team_id: - return super().dispatch(request, *args, **kwargs) - raise PermissionDenied - - def form_valid(self, form): - form.instance.participation = self.participation - return super().form_valid(form) - - def get_success_url(self): - return reverse_lazy("participation:participation_detail", args=(self.participation.pk,)) - - -class UpdateQuestionView(LoginRequiredMixin, UpdateView): - """ - Edit a question. - """ - model = Question - form_class = QuestionForm - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - if not request.user.is_authenticated: - return self.handle_no_permission() - if request.user.registration.is_admin or \ - request.user.registration.participates and \ - self.object.participation.valid and \ - request.user.registration.team.pk == self.object.participation.team_id: - return super().dispatch(request, *args, **kwargs) - raise PermissionDenied - - def get_success_url(self): - return reverse_lazy("participation:participation_detail", args=(self.object.participation.pk,)) - - -class DeleteQuestionView(LoginRequiredMixin, DeleteView): - """ - Remove a question. - """ - model = Question - extra_context = dict(title=_("Delete question")) - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - if not request.user.is_authenticated: - return self.handle_no_permission() - if request.user.registration.is_admin or \ - request.user.registration.participates and \ - self.object.participation.valid and \ - request.user.registration.team.pk == self.object.participation.team_id: - return super().dispatch(request, *args, **kwargs) - raise PermissionDenied - - def get_success_url(self): - return reverse_lazy("participation:participation_detail", args=(self.object.participation.pk,)) - - -class UploadVideoView(LoginRequiredMixin, UpdateView): - """ - Upload a solution video for a team. - """ - model = Video - form_class = UploadVideoForm - template_name = "participation/upload_video.html" - extra_context = dict(title=_("Upload video")) - - def dispatch(self, request, *args, **kwargs): - user = request.user - if not user.is_authenticated: - return super().handle_no_permission() - if user.registration.is_admin or user.registration.participates \ - and user.registration.team.participation.pk == self.get_object().participation.pk: - return super().dispatch(request, *args, **kwargs) - raise PermissionDenied - - def get_success_url(self): - return reverse_lazy("participation:participation_detail", args=(self.object.participation.pk,)) - - -class CalendarView(SingleTableView): - """ - Display the calendar of the action. - """ - table_class = CalendarTable - model = Phase - extra_context = dict(title=_("Calendar")) - - -class PhaseUpdateView(AdminMixin, UpdateView): - """ - Update a phase of the calendar, if we have sufficient rights. - """ - model = Phase - form_class = PhaseForm - extra_context = dict(title=_("Calendar update")) - - def get_success_url(self): - return reverse_lazy("participation:calendar") diff --git a/apps/registration/migrations/0001_initial.py b/apps/registration/migrations/0001_initial.py deleted file mode 100644 index 37baf8d..0000000 --- a/apps/registration/migrations/0001_initial.py +++ /dev/null @@ -1,74 +0,0 @@ -# Generated by Django 3.1.3 on 2020-11-04 12:05 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import registration.models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('participation', '0001_initial'), - ('contenttypes', '0002_remove_content_type_name'), - ] - - operations = [ - migrations.CreateModel( - name='Registration', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('give_contact_to_animath', models.BooleanField(default=False, verbose_name='Grant Animath to contact me in the future about other actions')), - ('email_confirmed', models.BooleanField(default=False, verbose_name='email confirmed')), - ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_registration.registration_set+', to='contenttypes.contenttype')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')), - ], - options={ - 'verbose_name': 'registration', - 'verbose_name_plural': 'registrations', - }, - ), - migrations.CreateModel( - name='AdminRegistration', - fields=[ - ('registration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.registration')), - ('role', models.TextField(verbose_name='role of the administrator')), - ], - options={ - 'verbose_name': 'admin registration', - 'verbose_name_plural': 'admin registrations', - }, - bases=('registration.registration',), - ), - migrations.CreateModel( - name='StudentRegistration', - fields=[ - ('registration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.registration')), - ('student_class', models.IntegerField(choices=[(12, '12th grade'), (11, '11th grade'), (10, '10th grade or lower')], verbose_name='student class')), - ('school', models.CharField(max_length=255, verbose_name='school')), - ('photo_authorization', models.FileField(blank=True, default='', upload_to=registration.models.get_random_filename, verbose_name='photo authorization')), - ('team', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='students', to='participation.team', verbose_name='team')), - ], - options={ - 'verbose_name': 'student registration', - 'verbose_name_plural': 'student registrations', - }, - bases=('registration.registration',), - ), - migrations.CreateModel( - name='CoachRegistration', - fields=[ - ('registration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.registration')), - ('professional_activity', models.TextField(verbose_name='professional activity')), - ('team', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='coachs', to='participation.team', verbose_name='team')), - ], - options={ - 'verbose_name': 'coach registration', - 'verbose_name_plural': 'coach registrations', - }, - bases=('registration.registration',), - ), - ] diff --git a/apps/registration/models.py b/apps/registration/models.py index 536befc..00ded2f 100644 --- a/apps/registration/models.py +++ b/apps/registration/models.py @@ -129,11 +129,11 @@ class ParticipantRegistration(Registration): ) @property - def type(self): + def type(self): # pragma: no cover raise NotImplementedError @property - def form_class(self): + def form_class(self): # pragma: no cover raise NotImplementedError diff --git a/apps/registration/templatetags/search_results_tables.py b/apps/registration/templatetags/search_results_tables.py index f80a2ac..795bd75 100644 --- a/apps/registration/templatetags/search_results_tables.py +++ b/apps/registration/templatetags/search_results_tables.py @@ -3,7 +3,7 @@ from django import template from django_tables2 import Table -from participation.models import Participation, Team, Video +from participation.models import Participation, Team from participation.tables import ParticipationTable, TeamTable, VideoTable from ..models import Registration @@ -19,8 +19,6 @@ def search_table(results): table_class = TeamTable elif issubclass(model_class, Participation): table_class = ParticipationTable - elif issubclass(model_class, Video): - table_class = VideoTable return table_class([result.object for result in results], prefix=model_class._meta.model_name) diff --git a/apps/registration/tests.py b/apps/registration/tests.py index dc3e93c..b4384e3 100644 --- a/apps/registration/tests.py +++ b/apps/registration/tests.py @@ -1,7 +1,6 @@ # Copyright (C) 2020 by Animath # SPDX-License-Identifier: GPL-3.0-or-later -from datetime import timedelta import os from tfjm.tokens import email_validation_token @@ -12,10 +11,9 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.core.management import call_command from django.test import TestCase from django.urls import reverse -from django.utils import timezone from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode -from participation.models import Phase, Team +from participation.models import Team from .models import AdminRegistration, CoachRegistration, StudentRegistration @@ -54,8 +52,6 @@ class TestIndexPage(TestCase): response = self.client.get(reverse("participation:participation_detail", args=(1,))) self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:participation_detail", args=(1,))) - response = self.client.get(reverse("participation:upload_video", args=(1,))) - self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:upload_video", args=(1,))) class TestRegistration(TestCase): @@ -110,13 +106,6 @@ class TestRegistration(TestCase): """ Ensure that the signup form is working successfully. """ - # After first phase - response = self.client.get(reverse("registration:signup")) - self.assertEqual(response.status_code, 403) - - Phase.objects.filter(phase_number__gte=2).update(start=timezone.now() + timedelta(days=1), - end=timezone.now() + timedelta(days=2)) - response = self.client.get(reverse("registration:signup")) self.assertEqual(response.status_code, 200) @@ -300,7 +289,7 @@ class TestRegistration(TestCase): response = self.client.post(reverse("registration:upload_user_photo_authorization", args=(self.student.registration.pk,)), data=dict( - photo_authorization=open("tfjm/static/Autorisation de droit à l'image - majeur.pdf", "rb"), + photo_authorization=open("tfjm/static/Fiche_sanitaire.pdf", "rb"), )) self.assertRedirects(response, reverse("registration:user_detail", args=(self.student.pk,)), 302, 200) @@ -323,7 +312,7 @@ class TestRegistration(TestCase): old_authoratization = self.student.registration.photo_authorization.path response = self.client.post(reverse("registration:upload_user_photo_authorization", args=(self.student.registration.pk,)), data=dict( - photo_authorization=open("tfjm/static/Autorisation de droit à l'image - majeur.pdf", "rb"), + photo_authorization=open("tfjm/static/Fiche_sanitaire.pdf", "rb"), )) self.assertRedirects(response, reverse("registration:user_detail", args=(self.student.pk,)), 302, 200) self.assertFalse(os.path.isfile(old_authoratization)) diff --git a/apps/registration/views.py b/apps/registration/views.py index aa96f92..fa5d476 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -18,7 +18,6 @@ from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View from django_tables2 import SingleTableView from magic import Magic -from participation.models import Phase from .forms import CoachRegistrationForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm from .models import Registration, StudentRegistration @@ -34,15 +33,6 @@ class SignupView(CreateView): template_name = "registration/signup.html" extra_context = dict(title=_("Sign up")) - def dispatch(self, request, *args, **kwargs): - """ - The signup view is available only during the first phase. - """ - current_phase = Phase.current_phase() - if not current_phase or current_phase.phase_number >= 2: - raise PermissionDenied(_("You can't register now.")) - return super().dispatch(request, *args, **kwargs) - def get_context_data(self, **kwargs): context = super().get_context_data() diff --git a/tfjm/static/Fiche synthèse.pdf b/tfjm/static/Fiche_synthèse.pdf similarity index 100% rename from tfjm/static/Fiche synthèse.pdf rename to tfjm/static/Fiche_synthèse.pdf diff --git a/tfjm/static/Fiche synthèse.tex b/tfjm/static/Fiche_synthèse.tex similarity index 100% rename from tfjm/static/Fiche synthèse.tex rename to tfjm/static/Fiche_synthèse.tex diff --git a/tfjm/templates/base.html b/tfjm/templates/base.html index 994e545..3407f74 100644 --- a/tfjm/templates/base.html +++ b/tfjm/templates/base.html @@ -1,4 +1,4 @@ -{% load static i18n static calendar %} +{% load static i18n static %} {% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %} @@ -63,13 +63,6 @@ - {% if user.is_authenticated and user.registration.is_admin %} {% endif %} {% if not user.is_authenticated %} - {% if 1|current_phase %} - - {% endif %} +