From 2ca04440538ce5dc66743ef903f80caf4659a66e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 12 Jan 2021 17:24:46 +0100 Subject: [PATCH] Upload solution is working --- apps/participation/forms.py | 13 +++++- .../migrations/0006_participation_final.py | 18 ++++++++ apps/participation/models.py | 6 +++ .../participation/participation_detail.html | 21 ++++++++++ .../participation/upload_solution.html | 13 ++++++ apps/participation/urls.py | 3 +- apps/participation/views.py | 41 +++++++++++++++++-- 7 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 apps/participation/migrations/0006_participation_final.py create mode 100644 apps/participation/templates/participation/upload_solution.html diff --git a/apps/participation/forms.py b/apps/participation/forms.py index ee7ef08..8f7f649 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, Team, Tournament +from .models import Participation, Team, Tournament, Solution class TeamForm(forms.ModelForm): @@ -112,3 +112,14 @@ class TournamentForm(forms.ModelForm): class Meta: model = Tournament fields = '__all__' + + +class SolutionForm(forms.ModelForm): + def save(self, commit=True): + """ + Don't save a solution with this way. Use a view instead + """ + + class Meta: + model = Solution + fields = ('problem', 'file',) diff --git a/apps/participation/migrations/0006_participation_final.py b/apps/participation/migrations/0006_participation_final.py new file mode 100644 index 0000000..85d01f4 --- /dev/null +++ b/apps/participation/migrations/0006_participation_final.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.11 on 2021-01-12 16:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('participation', '0005_auto_20210101_1149'), + ] + + operations = [ + migrations.AddField( + model_name='participation', + name='final', + field=models.BooleanField(default=False, help_text='The team is selected for the final tournament.', verbose_name='selected for final'), + ), + ] diff --git a/apps/participation/models.py b/apps/participation/models.py index 7aaeb07..f5b92b2 100644 --- a/apps/participation/models.py +++ b/apps/participation/models.py @@ -259,6 +259,12 @@ class Participation(models.Model): help_text=_("The participation got the validation of the organizers."), ) + final = models.BooleanField( + default=False, + verbose_name=_("selected for final"), + help_text=_("The team is selected for the final tournament."), + ) + def get_absolute_url(self): return reverse_lazy("participation:participation_detail", args=(self.pk,)) diff --git a/apps/participation/templates/participation/participation_detail.html b/apps/participation/templates/participation/participation_detail.html index 4f47d47..eafd4a5 100644 --- a/apps/participation/templates/participation/participation_detail.html +++ b/apps/participation/templates/participation/participation_detail.html @@ -28,5 +28,26 @@ + + + {% trans "Upload solution" as modal_title %} + {% trans "Upload" as modal_button %} + {% url "participation:upload_solution" pk=participation.pk as modal_action %} + {% include "base_modal.html" with modal_id="uploadSolution" %} +{% endblock %} + +{% block extrajavascript %} + {% endblock %} diff --git a/apps/participation/templates/participation/upload_solution.html b/apps/participation/templates/participation/upload_solution.html new file mode 100644 index 0000000..8acce11 --- /dev/null +++ b/apps/participation/templates/participation/upload_solution.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% load crispy_forms_filters i18n %} + +{% block content %} +
+
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+{% endblock content %} diff --git a/apps/participation/urls.py b/apps/participation/urls.py index ae45494..f67deec 100644 --- a/apps/participation/urls.py +++ b/apps/participation/urls.py @@ -7,7 +7,7 @@ from django.views.generic import TemplateView from .views import CreateTeamView, JoinTeamView, \ MyParticipationDetailView, MyTeamDetailView, ParticipationDetailView, TeamAuthorizationsView, \ TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, TournamentCreateView, TournamentDetailView, \ - TournamentListView, TournamentUpdateView + TournamentListView, TournamentUpdateView, SolutionUploadView app_name = "participation" @@ -23,6 +23,7 @@ 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//solution/", SolutionUploadView.as_view(), name="upload_solution"), path("tournament/", TournamentListView.as_view(), name="tournament_list"), path("tournament/create/", TournamentCreateView.as_view(), name="tournament_create"), path("tournament//", TournamentDetailView.as_view(), name="tournament_detail"), diff --git a/apps/participation/views.py b/apps/participation/views.py index c9fc27f..2f1be04 100644 --- a/apps/participation/views.py +++ b/apps/participation/views.py @@ -9,7 +9,7 @@ from django.contrib.sites.models import Site from django.core.exceptions import PermissionDenied from django.core.mail import send_mail from django.db import transaction -from django.http import HttpResponse +from django.http import HttpResponse, Http404 from django.shortcuts import redirect from django.template.loader import render_to_string from django.urls import reverse_lazy @@ -23,9 +23,9 @@ from tfjm.lists import get_sympa_client from tfjm.matrix import Matrix from tfjm.views import AdminMixin -from .forms import JoinTeamForm, ParticipationForm, RequestValidationForm, TeamForm, ValidateParticipationForm, \ - TournamentForm -from .models import Participation, Team, Tournament +from .forms import JoinTeamForm, ParticipationForm, RequestValidationForm, SolutionForm, TeamForm, TournamentForm, \ + ValidateParticipationForm +from .models import Participation, Team, Tournament, Solution from .tables import TeamTable, TournamentTable, ParticipationTable @@ -435,3 +435,36 @@ class TournamentDetailView(DetailView): context = super().get_context_data(**kwargs) context["teams"] = ParticipationTable(self.object.participations.all()) return context + + +class SolutionUploadView(LoginRequiredMixin, FormView): + template_name = "participation/upload_solution.html" + form_class = SolutionForm + + def dispatch(self, request, *args, **kwargs): + qs = Participation.objects.filter(pk=self.kwargs["pk"]) + if not qs.exists(): + raise Http404 + self.participation = 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_sol = form.instance + # Drop previous solution if existing + Solution.objects.filter( + participation=self.participation, + problem=form_sol.problem, + final_solution=self.participation.final, + ).delete() + form_sol.participation = self.participation + form_sol.final = self.participation.final + form_sol.save() + return super().form_valid(form) + + def get_success_url(self): + return reverse_lazy("participation:participation_detail", args=(self.participation.pk,))