Upload syntheses

This commit is contained in:
Yohann D'ANELLO 2021-01-14 17:26:08 +01:00
parent 6f26b24359
commit c8780a6d9d
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
8 changed files with 110 additions and 20 deletions

View File

@ -9,7 +9,7 @@ from django.core.exceptions import ValidationError
from django.utils import formats from django.utils import formats
from django.utils.translation import gettext_lazy as _ 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): class TeamForm(forms.ModelForm):
@ -162,3 +162,14 @@ class PassageForm(forms.ModelForm):
class Meta: class Meta:
model = Passage model = Passage
fields = ('solution_number', 'place', 'defender', 'opponent', 'reporter',) 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',)

View File

@ -24,6 +24,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='synthesis', model_name='synthesis',
name='file', 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'),
), ),
] ]

View File

@ -404,8 +404,8 @@ def get_solution_filename(instance, filename):
+ ("final" if instance.final_solution else "") + ("final" if instance.final_solution else "")
def get_random_synthesis_filename(instance, filename): def get_synthesis_filename(instance, filename):
return "syntheses/" + get_random_string(64) return f"syntheses/{instance.participation.team.trigram}_{instance.type}_{instance.passage.pk}"
class Solution(models.Model): class Solution(models.Model):
@ -469,12 +469,15 @@ class Synthesis(models.Model):
file = models.FileField( file = models.FileField(
verbose_name=_("file"), verbose_name=_("file"),
upload_to=get_random_synthesis_filename, upload_to=get_synthesis_filename,
unique=True, unique=True,
blank=True, blank=True,
default="", default="",
) )
def __str__(self):
return _("Synthesis for the {type} of the {passage}").format(type=self.get_type_display(), passage=self.passage)
class Meta: class Meta:
verbose_name = _("synthesis") verbose_name = _("synthesis")
verbose_name_plural = _("syntheses") verbose_name_plural = _("syntheses")

View File

@ -27,29 +27,57 @@
<dt class="col-sm-3">{% trans "Place:" %}</dt> <dt class="col-sm-3">{% trans "Place:" %}</dt>
<dd class="col-sm-9">{{ passage.place }}</dd> <dd class="col-sm-9">{{ passage.place }}</dd>
<dt class="col-sm-3">{% trans "Syntheses:" %}</dt>
<dd class="col-sm-9">
{% for synthesis in passage.syntheses.all %}
<a href="{{ synthesis.file.url }}" data-turbolinks="false">{{ synthesis }}{% if not forloop.last %}, {% endif %}</a>
{% empty %}
{% trans "No synthesis was uploaded yet." %}
{% endfor %}
</dd>
</dl> </dl>
</div> </div>
{% if user.registration.is_admin %} {% if user.registration.is_admin %}
<div class="card-footer text-center"> <div class="card-footer text-center">
<button class="btn btn-primary" data-toggle="modal" data-target="#updatePassageModal">{% trans "Update" %}</button> <button class="btn btn-primary" data-toggle="modal" data-target="#updatePassageModal">{% trans "Update" %}</button>
</div> </div>
{% elif user.registration.participates %}
<div class="card-footer text-center">
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadSynthesisModal">{% trans "Upload synthesis" %}</button>
</div>
{% endif %} {% endif %}
</div> </div>
{% trans "Update passage" as modal_title %} {% if user.registration.is_admin %}
{% trans "Update" as modal_button %} {% trans "Update passage" as modal_title %}
{% url "participation:passage_update" pk=passage.pk as modal_action %} {% trans "Update" as modal_button %}
{% include "base_modal.html" with modal_id="updatePassage" %} {% 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 %} {% endblock %}
{% block extrajavascript %} {% block extrajavascript %}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$('button[data-target="#updatePassageModal"]').click(function() { {% if user.registration.is_admin %}
let modalBody = $("#updatePassageModal div.modal-body"); $('button[data-target="#updatePassageModal"]').click(function() {
if (!modalBody.html().trim()) let modalBody = $("#updatePassageModal div.modal-body");
modalBody.load("{% url "participation:passage_update" pk=passage.pk %} #form-content") if (!modalBody.html().trim())
}); modalBody.load("{% url "participation:passage_update" pk=passage.pk %} #form-content")
});
{% elif user.registration.participates %}
$('button[data-target="#uploadSynthesisModal"]').click(function() {
let modalBody = $("#uploadSynthesisModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "participation:upload_synthesis" pk=passage.pk %} #form-content")
});
{% endif %}
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

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

View File

@ -9,7 +9,7 @@ from .views import CreateTeamView, JoinTeamView, \
PassageCreateView, PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, \ PassageCreateView, PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, \
PoolUpdateView, PoolUpdateTeamsView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, \ PoolUpdateView, PoolUpdateTeamsView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, \
TeamUpdateView, TournamentCreateView, TournamentDetailView, TournamentListView, TournamentUpdateView, \ TeamUpdateView, TournamentCreateView, TournamentDetailView, TournamentListView, TournamentUpdateView, \
SolutionUploadView SolutionUploadView, SynthesisUploadView
app_name = "participation" app_name = "participation"
@ -37,5 +37,6 @@ urlpatterns = [
path("pools/passages/add/<int:pk>/", PassageCreateView.as_view(), name="passage_create"), path("pools/passages/add/<int:pk>/", PassageCreateView.as_view(), name="passage_create"),
path("pools/passages/<int:pk>/", PassageDetailView.as_view(), name="passage_detail"), path("pools/passages/<int:pk>/", PassageDetailView.as_view(), name="passage_detail"),
path("pools/passages/<int:pk>/update/", PassageUpdateView.as_view(), name="passage_update"), path("pools/passages/<int:pk>/update/", PassageUpdateView.as_view(), name="passage_update"),
path("pools/passages/<int:pk>/solution/", SynthesisUploadView.as_view(), name="upload_synthesis"),
path("chat/", TemplateView.as_view(template_name="participation/chat.html"), name="chat") path("chat/", TemplateView.as_view(template_name="participation/chat.html"), name="chat")
] ]

View File

@ -24,8 +24,8 @@ from tfjm.matrix import Matrix
from tfjm.views import AdminMixin from tfjm.views import AdminMixin
from .forms import JoinTeamForm, ParticipationForm, PassageForm, PoolForm, PoolTeamsForm, RequestValidationForm, \ from .forms import JoinTeamForm, ParticipationForm, PassageForm, PoolForm, PoolTeamsForm, RequestValidationForm, \
SolutionForm, TeamForm, TournamentForm, ValidateParticipationForm TeamForm, TournamentForm, ValidateParticipationForm, SolutionForm, SynthesisForm
from .models import Participation, Passage, Pool, Team, Tournament, Solution from .models import Participation, Passage, Pool, Team, Tournament, Solution, Synthesis
from .tables import TeamTable, TournamentTable, ParticipationTable, PoolTable from .tables import TeamTable, TournamentTable, ParticipationTable, PoolTable
@ -460,6 +460,7 @@ class SolutionUploadView(LoginRequiredMixin, FormView):
for sol in Solution.objects.filter(participation=self.participation, for sol in Solution.objects.filter(participation=self.participation,
problem=form_sol.problem, problem=form_sol.problem,
final_solution=self.participation.final).all(): final_solution=self.participation.final).all():
sol.file.delete()
sol.delete() sol.delete()
form_sol.participation = self.participation form_sol.participation = self.participation
form_sol.final = self.participation.final form_sol.final = self.participation.final
@ -475,7 +476,7 @@ class PoolCreateView(AdminMixin, CreateView):
form_class = PoolForm form_class = PoolForm
class PoolDetailView(AdminMixin, DetailView): class PoolDetailView(LoginRequiredMixin, DetailView):
model = Pool model = Pool
@ -509,7 +510,7 @@ class PassageCreateView(AdminMixin, CreateView):
return form return form
class PassageDetailView(AdminMixin, DetailView): class PassageDetailView(LoginRequiredMixin, DetailView):
model = Passage model = Passage
@ -517,3 +518,36 @@ class PassageUpdateView(AdminMixin, UpdateView):
model = Passage model = Passage
form_class = PassageForm 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,))

View File

@ -372,7 +372,7 @@ class SynthesisView(LoginRequiredMixin, View):
""" """
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
filename = kwargs["filename"] filename = kwargs["filename"]
path = f"media/syhntheses/{filename}" path = f"media/syntheses/{filename}"
if not os.path.exists(path): if not os.path.exists(path):
raise Http404 raise Http404
solution = Synthesis.objects.get(file__endswith=filename) solution = Synthesis.objects.get(file__endswith=filename)