Send syntheses

This commit is contained in:
Yohann D'ANELLO 2020-05-05 00:11:38 +02:00
parent 26eacad2fd
commit b55aa6f4f3
12 changed files with 219 additions and 50 deletions

View File

@ -110,7 +110,7 @@ class SynthesisViewSet(ModelViewSet):
queryset = Synthesis.objects.all()
serializer_class = SynthesisSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['team', 'team__trigram', 'dest', 'round', ]
filterset_fields = ['team', 'team__trigram', 'source', 'round', ]
# Routers provide an easy way of automatically determining the URL conf.

View File

@ -263,7 +263,7 @@ class Command(BaseCommand):
obj_dict = {
"file": args[0],
"team": Team.objects.get(pk=args[1]),
"dest": "defender" if args[3] == "DEFENDER" else "opponent"
"source": "defender" if args[3] == "DEFENDER" else "opponent"
if args[4] == "OPPOSANT" else "rapporteur",
"uploaded_at": args[4],
}

View File

@ -243,6 +243,10 @@ class Solution(Document):
verbose_name=_("final solution"),
)
@property
def tournament(self):
return Tournament.get_final() if self.final else self.team.tournament
class Meta:
verbose_name = _("solution")
verbose_name_plural = _("solutions")
@ -261,14 +265,14 @@ class Synthesis(Document):
verbose_name=_("team"),
)
dest = models.CharField(
source = models.CharField(
max_length=16,
choices=[
("defender", _("Defender")),
("opponent", _("Opponent")),
("rapporteur", _("Rapporteur")),
],
verbose_name=_("dest"),
verbose_name=_("source"),
)
round = models.PositiveSmallIntegerField(
@ -284,14 +288,18 @@ class Synthesis(Document):
verbose_name=_("final synthesis"),
)
@property
def tournament(self):
return Tournament.get_final() if self.final else self.team.tournament
class Meta:
verbose_name = _("synthesis")
verbose_name_plural = _("syntheses")
unique_together = ('team', 'dest', 'round', 'final',)
unique_together = ('team', 'source', 'round', 'final',)
def __str__(self):
return _("Synthesis of team {trigram} that is {dest} for problem {problem}")\
.format(trigram=self.team.trigram, dest=self.dest, problem=self.problem)
return _("Synthesis of team {trigram} that is {source} for the tournament {tournament}")\
.format(trigram=self.team.trigram, source=self.source, tournament=self.tournament)
class Config(models.Model):

View File

@ -58,4 +58,4 @@ class SolutionForm(forms.ModelForm):
class SynthesisForm(forms.ModelForm):
class Meta:
model = Synthesis
fields = ('file', 'dest',)
fields = ('file', 'source', 'round',)

View File

@ -64,10 +64,22 @@ class Tournament(models.Model):
verbose_name=_("year"),
)
@property
def linked_organizers(self):
return ['<a href="{url}">'.format(url=reverse_lazy("member:information", args=(user.pk,))) + str(user) + '</a>'
for user in self.organizers.all()]
@property
def solutions(self):
from member.models import Solution
return Solution.objects.filter(team__tournament=self)
return Solution.objects.filter(final=self.final) if self.final \
else Solution.objects.filter(team__tournament=self)
@property
def syntheses(self):
from member.models import Synthesis
return Synthesis.objects.filter(final=self.final) if self.final \
else Synthesis.objects.filter(team__tournament=self)
@classmethod
def get_final(cls):

View File

@ -2,7 +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 member.models import Solution, Synthesis
from .models import Tournament, Team
@ -49,8 +49,8 @@ class SolutionTable(tables.Table):
tournament = tables.LinkColumn(
"tournament:detail",
args=[A("team.tournament.pk")],
accessor=A("team.tournament"),
args=[A("tournament.pk")],
accessor=A("tournament"),
)
file = tables.LinkColumn(
@ -75,6 +75,17 @@ class SolutionTable(tables.Table):
class SynthesisTable(tables.Table):
team = tables.LinkColumn(
"tournament:team_detail",
args=[A("team.pk")],
)
tournament = tables.LinkColumn(
"tournament:detail",
args=[A("tournament.pk")],
accessor=A("tournament"),
)
file = tables.LinkColumn(
"document",
args=[A("file")],
@ -89,8 +100,8 @@ class SynthesisTable(tables.Table):
return _("Download")
class Meta:
model = Team
fields = ("team", "team.tournament", "round", "dest", "uploaded_at", "file", )
model = Synthesis
fields = ("team", "tournament", "round", "source", "uploaded_at", "file", )
attrs = {
'class': 'table table-condensed table-striped table-hover'
}

View File

@ -1,8 +1,8 @@
from django.urls import path
from django.views.generic import RedirectView
from .views import TournamentListView, TournamentCreateView, TournamentDetailView, TournamentUpdateView,\
TeamDetailView, TeamUpdateView, AddOrganizerView, SolutionsView, SolutionsOrgaListView
from .views import TournamentListView, TournamentCreateView, TournamentDetailView, TournamentUpdateView, \
TeamDetailView, TeamUpdateView, AddOrganizerView, SolutionsView, SolutionsOrgaListView, SynthesesView,\
SynthesesOrgaListView
app_name = "tournament"
@ -16,6 +16,6 @@ urlpatterns = [
path("add-organizer/", AddOrganizerView.as_view(), name="add_organizer"),
path("solutions/", SolutionsView.as_view(), name="solutions"),
path("all-solutions/", SolutionsOrgaListView.as_view(), name="all_solutions"),
path("syntheses/", RedirectView.as_view(pattern_name="index"), name="syntheses"),
path("all_syntheses/", RedirectView.as_view(pattern_name="index"), name="all_syntheses"),
path("syntheses/", SynthesesView.as_view(), name="syntheses"),
path("all_syntheses/", SynthesesOrgaListView.as_view(), name="all_syntheses"),
]

View File

@ -10,13 +10,13 @@ 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.views.generic.edit import BaseFormView
from django_tables2.views import SingleTableView
from member.models import TFJMUser, Solution
from .forms import TournamentForm, OrganizerForm, TeamForm, SolutionForm
from member.models import TFJMUser, Solution, Synthesis
from .forms import TournamentForm, OrganizerForm, TeamForm, SolutionForm, SynthesisForm
from .models import Tournament, Team
from .tables import TournamentTable, TeamTable, SolutionTable
from .tables import TournamentTable, TeamTable, SolutionTable, SynthesisTable
class AdminMixin(LoginRequiredMixin):
@ -26,6 +26,13 @@ class AdminMixin(LoginRequiredMixin):
return super().dispatch(request, *args, **kwargs)
class OrgaMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.organizes:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
class TeamMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.team:
@ -181,14 +188,6 @@ class SolutionsView(TeamMixin, BaseFormView, SingleTableView):
return super().post(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["tournaments"] = \
Tournament.objects if self.request.user.admin else self.request.user.organized_tournaments
return context
def get_queryset(self):
qs = super().get_queryset()
if not self.request.user.admin:
@ -211,16 +210,11 @@ class SolutionsView(TeamMixin, BaseFormView, SingleTableView):
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(OrgaMixin, SingleTableView):
model = Solution
table_class = SolutionTable
template_name = "tournament/solutions_orga_list.html"
@ -262,3 +256,104 @@ class SolutionsOrgaListView(AdminMixin, SingleTableView):
if not self.request.user.admin:
qs = qs.filter(team__tournament__organizers=self.request.user)
return qs.order_by('team__tournament__date_start', 'team__tournament__name', 'team__trigram', 'problem',)
class SynthesesView(TeamMixin, BaseFormView, SingleTableView):
model = Synthesis
table_class = SynthesisTable
form_class = SynthesisForm
template_name = "tournament/syntheses_list.html"
extra_context = dict(title=_("Syntheses"))
def post(self, request, *args, **kwargs):
if "zip" in request.POST:
syntheses = request.user.team.syntheses
out = BytesIO()
zf = zipfile.ZipFile(out, "w")
for synthesis in syntheses:
zf.write(synthesis.file.path, str(synthesis) + ".pdf")
zf.close()
resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed")
resp['Content-Disposition'] = 'attachment; filename={}'\
.format(_("Syntheses for team {team}.zip")
.format(team=str(request.user.team)).replace(" ", "%20"))
return resp
return super().post(request, *args, **kwargs)
def get_queryset(self):
qs = super().get_queryset()
if not self.request.user.admin:
qs = qs.filter(team=self.request.user.team)
return qs.order_by('team__tournament__date_start', 'team__tournament__name', 'team__trigram', 'round',
'source',)
def form_valid(self, form):
synthesis = form.instance
synthesis.team = self.request.user.team
synthesis.final = synthesis.team.selected_for_final
prev_syn = Synthesis.objects.filter(team=synthesis.team, round=synthesis.round, source=synthesis.source,
final=synthesis.final)
for syn in prev_syn.all():
syn.delete()
alphabet = "0123456789abcdefghijklmnopqrstuvwxyz0123456789"
id = ""
for _ in range(64):
id += random.choice(alphabet)
synthesis.file.name = id
synthesis.save()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("tournament:syntheses")
class SynthesesOrgaListView(OrgaMixin, SingleTableView):
model = Synthesis
table_class = SynthesisTable
template_name = "tournament/syntheses_orga_list.html"
extra_context = dict(title=_("All syntheses"))
def post(self, request, *args, **kwargs):
if "tournament_zip" in request.POST:
tournament = Tournament.objects.get(pk=request.POST["tournament_zip"][0])
syntheses = tournament.syntheses
if not request.user.admin and request.user not in tournament.organizers.all():
raise PermissionDenied
out = BytesIO()
zf = zipfile.ZipFile(out, "w")
for synthesis in syntheses:
zf.write(synthesis.file.path, str(synthesis) + ".pdf")
zf.close()
resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed")
resp['Content-Disposition'] = 'attachment; filename={}'\
.format(_("Syntheses for tournament {tournament}.zip")
.format(tournament=str(tournament)).replace(" ", "%20"))
return resp
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["tournaments"] = \
Tournament.objects if self.request.user.admin else self.request.user.organized_tournaments
return context
def get_queryset(self):
qs = super().get_queryset()
if not self.request.user.admin:
qs = qs.filter(team__tournament__organizers=self.request.user)
return qs.order_by('team__tournament__date_start', 'team__tournament__name', 'team__trigram', 'round',
'source',)

View File

@ -12,23 +12,33 @@
<dt class="col-xl-6 text-right">{% trans 'role'|capfirst %}</dt>
<dd class="col-xl-6">{{ tfjmuser.get_role_display }}</dd>
{% if tfjmuser.team %}
<dt class="col-xl-6 text-right">{% trans 'team'|capfirst %}</dt>
<dd class="col-xl-6"><a href="{% url "tournament:team_detail" pk=tfjmuser.team.pk %}">{{ tfjmuser.team }}</a></dd>
{% endif %}
{% if tfjmuser.birth_date %}
<dt class="col-xl-6 text-right">{% trans 'birth date'|capfirst %}</dt>
<dd class="col-xl-6">{{ tfjmuser.birth_date }}</dd>
{% endif %}
{% if tfjmuser.participates %}
<dt class="col-xl-6 text-right">{% trans 'gender'|capfirst %}</dt>
<dd class="col-xl-6">{{ tfjmuser.get_gender_display }}</dd>
{% endif %}
{% if tfjmuser.address %}
<dt class="col-xl-6 text-right">{% trans 'address'|capfirst %}</dt>
<dd class="col-xl-6">{{ tfjmuser.address }}, {{ tfjmuser.postal_code }}, {{ tfjmuser.city }}{% if tfjmuser.country != "France" %}, {{ tfjmuser.country }}{% endif %}</dd>
{% endif %}
<dt class="col-xl-6 text-right">{% trans 'email'|capfirst %}</dt>
<dd class="col-xl-6"><a href="mailto:{{ tfjmuser.email }}">{{ tfjmuser.email }}</a></dd>
{% if tfjmuser.phone_number %}
<dt class="col-xl-6 text-right">{% trans 'phone number'|capfirst %}</dt>
<dd class="col-xl-6">{{ tfjmuser.phone_number }}</dd>
{% endif %}
{% if tfjmuser.role == '3participant' %}
<dt class="col-xl-6 text-right">{% trans 'school'|capfirst %}</dt>
@ -53,9 +63,9 @@
{% endif %}
{% endif %}
{% if tfjmuser.role == '2coach' %}
{% if tfjmuser.role != '3participant' %}
<dt class="col-xl-6 text-right">{% trans 'description'|capfirst %}</dt>
<dd class="col-xl-6">{{ tfjmuser.description }}</dd>
<dd class="col-xl-6">{{ tfjmuser.description|default_if_none:"" }}</dd>
{% endif %}
</dl>
</div>

View File

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% 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 %}

View File

@ -0,0 +1,18 @@
{% extends "base.html" %}
{% load i18n django_tables2 %}
{% block content %}
{% render_table table %}
<hr>
<form method="post">
{% csrf_token %}
<div class="btn-group btn-block">
{% for tournament in tournaments.all %}
<button name="tournament_zip" value="{{ tournament.id }}" class="btn btn-success">{% blocktrans %}{{ tournament }} — ZIP{% endblocktrans %}</button>
{% endfor %}
</div>
</form>
{% endblock %}

View File

@ -10,7 +10,7 @@
<div class="card-body">
<dl class="row">
<dt class="col-xl-6 text-right">{% trans 'organizers'|capfirst %}</dt>
<dd class="col-xl-6">{{ tournament.organizers.all|join:", " }}</dd>
<dd class="col-xl-6">{% autoescape off %}{{ tournament.linked_organizers|join:", " }}{% endautoescape %}</dd>
<dt class="col-xl-6 text-right">{% trans 'size'|capfirst %}</dt>
<dd class="col-xl-6">{{ tournament.size }}</dd>