Send solutions

This commit is contained in:
Yohann D'ANELLO 2020-05-04 23:37:21 +02:00
parent 9499e10524
commit 26eacad2fd
9 changed files with 118 additions and 21 deletions

View File

@ -10,7 +10,6 @@ class SignUpForm(UserCreationForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["first_name"].required = True self.fields["first_name"].required = True
self.fields["last_name"].required = True self.fields["last_name"].required = True
print(self.fields["role"].choices)
self.fields["role"].choices = [ self.fields["role"].choices = [
('', _("Choose a role...")), ('', _("Choose a role...")),
('participant', _("Participant")), ('participant', _("Participant")),

View File

@ -178,6 +178,10 @@ class Document(PolymorphicModel):
verbose_name = _("document") verbose_name = _("document")
verbose_name_plural = _("documents") verbose_name_plural = _("documents")
def delete(self, *args, **kwargs):
self.file.delete(True)
return super().delete(*args, **kwargs)
class Authorization(Document): class Authorization(Document):
user = models.ForeignKey( user = models.ForeignKey(
@ -234,14 +238,15 @@ class Solution(Document):
verbose_name=_("problem"), verbose_name=_("problem"),
) )
def save(self, **kwargs): final = models.BooleanField(
self.type = "solution" default=False,
super().save(**kwargs) verbose_name=_("final solution"),
)
class Meta: class Meta:
verbose_name = _("solution") verbose_name = _("solution")
verbose_name_plural = _("solutions") verbose_name_plural = _("solutions")
unique_together = ('team', 'problem',) unique_together = ('team', 'problem', 'final',)
def __str__(self): def __str__(self):
return _("Solution of team {trigram} for problem {problem}")\ return _("Solution of team {trigram} for problem {problem}")\
@ -274,13 +279,15 @@ class Synthesis(Document):
verbose_name=_("round"), verbose_name=_("round"),
) )
def save(self, **kwargs): final = models.BooleanField(
self.type = "synthesis" default=False,
super().save(**kwargs) verbose_name=_("final synthesis"),
)
class Meta: class Meta:
verbose_name = _("synthesis") verbose_name = _("synthesis")
verbose_name_plural = _("syntheses") verbose_name_plural = _("syntheses")
unique_together = ('team', 'dest', 'round', 'final',)
def __str__(self): def __str__(self):
return _("Synthesis of team {trigram} that is {dest} for problem {problem}")\ return _("Synthesis of team {trigram} that is {dest} for problem {problem}")\

View File

@ -12,7 +12,7 @@ from django_tables2 import SingleTableView
from tournament.models import Team from tournament.models import Team
from tournament.views import AdminMixin, TeamMixin from tournament.views import AdminMixin, TeamMixin
from .forms import SignUpForm, TFJMUserForm from .forms import SignUpForm, TFJMUserForm
from .models import TFJMUser, Document from .models import TFJMUser, Document, Solution, MotivationLetter, Synthesis
from .tables import UserTable from .tables import UserTable
@ -93,7 +93,12 @@ class DocumentView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
doc = Document.objects.get(file=self.kwargs["file"]) 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 raise PermissionDenied
return FileResponse(doc.file, content_type="application/pdf") return FileResponse(doc.file, content_type="application/pdf")

View File

@ -1,7 +1,7 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ 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 tfjm.inputs import DatePickerInput, DateTimePickerInput, AmountInput
from tournament.models import Tournament, Team from tournament.models import Tournament, Team
@ -42,3 +42,20 @@ class JoinTeam(forms.Form):
label=_("Access code"), label=_("Access code"),
max_length=6, 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',)

View File

@ -2,6 +2,7 @@ import django_tables2 as tables
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_tables2 import A from django_tables2 import A
from member.models import Solution
from .models import Tournament, Team from .models import Tournament, Team
@ -41,6 +42,39 @@ class TeamTable(tables.Table):
class SolutionTable(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( file = tables.LinkColumn(
"document", "document",
args=[A("file")], args=[A("file")],
@ -56,7 +90,7 @@ class SolutionTable(tables.Table):
class Meta: class Meta:
model = Team model = Team
fields = ("team", "team.tournament", "problem", "uploaded_at", "file", ) fields = ("team", "team.tournament", "round", "dest", "uploaded_at", "file", )
attrs = { attrs = {
'class': 'table table-condensed table-striped table-hover' 'class': 'table table-condensed table-striped table-hover'
} }

View File

@ -1,3 +1,4 @@
import random
import zipfile import zipfile
from io import BytesIO from io import BytesIO
@ -9,10 +10,11 @@ from django.shortcuts import redirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, CreateView, UpdateView from django.views.generic import DetailView, CreateView, UpdateView
from django.views.generic.edit import FormMixin, BaseFormView
from django_tables2.views import SingleTableView from django_tables2.views import SingleTableView
from member.models import TFJMUser, Solution 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 .models import Tournament, Team
from .tables import TournamentTable, TeamTable, SolutionTable from .tables import TournamentTable, TeamTable, SolutionTable
@ -96,7 +98,8 @@ class TeamDetailView(LoginRequiredMixin, DetailView):
model = Team model = Team
def dispatch(self, request, *args, **kwargs): 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 raise PermissionDenied
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
@ -151,9 +154,10 @@ class AddOrganizerView(AdminMixin, CreateView):
template_name = "tournament/add_organizer.html" template_name = "tournament/add_organizer.html"
class SolutionsView(TeamMixin, SingleTableView): class SolutionsView(TeamMixin, BaseFormView, SingleTableView):
model = Solution model = Solution
table_class = SolutionTable table_class = SolutionTable
form_class = SolutionForm
template_name = "tournament/solutions_list.html" template_name = "tournament/solutions_list.html"
extra_context = dict(title=_("Solutions")) extra_context = dict(title=_("Solutions"))
@ -175,7 +179,7 @@ class SolutionsView(TeamMixin, SingleTableView):
.format(team=str(request.user.team)).replace(" ", "%20")) .format(team=str(request.user.team)).replace(" ", "%20"))
return resp return resp
return self.get(request, *args, **kwargs) return super().post(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -188,9 +192,33 @@ class SolutionsView(TeamMixin, SingleTableView):
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
if not self.request.user.admin: 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',) 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): class SolutionsOrgaListView(AdminMixin, SingleTableView):
model = Solution model = Solution
@ -202,7 +230,7 @@ class SolutionsOrgaListView(AdminMixin, SingleTableView):
if "tournament_zip" in request.POST: if "tournament_zip" in request.POST:
tournament = Tournament.objects.get(pk=request.POST["tournament_zip"][0]) tournament = Tournament.objects.get(pk=request.POST["tournament_zip"][0])
solutions = tournament.solutions 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 raise PermissionDenied
out = BytesIO() out = BytesIO()

View File

@ -1,7 +1,15 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n django_tables2 %} {% load i18n crispy_forms_filters django_tables2 %}
{% block content %} {% 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 %} {% render_table table %}
{% endblock %} {% endblock %}

View File

@ -16,7 +16,7 @@
<dd class="col-xl-6">{{ team.trigram }}</dd> <dd class="col-xl-6">{{ team.trigram }}</dd>
<dt class="col-xl-6 text-right">{% trans 'tournament'|capfirst %}</dt> <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> <dt class="col-xl-6 text-right">{% trans 'coachs'|capfirst %}</dt>
<dd class="col-xl-6">{% autoescape off %}{{ team.linked_encadrants|join:", " }}{% endautoescape %}</dd> <dd class="col-xl-6">{% autoescape off %}{{ team.linked_encadrants|join:", " }}{% endautoescape %}</dd>

View File

@ -55,7 +55,6 @@ class SessionMiddleware(object):
request.user = TFJMUser.objects.get(pk=request.session["_fake_user_id"]) request.user = TFJMUser.objects.get(pk=request.session["_fake_user_id"])
user = request.user user = request.user
print(user)
if 'HTTP_X_FORWARDED_FOR' in request.META: if 'HTTP_X_FORWARDED_FOR' in request.META:
ip = request.META.get('HTTP_X_FORWARDED_FOR') ip = request.META.get('HTTP_X_FORWARDED_FOR')
else: else: