mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2024-12-24 17:42:23 +00:00
Upload syntheses
This commit is contained in:
parent
6f26b24359
commit
c8780a6d9d
@ -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',)
|
||||||
|
@ -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'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -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")
|
||||||
|
@ -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 %}
|
||||||
|
@ -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 %}
|
@ -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")
|
||||||
]
|
]
|
||||||
|
@ -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,))
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user