diff --git a/apps/participation/forms.py b/apps/participation/forms.py
index ad98eed..7b0ddfd 100644
--- a/apps/participation/forms.py
+++ b/apps/participation/forms.py
@@ -9,7 +9,7 @@ from django.core.exceptions import ValidationError
from django.utils import formats
from django.utils.translation import gettext_lazy as _
-from .models import Participation, Passage, Pool, Team, Tournament, Solution
+from .models import Participation, Passage, Pool, Team, Tournament, Solution, Synthesis
class TeamForm(forms.ModelForm):
@@ -162,3 +162,14 @@ class PassageForm(forms.ModelForm):
class Meta:
model = Passage
fields = ('solution_number', 'place', 'defender', 'opponent', 'reporter',)
+
+
+class SynthesisForm(forms.ModelForm):
+ def save(self, commit=True):
+ """
+ Don't save a synthesis with this way. Use a view instead
+ """
+
+ class Meta:
+ model = Synthesis
+ fields = ('type', 'file',)
diff --git a/apps/participation/migrations/0007_auto_20210112_1801.py b/apps/participation/migrations/0007_auto_20210112_1801.py
index f7615dc..e2a46ed 100644
--- a/apps/participation/migrations/0007_auto_20210112_1801.py
+++ b/apps/participation/migrations/0007_auto_20210112_1801.py
@@ -24,6 +24,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='synthesis',
name='file',
- field=models.FileField(blank=True, default='', unique=True, upload_to=participation.models.get_random_synthesis_filename, verbose_name='file'),
+ field=models.FileField(blank=True, default='', unique=True, upload_to=participation.models.get_synthesis_filename, verbose_name='file'),
),
]
diff --git a/apps/participation/models.py b/apps/participation/models.py
index d85adf0..8635651 100644
--- a/apps/participation/models.py
+++ b/apps/participation/models.py
@@ -404,8 +404,8 @@ def get_solution_filename(instance, filename):
+ ("final" if instance.final_solution else "")
-def get_random_synthesis_filename(instance, filename):
- return "syntheses/" + get_random_string(64)
+def get_synthesis_filename(instance, filename):
+ return f"syntheses/{instance.participation.team.trigram}_{instance.type}_{instance.passage.pk}"
class Solution(models.Model):
@@ -469,12 +469,15 @@ class Synthesis(models.Model):
file = models.FileField(
verbose_name=_("file"),
- upload_to=get_random_synthesis_filename,
+ upload_to=get_synthesis_filename,
unique=True,
blank=True,
default="",
)
+ def __str__(self):
+ return _("Synthesis for the {type} of the {passage}").format(type=self.get_type_display(), passage=self.passage)
+
class Meta:
verbose_name = _("synthesis")
verbose_name_plural = _("syntheses")
diff --git a/apps/participation/templates/participation/passage_detail.html b/apps/participation/templates/participation/passage_detail.html
index 38a5203..3f376cb 100644
--- a/apps/participation/templates/participation/passage_detail.html
+++ b/apps/participation/templates/participation/passage_detail.html
@@ -27,29 +27,57 @@
{% trans "Place:" %}
{{ passage.place }}
+
+ {% trans "Syntheses:" %}
+
+ {% for synthesis in passage.syntheses.all %}
+ {{ synthesis }}{% if not forloop.last %}, {% endif %}
+ {% empty %}
+ {% trans "No synthesis was uploaded yet." %}
+ {% endfor %}
+
{% if user.registration.is_admin %}
+ {% elif user.registration.participates %}
+
{% endif %}
- {% trans "Update passage" as modal_title %}
- {% trans "Update" as modal_button %}
- {% url "participation:passage_update" pk=passage.pk as modal_action %}
- {% include "base_modal.html" with modal_id="updatePassage" %}
+ {% if user.registration.is_admin %}
+ {% trans "Update passage" as modal_title %}
+ {% trans "Update" as modal_button %}
+ {% url "participation:passage_update" pk=passage.pk as modal_action %}
+ {% include "base_modal.html" with modal_id="updatePassage" %}
+ {% elif user.registration.participates %}
+ {% trans "Upload synthesis" as modal_title %}
+ {% trans "Upload" as modal_button %}
+ {% url "participation:upload_synthesis" pk=passage.pk as modal_action %}
+ {% include "base_modal.html" with modal_id="uploadSynthesis" modal_enctype="multipart/form-data" %}
+ {% endif %}
{% endblock %}
{% block extrajavascript %}
{% endblock %}
diff --git a/apps/participation/templates/participation/upload_synthesis.html b/apps/participation/templates/participation/upload_synthesis.html
new file mode 100644
index 0000000..378585f
--- /dev/null
+++ b/apps/participation/templates/participation/upload_synthesis.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+
+{% load crispy_forms_filters i18n %}
+
+{% block content %}
+
+{% endblock content %}
diff --git a/apps/participation/urls.py b/apps/participation/urls.py
index 026c2e8..b622178 100644
--- a/apps/participation/urls.py
+++ b/apps/participation/urls.py
@@ -9,7 +9,7 @@ from .views import CreateTeamView, JoinTeamView, \
PassageCreateView, PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, \
PoolUpdateView, PoolUpdateTeamsView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, \
TeamUpdateView, TournamentCreateView, TournamentDetailView, TournamentListView, TournamentUpdateView, \
- SolutionUploadView
+ SolutionUploadView, SynthesisUploadView
app_name = "participation"
@@ -37,5 +37,6 @@ urlpatterns = [
path("pools/passages/add//", PassageCreateView.as_view(), name="passage_create"),
path("pools/passages//", PassageDetailView.as_view(), name="passage_detail"),
path("pools/passages//update/", PassageUpdateView.as_view(), name="passage_update"),
+ path("pools/passages//solution/", SynthesisUploadView.as_view(), name="upload_synthesis"),
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 6151f86..45aa654 100644
--- a/apps/participation/views.py
+++ b/apps/participation/views.py
@@ -24,8 +24,8 @@ from tfjm.matrix import Matrix
from tfjm.views import AdminMixin
from .forms import JoinTeamForm, ParticipationForm, PassageForm, PoolForm, PoolTeamsForm, RequestValidationForm, \
- SolutionForm, TeamForm, TournamentForm, ValidateParticipationForm
-from .models import Participation, Passage, Pool, Team, Tournament, Solution
+ TeamForm, TournamentForm, ValidateParticipationForm, SolutionForm, SynthesisForm
+from .models import Participation, Passage, Pool, Team, Tournament, Solution, Synthesis
from .tables import TeamTable, TournamentTable, ParticipationTable, PoolTable
@@ -460,6 +460,7 @@ class SolutionUploadView(LoginRequiredMixin, FormView):
for sol in Solution.objects.filter(participation=self.participation,
problem=form_sol.problem,
final_solution=self.participation.final).all():
+ sol.file.delete()
sol.delete()
form_sol.participation = self.participation
form_sol.final = self.participation.final
@@ -475,7 +476,7 @@ class PoolCreateView(AdminMixin, CreateView):
form_class = PoolForm
-class PoolDetailView(AdminMixin, DetailView):
+class PoolDetailView(LoginRequiredMixin, DetailView):
model = Pool
@@ -509,7 +510,7 @@ class PassageCreateView(AdminMixin, CreateView):
return form
-class PassageDetailView(AdminMixin, DetailView):
+class PassageDetailView(LoginRequiredMixin, DetailView):
model = Passage
@@ -517,3 +518,36 @@ class PassageUpdateView(AdminMixin, UpdateView):
model = Passage
form_class = PassageForm
+
+class SynthesisUploadView(LoginRequiredMixin, FormView):
+ template_name = "participation/upload_synthesis.html"
+ form_class = SynthesisForm
+
+ def dispatch(self, request, *args, **kwargs):
+ qs = Passage.objects.filter(pk=self.kwargs["pk"])
+ if not qs.exists():
+ raise Http404
+ self.participation = self.request.user.registration.team.participation
+ self.passage = qs.get()
+ return super().dispatch(request, *args, **kwargs)
+
+ def form_valid(self, form):
+ """
+ When a solution is submitted, it replaces a previous solution if existing,
+ otherwise it creates a new solution.
+ It is discriminating whenever the team is selected for the final tournament or not.
+ """
+ form_syn = form.instance
+ # Drop previous solution if existing
+ for syn in Synthesis.objects.filter(participation=self.participation,
+ passage=self.passage,
+ type=form_syn.type).all():
+ syn.file.delete()
+ syn.delete()
+ form_syn.participation = self.participation
+ form_syn.passage = self.passage
+ form_syn.save()
+ return super().form_valid(form)
+
+ def get_success_url(self):
+ return reverse_lazy("participation:passage_detail", args=(self.passage.pk,))
diff --git a/apps/registration/views.py b/apps/registration/views.py
index b30437d..d6c1b43 100644
--- a/apps/registration/views.py
+++ b/apps/registration/views.py
@@ -372,7 +372,7 @@ class SynthesisView(LoginRequiredMixin, View):
"""
def get(self, request, *args, **kwargs):
filename = kwargs["filename"]
- path = f"media/syhntheses/{filename}"
+ path = f"media/syntheses/{filename}"
if not os.path.exists(path):
raise Http404
solution = Synthesis.objects.get(file__endswith=filename)