nk20/apps/activity/views.py

284 lines
11 KiB
Python
Raw Normal View History

2020-03-01 16:16:38 +00:00
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
2020-03-28 00:45:13 +00:00
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
2020-03-28 00:45:13 +00:00
from django.db.models import F, Q
from django.urls import reverse_lazy
2020-08-06 15:41:30 +00:00
from django.utils import timezone
2020-03-27 00:31:54 +00:00
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, TemplateView, UpdateView
2020-03-01 16:16:38 +00:00
from django_tables2.views import SingleTableView
from note.models import Alias, NoteSpecial, NoteUser
2020-03-27 20:18:27 +00:00
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
2020-03-28 00:45:13 +00:00
2020-03-27 17:02:22 +00:00
from .forms import ActivityForm, GuestForm
from .models import Activity, Entry, Guest
from .tables import ActivityTable, EntryTable, GuestTable
2020-03-26 23:40:35 +00:00
2020-03-01 16:16:38 +00:00
class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView):
2020-08-19 09:31:15 +00:00
"""
View to create a new Activity
"""
2020-03-01 16:16:38 +00:00
model = Activity
2020-03-27 00:31:54 +00:00
form_class = ActivityForm
2020-07-30 15:30:21 +00:00
extra_context = {"title": _("Create new activity")}
2020-03-27 20:18:27 +00:00
def get_sample_object(self):
return Activity(
name="",
description="",
creater=self.request.user,
activity_type_id=1,
organizer_id=1,
attendees_club_id=1,
date_start=timezone.now(),
date_end=timezone.now(),
)
def form_valid(self, form):
form.instance.creater = self.request.user
return super().form_valid(form)
2020-03-27 20:18:27 +00:00
def get_success_url(self, **kwargs):
self.object.refresh_from_db()
return reverse_lazy('activity:activity_detail', kwargs={"pk": self.object.pk})
2020-03-26 23:40:35 +00:00
class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
2020-08-19 09:31:15 +00:00
"""
Displays all Activities, and classify if they are on-going or upcoming ones.
"""
2020-03-01 16:16:38 +00:00
model = Activity
2020-03-27 17:02:22 +00:00
table_class = ActivityTable
ordering = ('-date_start',)
2020-07-30 15:30:21 +00:00
extra_context = {"title": _("Activities")}
2020-07-28 13:25:08 +00:00
def get_queryset(self):
return super().get_queryset().distinct()
2020-03-27 00:31:54 +00:00
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
2020-03-27 00:31:54 +00:00
2020-08-06 15:41:30 +00:00
upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now())
2020-04-11 01:37:06 +00:00
context['upcoming'] = ActivityTable(
data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")),
prefix='upcoming-',
)
2020-03-27 00:31:54 +00:00
2020-08-06 15:41:30 +00:00
started_activities = Activity.objects\
.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\
.filter(open=True, valid=True).all()
context["started_activities"] = started_activities
return context
2020-03-01 16:16:38 +00:00
2020-03-26 23:40:35 +00:00
class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
2020-08-19 09:31:15 +00:00
"""
Shows details about one activity. Add guest to context
"""
2020-03-01 16:16:38 +00:00
model = Activity
2020-03-27 18:47:43 +00:00
context_object_name = "activity"
2020-07-30 15:30:21 +00:00
extra_context = {"title": _("Activity detail")}
2020-03-01 16:16:38 +00:00
2020-03-27 20:18:27 +00:00
def get_context_data(self, **kwargs):
context = super().get_context_data()
2020-03-27 20:18:27 +00:00
table = GuestTable(data=Guest.objects.filter(activity=self.object)
.filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view")))
context["guests"] = table
2020-03-27 20:18:27 +00:00
2020-08-06 15:41:30 +00:00
context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start)
return context
2020-03-27 20:18:27 +00:00
2020-03-26 23:40:35 +00:00
class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
2020-08-19 09:31:15 +00:00
"""
Updates one Activity
"""
2020-03-01 16:16:38 +00:00
model = Activity
2020-03-27 00:31:54 +00:00
form_class = ActivityForm
2020-07-30 15:30:21 +00:00
extra_context = {"title": _("Update activity")}
2020-03-27 20:18:27 +00:00
def get_success_url(self, **kwargs):
return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]})
2020-03-26 23:40:35 +00:00
class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView):
2020-08-19 09:31:15 +00:00
"""
Invite a Guest, The rules to invites someone are defined in `forms:activity.GuestForm`
"""
2020-03-27 17:02:22 +00:00
model = Guest
form_class = GuestForm
2020-08-25 15:39:30 +00:00
template_name = "activity/activity_form.html"
2020-03-27 17:02:22 +00:00
def get_sample_object(self):
2020-08-19 09:31:15 +00:00
""" Creates a standart Guest binds to the Activity"""
activity = Activity.objects.get(pk=self.kwargs["pk"])
return Guest(
activity=activity,
first_name="",
last_name="",
inviter=self.request.user.note,
)
2020-07-30 15:30:21 +00:00
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
activity = context["form"].activity
context["title"] = _('Invite guest to the activity "{}"').format(activity.name)
return context
def get_form(self, form_class=None):
form = super().get_form(form_class)
form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\
.get(pk=self.kwargs["pk"])
2020-08-29 21:06:50 +00:00
form.fields["inviter"].initial = self.request.user.note
return form
2020-03-27 18:16:38 +00:00
def form_valid(self, form):
form.instance.activity = Activity.objects\
.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).get(pk=self.kwargs["pk"])
2020-03-27 18:16:38 +00:00
return super().form_valid(form)
def get_success_url(self, **kwargs):
return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]})
2020-03-27 17:02:22 +00:00
class ActivityEntryView(LoginRequiredMixin, TemplateView):
2020-08-19 09:31:15 +00:00
"""
Manages entry to an activity
"""
2020-03-28 00:45:13 +00:00
template_name = "activity/activity_entry.html"
def dispatch(self, request, *args, **kwargs):
"""
Don't display the entry interface if the user has no right to see it (no right to add an entry for itself),
it is closed or doesn't manage entries.
"""
activity = Activity.objects.get(pk=self.kwargs["pk"])
sample_entry = Entry(activity=activity, note=self.request.user.note)
if not PermissionBackend.check_perm(self.request.user, "activity.add_entry", sample_entry):
raise PermissionDenied(_("You are not allowed to display the entry interface for this activity."))
if not activity.activity_type.manage_entries:
raise PermissionDenied(_("This activity does not support activity entries."))
if not activity.open:
raise PermissionDenied(_("This activity is closed."))
return super().dispatch(request, *args, **kwargs)
2020-08-19 21:00:49 +00:00
def get_invited_guest(self, activity):
2020-08-19 09:31:15 +00:00
"""
Retrieves all Guests to the activity
"""
2020-03-28 00:45:13 +00:00
guest_qs = Guest.objects\
.annotate(balance=F("inviter__balance"), note_name=F("inviter__user__username"))\
2020-08-03 11:33:25 +00:00
.filter(activity=activity)\
.filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))\
2020-08-03 11:33:25 +00:00
.order_by('last_name', 'first_name').distinct()
2020-08-06 12:11:55 +00:00
if "search" in self.request.GET and self.request.GET["search"]:
2020-08-03 11:33:25 +00:00
pattern = self.request.GET["search"]
if pattern[0] != "^":
pattern = "^" + pattern
guest_qs = guest_qs.filter(
Q(first_name__regex=pattern)
| Q(last_name__regex=pattern)
| Q(inviter__alias__name__regex=pattern)
| Q(inviter__alias__normalized_name__regex=Alias.normalize(pattern))
)
else:
guest_qs = guest_qs.none()
2020-08-19 09:31:15 +00:00
return guest_qs
2020-08-03 11:33:25 +00:00
2020-08-19 21:00:49 +00:00
def get_invited_note(self, activity):
2020-08-19 09:31:15 +00:00
"""
Retrieves all Note that can attend the activity,
they need to have an up-to-date membership in the attendees_club.
"""
2020-03-28 00:45:13 +00:00
note_qs = Alias.objects.annotate(last_name=F("note__noteuser__user__last_name"),
first_name=F("note__noteuser__user__first_name"),
username=F("note__noteuser__user__username"),
note_name=F("name"),
balance=F("note__balance"))
# Keep only users that have a note
note_qs = note_qs.filter(note__noteuser__isnull=False)
# Keep only members
note_qs = note_qs.filter(
2020-08-06 15:41:30 +00:00
note__noteuser__user__memberships__club=activity.attendees_club,
note__noteuser__user__memberships__date_start__lte=timezone.now(),
note__noteuser__user__memberships__date_end__gte=timezone.now(),
)
# Filter with permission backend
note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view"))
2020-08-29 21:06:50 +00:00
if "search" in self.request.GET and self.request.GET["search"]:
pattern = self.request.GET["search"]
2020-08-03 11:33:25 +00:00
note_qs = note_qs.filter(
Q(note__noteuser__user__first_name__regex=pattern)
| Q(note__noteuser__user__last_name__regex=pattern)
| Q(name__regex=pattern)
| Q(normalized_name__regex=Alias.normalize(pattern))
)
else:
note_qs = note_qs.none()
2020-08-29 21:06:50 +00:00
if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql':
note_qs = note_qs.distinct('note__pk')[:20]
else:
# SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only
# have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page.
# In production mode, please use PostgreSQL.
note_qs = note_qs.distinct()[:20]
2020-08-19 09:31:15 +00:00
return note_qs
def get_context_data(self, **kwargs):
"""
Query the list of Guest and Note to the activity and add information to makes entry with JS.
"""
context = super().get_context_data(**kwargs)
activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\
.distinct().get(pk=self.kwargs["pk"])
context["activity"] = activity
2020-08-29 21:06:50 +00:00
matched = []
2020-08-19 09:31:15 +00:00
2020-08-29 21:06:50 +00:00
for guest in self.get_invited_guest(activity):
2020-08-19 09:31:15 +00:00
guest.type = "Invité"
matched.append(guest)
2020-08-03 11:33:25 +00:00
2020-08-29 21:06:50 +00:00
for note in self.get_invited_note(activity):
2020-03-28 00:45:13 +00:00
note.type = "Adhérent"
note.activity = activity
2020-03-28 00:45:13 +00:00
matched.append(note)
table = EntryTable(data=matched)
context["table"] = table
2020-03-28 00:45:13 +00:00
context["entries"] = Entry.objects.filter(activity=activity)
context["title"] = _('Entry for activity "{}"').format(activity.name)
context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk
context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk
2020-08-03 14:11:05 +00:00
activities_open = Activity.objects.filter(open=True).filter(
PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all()
context["activities_open"] = [a for a in activities_open
if PermissionBackend.check_perm(self.request.user,
"activity.add_entry",
Entry(activity=a, note=self.request.user.note,))]
return context