mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-11-04 08:22:10 +01:00 
			
		
		
		
	Send solutions
This commit is contained in:
		@@ -10,7 +10,6 @@ class SignUpForm(UserCreationForm):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.fields["first_name"].required = True
 | 
			
		||||
        self.fields["last_name"].required = True
 | 
			
		||||
        print(self.fields["role"].choices)
 | 
			
		||||
        self.fields["role"].choices = [
 | 
			
		||||
            ('', _("Choose a role...")),
 | 
			
		||||
            ('participant', _("Participant")),
 | 
			
		||||
 
 | 
			
		||||
@@ -178,6 +178,10 @@ class Document(PolymorphicModel):
 | 
			
		||||
        verbose_name = _("document")
 | 
			
		||||
        verbose_name_plural = _("documents")
 | 
			
		||||
 | 
			
		||||
    def delete(self, *args, **kwargs):
 | 
			
		||||
        self.file.delete(True)
 | 
			
		||||
        return super().delete(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Authorization(Document):
 | 
			
		||||
    user = models.ForeignKey(
 | 
			
		||||
@@ -234,14 +238,15 @@ class Solution(Document):
 | 
			
		||||
        verbose_name=_("problem"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def save(self, **kwargs):
 | 
			
		||||
        self.type = "solution"
 | 
			
		||||
        super().save(**kwargs)
 | 
			
		||||
    final = models.BooleanField(
 | 
			
		||||
        default=False,
 | 
			
		||||
        verbose_name=_("final solution"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("solution")
 | 
			
		||||
        verbose_name_plural = _("solutions")
 | 
			
		||||
        unique_together = ('team', 'problem',)
 | 
			
		||||
        unique_together = ('team', 'problem', 'final',)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return _("Solution of team {trigram} for problem {problem}")\
 | 
			
		||||
@@ -274,13 +279,15 @@ class Synthesis(Document):
 | 
			
		||||
        verbose_name=_("round"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def save(self, **kwargs):
 | 
			
		||||
        self.type = "synthesis"
 | 
			
		||||
        super().save(**kwargs)
 | 
			
		||||
    final = models.BooleanField(
 | 
			
		||||
        default=False,
 | 
			
		||||
        verbose_name=_("final synthesis"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("synthesis")
 | 
			
		||||
        verbose_name_plural = _("syntheses")
 | 
			
		||||
        unique_together = ('team', 'dest', 'round', 'final',)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return _("Synthesis of team {trigram} that is {dest} for problem {problem}")\
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ from django_tables2 import SingleTableView
 | 
			
		||||
from tournament.models import Team
 | 
			
		||||
from tournament.views import AdminMixin, TeamMixin
 | 
			
		||||
from .forms import SignUpForm, TFJMUserForm
 | 
			
		||||
from .models import TFJMUser, Document
 | 
			
		||||
from .models import TFJMUser, Document, Solution, MotivationLetter, Synthesis
 | 
			
		||||
from .tables import UserTable
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -93,7 +93,12 @@ class DocumentView(LoginRequiredMixin, View):
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
        doc = Document.objects.get(file=self.kwargs["file"])
 | 
			
		||||
 | 
			
		||||
        if not request.user.admin:
 | 
			
		||||
        grant = request.user.admin
 | 
			
		||||
 | 
			
		||||
        if isinstance(doc, Solution) or isinstance(doc, Synthesis) or isinstance(doc, MotivationLetter):
 | 
			
		||||
            grant = grant or doc.team == request.user.team or request.user in doc.team.tournament.organizers.all()
 | 
			
		||||
 | 
			
		||||
        if not grant:
 | 
			
		||||
            raise PermissionDenied
 | 
			
		||||
 | 
			
		||||
        return FileResponse(doc.file, content_type="application/pdf")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from member.models import TFJMUser
 | 
			
		||||
from member.models import TFJMUser, Solution, Synthesis
 | 
			
		||||
from tfjm.inputs import DatePickerInput, DateTimePickerInput, AmountInput
 | 
			
		||||
from tournament.models import Tournament, Team
 | 
			
		||||
 | 
			
		||||
@@ -42,3 +42,20 @@ class JoinTeam(forms.Form):
 | 
			
		||||
        label=_("Access code"),
 | 
			
		||||
        max_length=6,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SolutionForm(forms.ModelForm):
 | 
			
		||||
    problem = forms.ChoiceField(
 | 
			
		||||
        label=_("Problem"),
 | 
			
		||||
        choices=[(str(i), _("Problem #{problem:d}").format(problem=i)) for i in range(1, 9)],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Solution
 | 
			
		||||
        fields = ('file', 'problem',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SynthesisForm(forms.ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Synthesis
 | 
			
		||||
        fields = ('file', 'dest',)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import django_tables2 as tables
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
from django_tables2 import A
 | 
			
		||||
 | 
			
		||||
from member.models import Solution
 | 
			
		||||
from .models import Tournament, Team
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +42,39 @@ class TeamTable(tables.Table):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SolutionTable(tables.Table):
 | 
			
		||||
    team = tables.LinkColumn(
 | 
			
		||||
        "tournament:team_detail",
 | 
			
		||||
        args=[A("team.pk")],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    tournament = tables.LinkColumn(
 | 
			
		||||
        "tournament:detail",
 | 
			
		||||
        args=[A("team.tournament.pk")],
 | 
			
		||||
        accessor=A("team.tournament"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    file = tables.LinkColumn(
 | 
			
		||||
        "document",
 | 
			
		||||
        args=[A("file")],
 | 
			
		||||
        attrs={
 | 
			
		||||
            "a": {
 | 
			
		||||
                "data-turbolinks": "false",
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def render_file(self):
 | 
			
		||||
        return _("Download")
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Solution
 | 
			
		||||
        fields = ("team", "tournament", "problem", "uploaded_at", "file", )
 | 
			
		||||
        attrs = {
 | 
			
		||||
            'class': 'table table-condensed table-striped table-hover'
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SynthesisTable(tables.Table):
 | 
			
		||||
    file = tables.LinkColumn(
 | 
			
		||||
        "document",
 | 
			
		||||
        args=[A("file")],
 | 
			
		||||
@@ -56,7 +90,7 @@ class SolutionTable(tables.Table):
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Team
 | 
			
		||||
        fields = ("team", "team.tournament", "problem", "uploaded_at", "file", )
 | 
			
		||||
        fields = ("team", "team.tournament", "round", "dest", "uploaded_at", "file", )
 | 
			
		||||
        attrs = {
 | 
			
		||||
            'class': 'table table-condensed table-striped table-hover'
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import random
 | 
			
		||||
import zipfile
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
 | 
			
		||||
@@ -9,10 +10,11 @@ from django.shortcuts import redirect
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.views.generic import DetailView, CreateView, UpdateView
 | 
			
		||||
from django.views.generic.edit import FormMixin, BaseFormView
 | 
			
		||||
from django_tables2.views import SingleTableView
 | 
			
		||||
 | 
			
		||||
from member.models import TFJMUser, Solution
 | 
			
		||||
from .forms import TournamentForm, OrganizerForm, TeamForm
 | 
			
		||||
from .forms import TournamentForm, OrganizerForm, TeamForm, SolutionForm
 | 
			
		||||
from .models import Tournament, Team
 | 
			
		||||
from .tables import TournamentTable, TeamTable, SolutionTable
 | 
			
		||||
 | 
			
		||||
@@ -96,7 +98,8 @@ class TeamDetailView(LoginRequiredMixin, DetailView):
 | 
			
		||||
    model = Team
 | 
			
		||||
 | 
			
		||||
    def dispatch(self, request, *args, **kwargs):
 | 
			
		||||
        if not request.user.admin and self.request.user not in self.get_object().tournament.organizers:
 | 
			
		||||
        if not request.user.admin and self.request.user not in self.get_object().tournament.organizers.all()\
 | 
			
		||||
                and self.get_object() != request.user.team:
 | 
			
		||||
            raise PermissionDenied
 | 
			
		||||
        return super().dispatch(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
@@ -151,9 +154,10 @@ class AddOrganizerView(AdminMixin, CreateView):
 | 
			
		||||
    template_name = "tournament/add_organizer.html"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SolutionsView(TeamMixin, SingleTableView):
 | 
			
		||||
class SolutionsView(TeamMixin, BaseFormView, SingleTableView):
 | 
			
		||||
    model = Solution
 | 
			
		||||
    table_class = SolutionTable
 | 
			
		||||
    form_class = SolutionForm
 | 
			
		||||
    template_name = "tournament/solutions_list.html"
 | 
			
		||||
    extra_context = dict(title=_("Solutions"))
 | 
			
		||||
 | 
			
		||||
@@ -175,7 +179,7 @@ class SolutionsView(TeamMixin, SingleTableView):
 | 
			
		||||
                        .format(team=str(request.user.team)).replace(" ", "%20"))
 | 
			
		||||
            return resp
 | 
			
		||||
 | 
			
		||||
        return self.get(request, *args, **kwargs)
 | 
			
		||||
        return super().post(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = super().get_context_data(**kwargs)
 | 
			
		||||
@@ -188,9 +192,33 @@ class SolutionsView(TeamMixin, SingleTableView):
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        qs = super().get_queryset()
 | 
			
		||||
        if not self.request.user.admin:
 | 
			
		||||
            qs = qs.filter(team__tournament__organizers=self.request.user)
 | 
			
		||||
            qs = qs.filter(team=self.request.user.team)
 | 
			
		||||
        return qs.order_by('team__tournament__date_start', 'team__tournament__name', 'team__trigram', 'problem',)
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        solution = form.instance
 | 
			
		||||
        solution.team = self.request.user.team
 | 
			
		||||
        solution.final = solution.team.selected_for_final
 | 
			
		||||
        prev_sol = Solution.objects.filter(problem=solution.problem, team=solution.team, final=solution.final)
 | 
			
		||||
        for sol in prev_sol.all():
 | 
			
		||||
            sol.delete()
 | 
			
		||||
        alphabet = "0123456789abcdefghijklmnopqrstuvwxyz0123456789"
 | 
			
		||||
        id = ""
 | 
			
		||||
        for _ in range(64):
 | 
			
		||||
            id += random.choice(alphabet)
 | 
			
		||||
        solution.file.name = id
 | 
			
		||||
        solution.save()
 | 
			
		||||
 | 
			
		||||
        return super().form_valid(form)
 | 
			
		||||
 | 
			
		||||
    def form_invalid(self, form):
 | 
			
		||||
        print(form.errors)
 | 
			
		||||
 | 
			
		||||
        return super().form_invalid(form)
 | 
			
		||||
 | 
			
		||||
    def get_success_url(self):
 | 
			
		||||
        return reverse_lazy("tournament:solutions")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SolutionsOrgaListView(AdminMixin, SingleTableView):
 | 
			
		||||
    model = Solution
 | 
			
		||||
@@ -202,7 +230,7 @@ class SolutionsOrgaListView(AdminMixin, SingleTableView):
 | 
			
		||||
        if "tournament_zip" in request.POST:
 | 
			
		||||
            tournament = Tournament.objects.get(pk=request.POST["tournament_zip"][0])
 | 
			
		||||
            solutions = tournament.solutions
 | 
			
		||||
            if not request.user.admin and request.user not in tournament.organizers:
 | 
			
		||||
            if not request.user.admin and request.user not in tournament.organizers.all():
 | 
			
		||||
                raise PermissionDenied
 | 
			
		||||
 | 
			
		||||
            out = BytesIO()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,15 @@
 | 
			
		||||
{% extends "base.html" %}
 | 
			
		||||
 | 
			
		||||
{% load i18n django_tables2 %}
 | 
			
		||||
{% load i18n crispy_forms_filters django_tables2 %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
    {% if form %}
 | 
			
		||||
        <form method="post" enctype="multipart/form-data">
 | 
			
		||||
            {% csrf_token %}
 | 
			
		||||
            {{ form|crispy }}
 | 
			
		||||
            <button class="btn btn-block btn-success">{% trans "Submit" %}</button>
 | 
			
		||||
        </form>
 | 
			
		||||
        <hr>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% render_table table %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
                <dd class="col-xl-6">{{ team.trigram }}</dd>
 | 
			
		||||
 | 
			
		||||
                <dt class="col-xl-6 text-right">{% trans 'tournament'|capfirst %}</dt>
 | 
			
		||||
                <dd class="col-xl-6">{{ team.tournament }}</dd>
 | 
			
		||||
                <dd class="col-xl-6"><a href="{% url "tournament:detail" pk=team.tournament.pk %}">{{ team.tournament }}</a></dd>
 | 
			
		||||
 | 
			
		||||
                <dt class="col-xl-6 text-right">{% trans 'coachs'|capfirst %}</dt>
 | 
			
		||||
                <dd class="col-xl-6">{% autoescape off %}{{ team.linked_encadrants|join:", " }}{% endautoescape %}</dd>
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,6 @@ class SessionMiddleware(object):
 | 
			
		||||
            request.user = TFJMUser.objects.get(pk=request.session["_fake_user_id"])
 | 
			
		||||
 | 
			
		||||
        user = request.user
 | 
			
		||||
        print(user)
 | 
			
		||||
        if 'HTTP_X_FORWARDED_FOR' in request.META:
 | 
			
		||||
            ip = request.META.get('HTTP_X_FORWARDED_FOR')
 | 
			
		||||
        else:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user