mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-11-04 12:32:18 +01:00 
			
		
		
		
	Upload syntheses
This commit is contained in:
		@@ -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',)
 | 
			
		||||
 
 | 
			
		||||
@@ -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'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -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")
 | 
			
		||||
 
 | 
			
		||||
@@ -27,29 +27,57 @@
 | 
			
		||||
 | 
			
		||||
                <dt class="col-sm-3">{% trans "Place:" %}</dt>
 | 
			
		||||
                <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>
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if user.registration.is_admin %}
 | 
			
		||||
            <div class="card-footer text-center">
 | 
			
		||||
                <button class="btn btn-primary" data-toggle="modal" data-target="#updatePassageModal">{% trans "Update" %}</button>
 | 
			
		||||
            </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 %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    {% 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 %}
 | 
			
		||||
    <script>
 | 
			
		||||
        $(document).ready(function () {
 | 
			
		||||
            $('button[data-target="#updatePassageModal"]').click(function() {
 | 
			
		||||
                let modalBody = $("#updatePassageModal div.modal-body");
 | 
			
		||||
                if (!modalBody.html().trim())
 | 
			
		||||
                    modalBody.load("{% url "participation:passage_update" pk=passage.pk %} #form-content")
 | 
			
		||||
            });
 | 
			
		||||
            {% if user.registration.is_admin %}
 | 
			
		||||
                $('button[data-target="#updatePassageModal"]').click(function() {
 | 
			
		||||
                    let modalBody = $("#updatePassageModal div.modal-body");
 | 
			
		||||
                    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>
 | 
			
		||||
{% 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, \
 | 
			
		||||
    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/<int:pk>/", PassageCreateView.as_view(), name="passage_create"),
 | 
			
		||||
    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>/solution/", SynthesisUploadView.as_view(), name="upload_synthesis"),
 | 
			
		||||
    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 .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,))
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user