# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.db import transaction from django.views.generic import DetailView, UpdateView from django.utils.translation import gettext_lazy as _ from django_tables2 import SingleTableView from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin, ProtectedCreateView from django.urls import reverse_lazy from .models import Family, Challenge, FamilyMembership, User from .tables import FamilyTable, ChallengeTable, FamilyMembershipTable from .forms import ChallengeUpdateForm, FamilyMembershipForm, FamilyUpdateForm from member.forms import ImageForm from member.views import PictureUpdateView class FamilyCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ Create family """ model = Family extra_context = {"title": _('Create family')} def get_sample_object(self): return Family( name="", description="Sample family", score=0, rank=0, ) class FamilyListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ List existing Families """ model = Family table_class = FamilyTable extra_context = {"title": _('Families list')} class FamilyDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): """ Display details of a family """ model = Family context_object_name = "family" extra_context = {"title": _('Family detail')} def get_context_data(self, **kwargs): """ Add members list """ context = super().get_context_data(**kwargs) family = self.object # member list family_member = FamilyMembership.objects.filter( family=family, year=date.today().year, ).filter(PermissionBackend.filter_queryset(self.request, FamilyMembership, "view"))\ .order_by("user__username") family_member = family_member.distinct("user__username")\ if settings.DATABASES["default"]["ENGINE"] == 'django.db.backends.postgresql' else family_member membership_table = FamilyMembershipTable(data=family_member, prefix="membership-") membership_table.paginate(per_page=5, page=self.request.GET.get('membership-page', 1)) context['member_list'] = membership_table # Check if the user has the right to create a membership, to display the button. empty_membership = FamilyMembership( family=family, user=User.objects.first(), year=date.today().year, ) context["can_add_members"] = PermissionBackend()\ .has_perm(self.request.user, "family.add_membership", empty_membership) return context class FamilyUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ Update the information of a family. """ model = Family context_object_name = "family" form_class = FamilyUpdateForm template_name = 'family/family_update.html' extra_context = {"title": _('Update family')} def get_success_url(self): return reverse_lazy('family:family_detail', kwargs={'pk': self.object.pk}) class FamilyPictureUpdateView(PictureUpdateView): """ Update profile picture of the family """ model = Family extra_context = {"title": _("Update family picture")} template_name = 'family/picture_update.html' def get_success_url(self): """Redirect to family page after upload""" return reverse_lazy('family:family_detail', kwargs={'pk': self.object.id}) @transaction.atomic def form_valid(self, form): """ Save the image """ image = form.cleaned_data['image'] if image is None: image = "pic/default.png" else: # Rename as PNG or GIF extension = image.name.split(".")[-1] if extension == "gif": image.name = "{}_pic.gif".format(self.object.pk) else: image.name = "{}_pic.png".format(self.object.pk) class FamilyAddMemberView(ProtectQuerysetMixin, ProtectedCreateView): """ Add a membership to a family """ model = FamilyMembership form_class = FamilyMembershipForm template_name = 'family/add_member.html' extra_context = {"title": _("Add a new member to the family")} def get_sample_object(self): if "family_pk" in self.kwargs: family = Family.objects.get(pk=self.kwargs["family_pk"]) else: family = FamilyMembership.objects.get(pk=self.kwargs["pk"]).family return FamilyMembership( user=self.request.user, family=family, year=date.today().year, ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) form = context['form'] family = Family.objects.filter(PermissionBackend.filter_queryset(self.request, Family, "view"))\ .get(pk=self.kwargs['family_pk']) context['family'] = family return context @transaction.atomic def form_valid(self, form): """ Create family membership, check that everythinf is good """ family = Family.objects.filter(PermissionBackend.filter_queryset(self.request, Family, "view")) \ .get(pk=self.kwargs["family_pk"]) form.instance.family = family return super().form_valid(form) def get_success_url(self): return reverse_lazy('family:family_detail', kwargs={'pk': self.object.family.id}) class ChallengeCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ Create challenge """ model = Challenge extra_context = {"title": _('Create challenge')} def get_sample_object(self): return Challenge( name="", description="Sample challenge", points=0, ) def get_success_url(self): return reverse_lazy('family:challenge_list') class ChallengeListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ List all challenges """ model = Challenge table_class = ChallengeTable extra_context = {"title": _('Challenges list')} class ChallengeDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): """ Display details of a challenge """ model = Challenge context_object_name = "challenge" extra_context = {"title": _('Details of:')} def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) fields = ["name", "description", "points",] fields = dict([(field, getattr(self.object, field)) for field in fields]) context["fields"] = [( Challenge._meta.get_field(field).verbose_name.capitalize(), value) for field, value in fields.items()] context["obtained"] = self.object.obtained context["update"] = PermissionBackend.check_perm(self.request, "family.change_challenge") return context class ChallengeUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ Update the information of a challenge """ model = Challenge context_object_name = "challenge" extra_context = {"title": _('Update challenge')} template_name = 'family/challenge_update.html' form_class = ChallengeUpdateForm def get_success_url(self, **kwargs): self.object.refresh_from_db() return reverse_lazy('family:challenge_detail', kwargs={'pk': self.object.pk})