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 == "" %}
- {% trans "Upload" %}
- {% endif %}
- {% if participation.solution.link %}
- {% trans "Display" %}
- {% endif %}
-
-
-
-
- {% if user.registration.is_admin or current_phase.phase_number >= 2 %}
-
-
-
-
-
-
-
-
- {% trans "Team that received your solution:" %}
- {{ participation.sent_participation.team|default:any }}
- {% if user.registration.is_admin %}
-
- {% trans "Change" %}
-
- {% 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 %}
- {% trans "Display" %}
- {% endif %}
-
-
- {% endif %}
-
-
-
-
-
-
-
-
- {% trans "Team that sent you their solution:" %}
- {{ participation.received_participation.team|default:any }}
- {% if user.registration.is_admin %}
-
- {% trans "Change" %}
-
- {% endif %}
-
- {% trans "Proposed solution:" %}
-
- {{ participation.received_participation.solution.link|default:novideo }}
- {% if participation.received_participation.solution.link %}
- {% trans "Display" %}
- {% 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 }}
-
- {% trans "Change" %}
-
-
- {% endfor %}
-
- {% if user.registration.participates %}
-
- {% trans "Add a question" %}
-
- {% 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 %}
-
- {% 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=''|add:delete|add:" "|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 @@
{% trans "Home" %}
-
- {% if user.registration.is_admin %}
- {% trans "Calendar" %}
- {% else %}
- {% trans "Calendar" %}
- {% endif %}
-
{% if user.is_authenticated and user.registration.is_admin %}
{% trans "Users" %}
@@ -129,11 +122,9 @@
{% endif %}
{% if not user.is_authenticated %}
- {% if 1|current_phase %}
-
- {% trans "Register" %}
-
- {% endif %}
+
+ {% trans "Register" %}
+
{% trans "Log in" %}
@@ -228,9 +219,6 @@
-{% trans "Calendar" as modal_title %}
-{% include "base_modal.html" with modal_id="calendar" modal_additional_class="modal-lg" %}
-
{% if user.is_authenticated %}
{% trans "All teams" as modal_title %}
{% include "base_modal.html" with modal_id="teams" modal_additional_class="modal-lg" %}
@@ -259,11 +247,6 @@
$(".invalid-feedback").addClass("d-block");
$(document).ready(function () {
- $('a[data-target="#calendarModal"]').click(function() {
- let modalBody = $("#calendarModal div.modal-body");
- if (!modalBody.html().trim())
- modalBody.load("{% url "participation:calendar" %} #form-content")
- });
{% if user.is_authenticated and user.registration.is_admin %}
$('a[data-target="#teamsModal"]').click(function() {
let modalBody = $("#teamsModal div.modal-body");
diff --git a/tfjm/views.py b/tfjm/views.py
index c858a73..76a43df 100644
--- a/tfjm/views.py
+++ b/tfjm/views.py
@@ -8,8 +8,6 @@ from haystack.generic_views import SearchView
class AdminMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
- if not request.user.is_authenticated:
- return self.handle_no_permission()
if not request.user.registration.is_admin:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
diff --git a/tfjm/whoosh_index/MAIN_3c7ed6y4sljdfo5s.seg b/tfjm/whoosh_index/MAIN_3c7ed6y4sljdfo5s.seg
deleted file mode 100644
index 032cf27..0000000
Binary files a/tfjm/whoosh_index/MAIN_3c7ed6y4sljdfo5s.seg and /dev/null differ
diff --git a/tfjm/whoosh_index/_MAIN_11.toc b/tfjm/whoosh_index/_MAIN_11.toc
deleted file mode 100644
index 1494dfd..0000000
Binary files a/tfjm/whoosh_index/_MAIN_11.toc and /dev/null differ