1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-21 01:48:21 +02:00

Merge branch 'beta' into migration-django-4-2

This commit is contained in:
korenstin
2024-08-25 16:13:01 +02:00
190 changed files with 2674 additions and 1795 deletions

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
default_app_config = 'wei.apps.WeiConfig'

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from note_kfet.admin import admin_site

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from rest_framework import serializers

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from .views import WEIClubViewSet, BusViewSet, BusTeamViewSet, WEIRoleViewSet, WEIRegistrationViewSet, \

View File

@ -1,8 +1,9 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.filters import OrderingFilter
from api.filters import RegexSafeSearchFilter
from api.viewsets import ReadProtectedModelViewSet
from .serializers import WEIClubSerializer, BusSerializer, BusTeamSerializer, WEIRoleSerializer, \
@ -18,7 +19,7 @@ class WEIClubViewSet(ReadProtectedModelViewSet):
"""
queryset = WEIClub.objects.order_by('id')
serializer_class = WEIClubSerializer
filter_backends = [DjangoFilterBackend, SearchFilter]
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
filterset_fields = ['name', 'year', 'date_start', 'date_end', 'email', 'note__alias__name',
'note__alias__normalized_name', 'parent_club', 'parent_club__name', 'require_memberships',
'membership_fee_paid', 'membership_fee_unpaid', 'membership_duration', 'membership_start',
@ -34,7 +35,7 @@ class BusViewSet(ReadProtectedModelViewSet):
"""
queryset = Bus.objects.order_by('id')
serializer_class = BusSerializer
filter_backends = [DjangoFilterBackend, SearchFilter]
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
filterset_fields = ['name', 'wei', 'description', ]
search_fields = ['$name', '$wei__name', '$description', ]
@ -47,7 +48,7 @@ class BusTeamViewSet(ReadProtectedModelViewSet):
"""
queryset = BusTeam.objects.order_by('id')
serializer_class = BusTeamSerializer
filter_backends = [DjangoFilterBackend, SearchFilter]
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
filterset_fields = ['name', 'bus', 'color', 'description', 'bus__wei', ]
search_fields = ['$name', '$bus__name', '$bus__wei__name', '$description', ]
@ -60,7 +61,7 @@ class WEIRoleViewSet(ReadProtectedModelViewSet):
"""
queryset = WEIRole.objects.order_by('id')
serializer_class = WEIRoleSerializer
filter_backends = [DjangoFilterBackend, SearchFilter]
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
filterset_fields = ['name', 'permissions', 'memberships', ]
search_fields = ['$name', ]
@ -73,7 +74,7 @@ class WEIRegistrationViewSet(ReadProtectedModelViewSet):
"""
queryset = WEIRegistration.objects.order_by('id')
serializer_class = WEIRegistrationSerializer
filter_backends = [DjangoFilterBackend, SearchFilter]
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
filterset_fields = ['user', 'user__username', 'user__first_name', 'user__last_name', 'user__email',
'user__note__alias__name', 'user__note__alias__normalized_name', 'wei', 'wei__name',
'wei__email', 'wei__year', 'soge_credit', 'caution_check', 'birth_date', 'gender',
@ -92,7 +93,7 @@ class WEIMembershipViewSet(ReadProtectedModelViewSet):
"""
queryset = WEIMembership.objects.order_by('id')
serializer_class = WEIMembershipSerializer
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
filter_backends = [DjangoFilterBackend, OrderingFilter, RegexSafeSearchFilter]
filterset_fields = ['club__name', 'club__email', 'club__note__alias__name',
'club__note__alias__normalized_name', 'user__username', 'user__last_name',
'user__first_name', 'user__email', 'user__note__alias__name',

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.apps import AppConfig

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from .registration import WEIForm, WEIRegistrationForm, WEIMembership1AForm, WEIMembershipForm, BusForm, BusTeamForm

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from bootstrap_datepicker_plus.widgets import DatePickerInput
@ -75,7 +75,7 @@ class WEIChooseBusForm(forms.Form):
queryset=WEIRole.objects.filter(~Q(name="1A")),
label=_("WEI Roles"),
help_text=_("Select the roles that you are interested in."),
initial=WEIRole.objects.filter(name="Adhérent WEI").all(),
initial=WEIRole.objects.filter(name="Adhérent⋅e WEI").all(),
widget=CheckboxSelectMultiple(),
)

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from typing import Optional, List

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import time

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import time

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from functools import lru_cache

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.core.management import BaseCommand, CommandError
@ -84,5 +84,5 @@ class Command(BaseCommand):
s += sep + user.profile.section_generated
s += sep + bus.name
s += sep + (team.name if team else "--")
s += sep + ", ".join(role.name for role in membership.roles.filter(~Q(name="Adhérent WEI")).all())
s += sep + ", ".join(role.name for role in membership.roles.filter(~Q(name="Adhérent⋅e WEI")).all())
self.stdout.write(s)

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import argparse
import sys

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from argparse import ArgumentParser, FileType

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from datetime import date

View File

@ -6,7 +6,7 @@
\begin{document}
\begin{center}
\huge{Liste des inscrits « {{ wei.name }} »}
\huge{Liste des personnes inscrites au WEI « {{ wei.name }} »}
{% if bus %}
\LARGE{Bus {{ bus.name|safe }}}

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import random

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import random

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import random

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import subprocess
@ -504,7 +504,7 @@ class TestWEIRegistration(TestCase):
emergency_contact_phone='+33600000000',
bus=[self.bus.id],
team=[self.team.id],
roles=[role.id for role in WEIRole.objects.filter(name="Adhérent WEI").all()],
roles=[role.id for role in WEIRole.objects.filter(name="Adhérent⋅e WEI").all()],
information_json=self.registration.information_json,
)
)
@ -558,7 +558,7 @@ class TestWEIRegistration(TestCase):
emergency_contact_phone='+33600000000',
bus=[self.bus.id],
team=[self.team.id],
roles=[role.id for role in WEIRole.objects.filter(name="Adhérent WEI").all()],
roles=[role.id for role in WEIRole.objects.filter(name="Adhérent⋅e WEI").all()],
information_json=self.registration.information_json,
)
)

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.urls import path

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import os
@ -22,7 +22,8 @@ from django.views import View
from django.views.generic import DetailView, UpdateView, RedirectView, TemplateView
from django.utils.translation import gettext_lazy as _
from django.views.generic.edit import BaseFormView, DeleteView
from django_tables2 import SingleTableView
from django_tables2 import SingleTableView, MultiTableMixin
from api.viewsets import is_regex
from member.models import Membership, Club
from note.models import Transaction, NoteClub, Alias, SpecialTransaction, NoteSpecial
from note.tables import HistoryTable
@ -100,7 +101,7 @@ class WEICreateView(ProtectQuerysetMixin, ProtectedCreateView):
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.pk})
class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, DetailView):
"""
View WEI information
"""
@ -108,34 +109,40 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context_object_name = "club"
extra_context = {"title": _("WEI Detail")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
club = context["club"]
tables = [
lambda data: HistoryTable(data, prefix="history-"),
lambda data: WEIMembershipTable(data, prefix="membership-"),
lambda data: WEIRegistrationTable(data, prefix="pre-registration-"),
lambda data: BusTable(data, prefix="bus-"),
]
paginate_by = 20 # number of rows in tables
def get_tables_data(self):
club = self.object
club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note)) \
.filter(PermissionBackend.filter_queryset(self.request, Transaction, "view")) \
.order_by('-created_at', '-id')
history_table = HistoryTable(club_transactions, prefix="history-")
history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1))
context['history_list'] = history_table
club_member = WEIMembership.objects.filter(
club=club,
date_end__gte=date.today(),
).filter(PermissionBackend.filter_queryset(self.request, WEIMembership, "view"))
membership_table = WEIMembershipTable(data=club_member, prefix="membership-")
membership_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1))
context['member_list'] = membership_table
pre_registrations = WEIRegistration.objects.filter(
PermissionBackend.filter_queryset(self.request, WEIRegistration, "view")).filter(
membership=None,
wei=club
)
pre_registrations_table = WEIRegistrationTable(data=pre_registrations, prefix="pre-registration-")
pre_registrations_table.paginate(per_page=20, page=self.request.GET.get('pre-registration-page', 1))
context['pre_registrations'] = pre_registrations_table
buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request, Bus, "view")) \
.filter(wei=self.object).annotate(count=Count("memberships")).order_by("name")
return [club_transactions, club_member, pre_registrations, buses, ]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
club = context["club"]
tables = context["tables"]
for name, table in zip(["history_list", "member_list", "pre_registrations", "buses"], tables):
context[name] = table
my_registration = WEIRegistration.objects.filter(wei=club, user=self.request.user)
if my_registration.exists():
@ -144,11 +151,6 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
my_registration = None
context["my_registration"] = my_registration
buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request, Bus, "view")) \
.filter(wei=self.object).annotate(count=Count("memberships")).order_by("name")
bus_table = BusTable(data=buses, prefix="bus-")
context['buses'] = bus_table
random_user = User.objects.filter(~Q(wei__wei__in=[club])).first()
if random_user is None:
@ -219,13 +221,18 @@ class WEIMembershipsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi
if not pattern:
return qs.none()
# Check if this is a valid regex. If not, we won't check regex
valid_regex = is_regex(pattern)
suffix_alias = "__iregex" if valid_regex else "__istartswith"
suffix = "__iregex" if valid_regex else "__icontains"
prefix = "^" if valid_regex else ""
qs = qs.filter(
Q(user__first_name__iregex=pattern)
| Q(user__last_name__iregex=pattern)
| Q(user__note__alias__name__iregex="^" + pattern)
| Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
| Q(bus__name__iregex=pattern)
| Q(team__name__iregex=pattern)
Q(**{f"user__first_name{suffix}": pattern})
| Q(**{f"user__last_name{suffix}": pattern})
| Q(**{f"user__note__alias__name{suffix_alias}": prefix + pattern})
| Q(**{f"user__note__alias__normalized_name{suffix_alias}": prefix + Alias.normalize(pattern)})
| Q(**{f"bus__name{suffix}": pattern})
| Q(**{f"team__name{suffix}": pattern})
)
return qs
@ -255,11 +262,16 @@ class WEIRegistrationsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTable
pattern = self.request.GET.get("search", "")
if pattern:
# Check if this is a valid regex. If not, we won't check regex
valid_regex = is_regex(pattern)
suffix_alias = "__iregex" if valid_regex else "__istartswith"
suffix = "__iregex" if valid_regex else "__icontains"
prefix = "^" if valid_regex else ""
qs = qs.filter(
Q(user__first_name__iregex=pattern)
| Q(user__last_name__iregex=pattern)
| Q(user__note__alias__name__iregex="^" + pattern)
| Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
Q(**{f"user__first_name{suffix}": pattern})
| Q(**{f"user__last_name{suffix}": pattern})
| Q(**{f"user__note__alias__name{suffix_alias}": prefix + pattern})
| Q(**{f"user__note__alias__normalized_name{suffix_alias}": prefix + Alias.normalize(pattern)})
)
return qs
@ -916,7 +928,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
form["team"].initial = BusTeam.objects.get(pk=information["preferred_team_pk"][0])
if "preferred_roles_pk" in information:
form["roles"].initial = WEIRole.objects.filter(
Q(pk__in=information["preferred_roles_pk"]) | Q(name="Adhérent WEI")
Q(pk__in=information["preferred_roles_pk"]) | Q(name="Adhérent⋅e WEI")
).all()
return form
@ -1008,7 +1020,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
membership.save()
membership.refresh_from_db()
membership.roles.add(WEIRole.objects.get(name="Adhérent WEI"))
membership.roles.add(WEIRole.objects.get(name="Adhérent⋅e WEI"))
return super().form_valid(form)