# 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 member.views import PictureUpdateView from .models import Family, Challenge, FamilyMembership, User, Achievement from .tables import FamilyTable, ChallengeTable, FamilyMembershipTable, AchievementTable from .forms import ChallengeForm, FamilyMembershipForm, FamilyForm class FamilyCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ Create family """ model = Family extra_context = {"title": _('Create family')} form_class = FamilyForm def get_sample_object(self): return Family( name="", description="Sample family", score=0, rank=0, ) def get_success_url(self): self.object.refresh_from_db() return reverse_lazy("family:manage") 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 = FamilyForm 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) 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')} form_class = ChallengeForm 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')} form_class = ChallengeForm def get_success_url(self, **kwargs): self.object.refresh_from_db() return reverse_lazy('family:challenge_detail', kwargs={'pk': self.object.pk}) class FamilyManageView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ Manage families and challenges """ model = Achievement template_name = 'family/manage.html' table_class = AchievementTable extra_context = {'title': _('Manage families and challenges')} def dispatch(self, request, *args, **kwargs): # Check that the user is authenticated if not request.user.is_authenticated: return self.handle_no_permission() return super().dispatch(request, *args, **kwargs) def get_queryset(self, **kwargs): # retrieves only Transaction that user has the right to see. return Achievement.objects.filter( PermissionBackend.filter_queryset(self.request, Achievement, "view") ).order_by("-obtained_at").all()[:20] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['all_challenges'] = Challenge.objects.filter( PermissionBackend.filter_queryset(self.request, Challenge, "view") ).order_by('name') context["can_add_family"] = PermissionBackend.check_perm(self.request, "family.add_family") context["can_add_challenge"] = PermissionBackend.check_perm(self.request, "family.add_challenge") return context