plateforme-tfjm2/apps/tournament/views.py

496 lines
18 KiB
Python
Raw Normal View History

2020-05-04 21:37:21 +00:00
import random
2020-04-30 18:11:03 +00:00
import zipfile
from io import BytesIO
2020-04-29 15:58:11 +00:00
from django.contrib.auth.mixins import LoginRequiredMixin
2020-04-30 18:11:03 +00:00
from django.core.exceptions import PermissionDenied
2020-05-05 00:47:17 +00:00
from django.core.mail import send_mail
2020-04-29 14:26:52 +00:00
from django.db.models import Q
2020-04-30 18:11:03 +00:00
from django.http import HttpResponse
2020-05-04 18:21:53 +00:00
from django.shortcuts import redirect
2020-05-05 00:47:17 +00:00
from django.template.loader import render_to_string
2020-05-04 18:21:53 +00:00
from django.urls import reverse_lazy
2020-05-05 11:54:26 +00:00
from django.utils import timezone
2020-05-05 03:57:57 +00:00
from django.utils.decorators import method_decorator
2020-04-29 14:26:52 +00:00
from django.utils.translation import gettext_lazy as _
2020-05-05 03:57:57 +00:00
from django.views import View
from django.views.decorators.csrf import csrf_exempt
2020-05-04 18:21:53 +00:00
from django.views.generic import DetailView, CreateView, UpdateView
2020-05-04 22:11:38 +00:00
from django.views.generic.edit import BaseFormView
2020-04-29 14:26:52 +00:00
from django_tables2.views import SingleTableView
2020-04-29 02:06:02 +00:00
2020-05-04 22:11:38 +00:00
from member.models import TFJMUser, Solution, Synthesis
2020-05-05 02:45:38 +00:00
from .forms import TournamentForm, OrganizerForm, SolutionForm, SynthesisForm, TeamForm, PoolForm
from .models import Tournament, Team, Pool
from .tables import TournamentTable, TeamTable, SolutionTable, SynthesisTable, PoolTable
2020-04-30 18:11:03 +00:00
2020-05-04 18:21:53 +00:00
class AdminMixin(LoginRequiredMixin):
2020-04-30 18:11:03 +00:00
def dispatch(self, request, *args, **kwargs):
2020-05-05 00:20:45 +00:00
if not request.user.is_authenticated or not request.user.admin:
2020-04-30 18:11:03 +00:00
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
2020-05-04 22:11:38 +00:00
class OrgaMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
2020-05-05 00:20:45 +00:00
if not request.user.is_authenticated or not request.user.organizes:
2020-05-04 22:11:38 +00:00
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
2020-05-04 18:21:53 +00:00
class TeamMixin(LoginRequiredMixin):
2020-04-30 18:11:03 +00:00
def dispatch(self, request, *args, **kwargs):
2020-05-05 00:20:45 +00:00
if not request.user.is_authenticated or not request.user.team:
2020-04-30 18:11:03 +00:00
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
2020-04-29 14:26:52 +00:00
class TournamentListView(SingleTableView):
model = Tournament
table_class = TournamentTable
extra_context = dict(title=_("Tournaments list"),)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
2020-04-29 14:59:59 +00:00
team_users = TFJMUser.objects.filter(Q(team__isnull=False) | Q(role="admin") | Q(role="organizer"))\
.order_by('-role')
valid_team_users = team_users.filter(
Q(team__validation_status="2valid") | Q(role="admin") | Q(role="organizer"))
2020-04-29 14:26:52 +00:00
context["team_users_emails"] = [user.email for user in team_users]
context["valid_team_users_emails"] = [user.email for user in valid_team_users]
return context
2020-04-29 14:59:59 +00:00
2020-05-04 18:21:53 +00:00
class TournamentCreateView(AdminMixin, CreateView):
model = Tournament
form_class = TournamentForm
extra_context = dict(title=_("Add tournament"),)
def get_success_url(self):
return reverse_lazy('tournament:detail', args=(self.object.pk,))
2020-04-29 14:59:59 +00:00
class TournamentDetailView(DetailView):
model = Tournament
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = _("Tournament of {name}").format(name=self.object.name)
team_users = TFJMUser.objects.filter(
Q(team__tournament=self.object)
| Q(organized_tournaments=self.object)).order_by('role')
valid_team_users = team_users.filter(
Q(team__validation_status="2valid")
| Q(role="admin")
| Q(organized_tournaments=self.object))
context["team_users_emails"] = [user.email for user in team_users]
context["valid_team_users_emails"] = [user.email for user in valid_team_users]
context["teams"] = TeamTable(self.object.teams.all())
return context
2020-04-29 15:58:11 +00:00
2020-05-04 18:21:53 +00:00
class TournamentUpdateView(AdminMixin, UpdateView):
model = Tournament
form_class = TournamentForm
extra_context = dict(title=_("Update tournament"),)
def get_success_url(self):
return reverse_lazy('tournament:detail', args=(self.object.pk,))
2020-04-29 15:58:11 +00:00
class TeamDetailView(LoginRequiredMixin, DetailView):
model = Team
2020-04-30 19:07:12 +00:00
def dispatch(self, request, *args, **kwargs):
2020-05-05 00:20:45 +00:00
if not request.user.is_authenticated or \
(not request.user.admin and self.request.user not in self.get_object().tournament.organizers.all()
and self.get_object() != request.user.team):
2020-04-30 19:07:12 +00:00
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
2020-05-05 00:20:45 +00:00
print(request.POST)
2020-04-30 19:07:12 +00:00
team = self.get_object()
if "zip" in request.POST:
solutions = team.solutions.all()
out = BytesIO()
zf = zipfile.ZipFile(out, "w")
for solution in solutions:
zf.write(solution.file.path, str(solution) + ".pdf")
zf.close()
resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed")
resp['Content-Disposition'] = 'attachment; filename={}'\
.format(_("Solutions for team {team}.zip")
.format(team=str(team)).replace(" ", "%20"))
return resp
2020-05-05 00:20:45 +00:00
elif "leave" in request.POST and request.user.participates:
2020-05-04 22:56:34 +00:00
request.user.team = None
request.user.save()
if not team.users.exists():
team.delete()
return redirect('tournament:detail', pk=team.tournament.pk)
2020-05-05 00:20:45 +00:00
elif "request_validation" in request.POST and request.user.participates:
team.validation_status = "1waiting"
team.save()
# TODO Send mail
return redirect('tournament:team_detail', pk=team.pk)
elif "validate" in request.POST and request.user.organizes:
team.validation_status = "2valid"
team.save()
# TODO Send mail
return redirect('tournament:team_detail', pk=team.pk)
elif "invalidate" in request.POST and request.user.organizes:
team.validation_status = "0invalid"
team.save()
# TODO Send mail
return redirect('tournament:team_detail', pk=team.pk)
elif "delete" in request.POST and request.user.organizes:
2020-05-04 18:21:53 +00:00
team.delete()
return redirect('tournament:detail', pk=team.tournament.pk)
2020-04-30 19:07:12 +00:00
return self.get(request, *args, **kwargs)
2020-04-29 15:58:11 +00:00
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = _("Information about team")
return context
2020-04-30 18:11:03 +00:00
2020-05-04 18:21:53 +00:00
class TeamUpdateView(LoginRequiredMixin, UpdateView):
model = Team
form_class = TeamForm
2020-05-04 20:27:45 +00:00
extra_context = dict(title=_("Update team"),)
2020-05-04 18:21:53 +00:00
def dispatch(self, request, *args, **kwargs):
2020-05-04 20:27:45 +00:00
if not request.user.admin and self.request.user not in self.get_object().tournament.organizers.all() \
and self.get_object() != self.request.user.team:
2020-05-04 18:21:53 +00:00
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
class AddOrganizerView(AdminMixin, CreateView):
model = TFJMUser
form_class = OrganizerForm
extra_context = dict(title=_("Add organizer"),)
template_name = "tournament/add_organizer.html"
2020-05-05 00:47:17 +00:00
def form_valid(self, form):
user = form.instance
msg = render_to_string("mail_templates/add_organizer.html", context=dict(user=user))
send_mail('Organisateur du TFJM² 2020', msg, 'contact@tfjm.org', [user.email])
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy('index')
2020-05-04 18:21:53 +00:00
2020-05-04 21:37:21 +00:00
class SolutionsView(TeamMixin, BaseFormView, SingleTableView):
2020-04-30 18:11:03 +00:00
model = Solution
table_class = SolutionTable
2020-05-04 21:37:21 +00:00
form_class = SolutionForm
2020-04-30 18:11:03 +00:00
template_name = "tournament/solutions_list.html"
extra_context = dict(title=_("Solutions"))
def post(self, request, *args, **kwargs):
if "zip" in request.POST:
solutions = request.user.team.solutions
out = BytesIO()
zf = zipfile.ZipFile(out, "w")
for solution in solutions:
zf.write(solution.file.path, str(solution) + ".pdf")
zf.close()
resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed")
resp['Content-Disposition'] = 'attachment; filename={}'\
.format(_("Solutions for team {team}.zip")
.format(team=str(request.user.team)).replace(" ", "%20"))
return resp
2020-05-04 21:37:21 +00:00
return super().post(request, *args, **kwargs)
2020-04-30 18:11:03 +00:00
def get_queryset(self):
qs = super().get_queryset()
if not self.request.user.admin:
2020-05-04 21:37:21 +00:00
qs = qs.filter(team=self.request.user.team)
2020-04-30 18:11:03 +00:00
return qs.order_by('team__tournament__date_start', 'team__tournament__name', 'team__trigram', 'problem',)
2020-05-04 21:37:21 +00:00
def form_valid(self, form):
solution = form.instance
solution.team = self.request.user.team
solution.final = solution.team.selected_for_final
2020-05-05 00:20:45 +00:00
2020-05-05 11:51:47 +00:00
if timezone.now() > solution.tournament.date_solutions:
2020-05-05 00:20:45 +00:00
form.add_error('file', _("You can't publish your solution anymore. Deadline: {date:%m-%d-%Y %h:%M}.")
.format(date=solution.tournament.date_solutions))
return super().form_invalid(form)
2020-05-04 21:37:21 +00:00
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 = ""
2020-05-05 00:20:45 +00:00
for i in range(64):
2020-05-04 21:37:21 +00:00
id += random.choice(alphabet)
solution.file.name = id
solution.save()
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("tournament:solutions")
2020-04-30 18:11:03 +00:00
2020-05-04 22:11:38 +00:00
class SolutionsOrgaListView(OrgaMixin, SingleTableView):
2020-04-30 18:11:03 +00:00
model = Solution
table_class = SolutionTable
template_name = "tournament/solutions_orga_list.html"
extra_context = dict(title=_("All solutions"))
def post(self, request, *args, **kwargs):
if "tournament_zip" in request.POST:
tournament = Tournament.objects.get(pk=request.POST["tournament_zip"][0])
solutions = tournament.solutions
2020-05-04 21:37:21 +00:00
if not request.user.admin and request.user not in tournament.organizers.all():
2020-04-30 18:11:03 +00:00
raise PermissionDenied
out = BytesIO()
zf = zipfile.ZipFile(out, "w")
for solution in solutions:
zf.write(solution.file.path, str(solution) + ".pdf")
zf.close()
resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed")
resp['Content-Disposition'] = 'attachment; filename={}'\
.format(_("Solutions 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', 'problem',)
2020-05-04 22:11:38 +00:00
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
2020-05-05 00:20:45 +00:00
2020-05-05 11:51:47 +00:00
if synthesis.round == '1' and timezone.now() > synthesis.tournament.date_syntheses:
2020-05-05 00:20:45 +00:00
form.add_error('file', _("You can't publish your synthesis anymore for the first round."
" Deadline: {date:%m-%d-%Y %h:%M}.")
.format(date=synthesis.tournament.date_syntheses))
return super().form_invalid(form)
2020-05-05 11:51:47 +00:00
if synthesis.round == '2' and timezone.now() > synthesis.tournament.date_syntheses_2:
2020-05-05 00:20:45 +00:00
form.add_error('file', _("You can't publish your synthesis anymore for the second round."
" Deadline: {date:%m-%d-%Y %h:%M}.")
.format(date=synthesis.tournament.date_syntheses_2))
return super().form_invalid(form)
2020-05-04 22:11:38 +00:00
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 = ""
2020-05-05 00:20:45 +00:00
for i in range(64):
2020-05-04 22:11:38 +00:00
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',)
2020-05-05 02:45:38 +00:00
class PoolListView(LoginRequiredMixin, SingleTableView):
model = Pool
table_class = PoolTable
extra_context = dict(title=_("Pools"))
def get_queryset(self):
qs = super().get_queryset()
user = self.request.user
if not user.admin and user.organizes:
2020-05-05 11:13:43 +00:00
qs = qs.filter(Q(juries=user) | Q(teams__tournament__organizers=user))
2020-05-05 02:45:38 +00:00
elif user.participates:
qs = qs.filter(teams=user.team)
2020-05-05 14:04:53 +00:00
qs = qs.distinct().order_by('teams__tournament__date_start', 'teams__tournament__name', 'round',)
return qs
2020-05-05 02:45:38 +00:00
class PoolCreateView(AdminMixin, CreateView):
model = Pool
form_class = PoolForm
extra_context = dict(title=_("Create pool"))
def get_success_url(self):
return reverse_lazy("tournament:pools")
class PoolDetailView(LoginRequiredMixin, DetailView):
model = Pool
extra_context = dict(title=_("Pool detail"))
def get_queryset(self):
qs = super().get_queryset()
user = self.request.user
if not user.admin and user.organizes:
2020-05-05 11:32:48 +00:00
qs = qs.filter(Q(juries=user) | Q(teams__tournament__organizers=user))
2020-05-05 02:45:38 +00:00
elif user.participates:
qs = qs.filter(teams=user.team)
return qs.distinct()
def post(self, request, *args, **kwargs):
user = request.user
pool = self.get_object()
if "solutions_zip" in request.POST:
out = BytesIO()
zf = zipfile.ZipFile(out, "w")
for solution in pool.solutions.all():
zf.write(solution.file.path, str(solution) + ".pdf")
zf.close()
resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed")
resp['Content-Disposition'] = 'attachment; filename={}' \
.format(_("Solutions of a pool.zip").replace(" ", "%20"))
return resp
2020-05-05 02:48:42 +00:00
elif "syntheses_zip" in request.POST and user.organizes:
2020-05-05 02:45:38 +00:00
out = BytesIO()
zf = zipfile.ZipFile(out, "w")
for synthesis in pool.syntheses.all():
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 of a pool.zip").replace(" ", "%20"))
return resp
2020-05-05 03:57:57 +00:00
return self.get(request, *args, **kwargs)
@method_decorator(csrf_exempt, 'dispatch')
class PoolEndpoint(View):
def post(self, request, *args, **kwargs):
print(request.headers)
print(request.user)
print(self.request.POST)