import random import zipfile from io import BytesIO from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied from django.db.models import Q from django.http import HttpResponse 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, SolutionForm from .models import Tournament, Team from .tables import TournamentTable, TeamTable, SolutionTable class AdminMixin(LoginRequiredMixin): def dispatch(self, request, *args, **kwargs): if not request.user.admin: raise PermissionDenied return super().dispatch(request, *args, **kwargs) class TeamMixin(LoginRequiredMixin): def dispatch(self, request, *args, **kwargs): if not request.user.team: raise PermissionDenied return super().dispatch(request, *args, **kwargs) 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) 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")) 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 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,)) 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 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,)) 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.all()\ and self.get_object() != request.user.team: raise PermissionDenied return super().dispatch(request, *args, **kwargs) def post(self, request, *args, **kwargs): 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 elif "delete" in request.POST: team.delete() return redirect('tournament:detail', pk=team.tournament.pk) return self.get(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["title"] = _("Information about team") return context class TeamUpdateView(LoginRequiredMixin, UpdateView): model = Team form_class = TeamForm extra_context = dict(title=_("Update team"),) def dispatch(self, request, *args, **kwargs): 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: 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" class SolutionsView(TeamMixin, BaseFormView, SingleTableView): model = Solution table_class = SolutionTable form_class = SolutionForm 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 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: 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 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 if not request.user.admin and request.user not in tournament.organizers.all(): 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',)