from io import BytesIO import os from zipfile import ZipFile from django.shortcuts import redirect from django.views.generic.base import View, TemplateView from corres2math.lists import get_sympa_client from corres2math.views import AdminMixin from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied from django.core.mail import send_mail from django.db import transaction from django.http import HttpResponse from django.template.loader import render_to_string from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, DetailView, FormView, RedirectView, UpdateView from django.views.generic.edit import FormMixin, ProcessFormView from django_tables2 import SingleTableView from magic import Magic from registration.models import AdminRegistration from .forms import JoinTeamForm, ParticipationForm, PhaseForm, RequestValidationForm, TeamForm, UploadVideoForm,\ ValidateParticipationForm from .models import Participation, Phase, Team, Video from .tables import CalendarTable class CreateTeamView(LoginRequiredMixin, CreateView): model = Team form_class = TeamForm extra_context = dict(title=_("Create team")) template_name = "participation/create_team.html" def dispatch(self, request, *args, **kwargs): user = request.user registration = user.registration if not registration.participates: raise PermissionDenied(_("You don't participate, so you can't create a team.")) elif registration.team: raise PermissionDenied(_("You are already in a team.")) return super().dispatch(request, *args, **kwargs) @transaction.atomic def form_valid(self, form): ret = super().form_valid(form) user = self.request.user registration = user.registration registration.team = form.instance registration.save() get_sympa_client().subscribe(user.email, f"equipe-{form.instance.trigram.lower()}", False, f"{user.first_name} {user.last_name}") return ret def get_success_url(self): return reverse_lazy("participation:team_detail", args=(self.object.pk,)) class JoinTeamView(LoginRequiredMixin, FormView): model = Team form_class = JoinTeamForm extra_context = dict(title=_("Join team")) template_name = "participation/create_team.html" def dispatch(self, request, *args, **kwargs): user = request.user registration = user.registration if not registration.participates: raise PermissionDenied(_("You don't participate, so you can't create a team.")) elif registration.team: raise PermissionDenied(_("You are already in a team.")) return super().dispatch(request, *args, **kwargs) @transaction.atomic def form_valid(self, form): self.object = form.instance ret = super().form_valid(form) user = self.request.user registration = user.registration registration.team = form.instance registration.save() get_sympa_client().subscribe(user.email, f"equipe-{form.instance.trigram.lower()}", False, f"{user.first_name} {user.last_name}") return ret def get_success_url(self): return reverse_lazy("participation:team_detail", args=(self.object.pk,)) class MyTeamDetailView(LoginRequiredMixin, RedirectView): def get_redirect_url(self, *args, **kwargs): user = self.request.user registration = user.registration if registration.participates: if registration.team: return reverse_lazy("participation:team_detail", args=(registration.team_id,)) raise PermissionDenied(_("You are not in a team.")) raise PermissionDenied(_("You don't participate, so you don't have any team.")) class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView): model = Team def get(self, request, *args, **kwargs): user = request.user self.object = self.get_object() if user.registration.is_admin or user.registration.participates and user.registration.team.pk == kwargs["pk"]: return super().get(request, *args, **kwargs) raise PermissionDenied def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) team = self.get_object() context["request_validation_form"] = RequestValidationForm(self.request.POST or None) context["validation_form"] = ValidateParticipationForm(self.request.POST or None) context["can_validate"] = team.students.count() >= 3 and \ all(r.email_confirmed for r in team.students.all()) and \ all(r.photo_authorization for r in team.students.all()) and \ team.participation.problem return context def get_form_class(self): if not self.request.POST: return RequestValidationForm elif self.request.POST["_form_type"] == "RequestValidationForm": return RequestValidationForm elif self.request.POST["_form_type"] == "ValidateParticipationForm": return ValidateParticipationForm return None def form_valid(self, form): self.object = self.get_object() if isinstance(form, RequestValidationForm): if not self.request.user.registration.participates: form.add_error(None, _("You don't participate, so you can't request the validation of the team.")) return self.form_invalid(form) if self.object.participation.valid is not None: form.add_error(None, _("The validation of the team is already done or pending.")) return self.form_invalid(form) self.object.participation.valid = False self.object.participation.save() for admin in AdminRegistration.objects.all(): mail_context = dict(user=admin.user, team=self.object) mail_plain = render_to_string("participation/mails/request_validation.txt", mail_context) mail_html = render_to_string("participation/mails/request_validation.html", mail_context) admin.user.email_user("[Corres2math] Validation d'équipe", mail_plain, html_message=mail_html) elif isinstance(form, ValidateParticipationForm): if not self.request.user.registration.is_admin: form.add_error(None, _("You are not an administrator.")) return self.form_invalid(form) elif self.object.participation.valid is not False: form.add_error(None, _("This team has no pending validation.")) return self.form_invalid(form) if "validate" in self.request.POST: self.object.participation.valid = True self.object.participation.save() mail_context = dict(team=self.object, message=form.cleaned_data["message"]) mail_plain = render_to_string("participation/mails/team_validated.txt", mail_context) mail_html = render_to_string("participation/mails/team_validated.html", mail_context) send_mail("[Corres2math] Équipe validée", mail_plain, None, [self.object.email], html_message=mail_html) elif "invalidate" in self.request.POST: self.object.participation.valid = None self.object.participation.save() mail_context = dict(team=self.object, message=form.cleaned_data["message"]) mail_plain = render_to_string("participation/mails/team_not_validated.txt", mail_context) mail_html = render_to_string("participation/mails/team_not_validated.html", mail_context) send_mail("[Corres2math] Équipe non validée", mail_plain, None, [self.object.email], html_message=mail_html) else: form.add_error(None, _("You must specify if you validate the registration or not.")) return self.form_invalid(form) return super().form_invalid(form) def get_success_url(self): return self.request.path class TeamUpdateView(LoginRequiredMixin, UpdateView): model = Team form_class = TeamForm template_name = "participation/update_team.html" def dispatch(self, request, *args, **kwargs): user = request.user if user.registration.is_admin or user.registration.participates and user.registration.team.pk == kwargs["pk"]: return super().dispatch(request, *args, **kwargs) raise PermissionDenied def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["participation_form"] = ParticipationForm(data=self.request.POST or None, instance=self.object.participation) return context @transaction.atomic def form_valid(self, form): participation_form = ParticipationForm(data=self.request.POST or None, instance=self.object.participation) if not participation_form.is_valid(): return self.form_invalid(form) participation_form.save() return super().form_valid(form) def get_success_url(self): return reverse_lazy("participation:team_detail", args=(self.object.pk,)) class TeamAuthorizationsView(LoginRequiredMixin, DetailView): model = Team def dispatch(self, request, *args, **kwargs): user = request.user if user.registration.is_admin or user.registration.participates and user.registration.team.pk == kwargs["pk"]: return super().dispatch(request, *args, **kwargs) raise PermissionDenied def get(self, request, *args, **kwargs): team = self.get_object() output = BytesIO() zf = ZipFile(output, "w") for student in team.students.all(): magic = Magic(mime=True) mime_type = magic.from_file("media/" + student.photo_authorization.name) ext = mime_type.split("/")[1].replace("jpeg", "jpg") zf.write("media/" + student.photo_authorization.name, _("Photo authorization of {student}.{ext}").format(student=str(student), ext=ext)) zf.close() response = HttpResponse(content_type="application/zip") response["Content-Disposition"] = "attachment; filename=\"{filename}\"" \ .format(filename=_("Photo authorizations of team {trigram}.zip").format(trigram=team.trigram)) response.write(output.getvalue()) return response class TeamLeaveView(LoginRequiredMixin, TemplateView): template_name = "participation/team_leave.html" def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated: return self.handle_no_permission() if not request.user.registration.team: raise PermissionDenied(_("You are not in a team.")) if request.user.registration.team.participation.valid is not None: raise PermissionDenied(_("The team is already validated or the validation is pending.")) return super().dispatch(request, *args, **kwargs) @transaction.atomic() def post(self, request, *args, **kwargs): team = request.user.registration.team request.user.registration.team = None request.user.registration.save() get_sympa_client().unsubscribe(request.user.email, f"equipe-{team.trigram.lower()}", False) if team.students.count() + team.coachs.count() == 0: team.delete() return redirect(reverse_lazy("index")) class MyParticipationDetailView(LoginRequiredMixin, RedirectView): def get_redirect_url(self, *args, **kwargs): user = self.request.user registration = user.registration if registration.participates: if registration.team: return reverse_lazy("participation:participation_detail", args=(registration.team.participation.id,)) raise PermissionDenied(_("You are not in a team.")) raise PermissionDenied(_("You don't participate, so you don't have any team.")) class ParticipationDetailView(LoginRequiredMixin, DetailView): model = Participation def dispatch(self, request, *args, **kwargs): user = request.user if not self.get_object().valid: raise PermissionDenied(_("The team is not validated yet.")) if user.registration.is_admin or user.registration.participates \ and user.registration.team.participation.pk == kwargs["pk"]: return super().dispatch(request, *args, **kwargs) raise PermissionDenied def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["title"] = lambda: _("Participation of team {trigram}").format(trigram=self.object.team.trigram) context["current_phase"] = Phase.current_phase() return context class UploadVideoView(LoginRequiredMixin, UpdateView): model = Video form_class = UploadVideoForm template_name = "participation/upload_video.html" def dispatch(self, request, *args, **kwargs): user = request.user if user.registration.is_admin or user.registration.participates \ and user.registration.team.participation.pk == self.get_object().participation.pk: return super().dispatch(request, *args, **kwargs) raise PermissionDenied def get_success_url(self): return reverse_lazy("participation:participation_detail", args=(self.object.participation.pk,)) class CalendarView(SingleTableView): table_class = CalendarTable model = Phase class PhaseUpdateView(AdminMixin, UpdateView): model = Phase form_class = PhaseForm def get_success_url(self): return reverse_lazy("participation:calendar")