mirror of
https://github.com/ae-utbm/sith.git
synced 2026-03-22 03:25:05 +00:00
adapt club members pages to new club roles framework
This commit is contained in:
@@ -23,13 +23,12 @@
|
||||
#
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.db.models import Exists, OuterRef, Q
|
||||
from django.db.models import Exists, OuterRef, Q, QuerySet
|
||||
from django.db.models.functions import Lower
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from club.models import Club, Mailing, MailingSubscription, Membership
|
||||
from club.models import Club, ClubRole, Mailing, MailingSubscription, Membership
|
||||
from core.models import User
|
||||
from core.views.forms import SelectDateTime
|
||||
from core.views.widgets.ajax_select import (
|
||||
@@ -215,9 +214,7 @@ class ClubOldMemberForm(forms.Form):
|
||||
|
||||
def __init__(self, *args, user: User, club: Club, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["members_old"].queryset = (
|
||||
Membership.objects.ongoing().filter(club=club).editable_by(user)
|
||||
)
|
||||
self.fields["members_old"].queryset = club.members.ongoing().editable_by(user)
|
||||
|
||||
|
||||
class ClubMemberForm(forms.ModelForm):
|
||||
@@ -235,19 +232,14 @@ class ClubMemberForm(forms.ModelForm):
|
||||
self.request_user = request_user
|
||||
self.request_user_membership = self.club.get_membership_for(self.request_user)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["role"].required = True
|
||||
self.fields["role"].choices = [
|
||||
(value, name)
|
||||
for value, name in settings.SITH_CLUB_ROLES.items()
|
||||
if value <= self.max_available_role
|
||||
]
|
||||
self.fields["role"].queryset = self.available_roles
|
||||
self.instance.club = club
|
||||
|
||||
@property
|
||||
def max_available_role(self):
|
||||
def available_roles(self) -> QuerySet[ClubRole]:
|
||||
"""The greatest role that will be obtainable with this form."""
|
||||
# this is unreachable, because it will be overridden by subclasses
|
||||
return -1 # pragma: no cover
|
||||
return ClubRole.objects.none() # pragma: no cover
|
||||
|
||||
|
||||
class ClubAddMemberForm(ClubMemberForm):
|
||||
@@ -258,7 +250,7 @@ class ClubAddMemberForm(ClubMemberForm):
|
||||
widgets = {"user": AutoCompleteSelectUser}
|
||||
|
||||
@cached_property
|
||||
def max_available_role(self):
|
||||
def available_roles(self):
|
||||
"""The greatest role that will be obtainable with this form.
|
||||
|
||||
Admins and the club president can attribute any role.
|
||||
@@ -266,13 +258,13 @@ class ClubAddMemberForm(ClubMemberForm):
|
||||
Other users cannot attribute roles with this form
|
||||
"""
|
||||
if self.request_user.has_perm("club.add_membership"):
|
||||
return settings.SITH_CLUB_ROLES_ID["President"]
|
||||
return self.club.roles.all()
|
||||
membership = self.request_user_membership
|
||||
if membership is None or membership.role <= settings.SITH_MAXIMUM_FREE_ROLE:
|
||||
return -1
|
||||
if membership.role == settings.SITH_CLUB_ROLES_ID["President"]:
|
||||
return membership.role
|
||||
return membership.role - 1
|
||||
if membership is None or not membership.role.is_board:
|
||||
return ClubRole.objects.none()
|
||||
if membership.role.is_presidency:
|
||||
return self.club.roles.all()
|
||||
return self.club.roles.above_instance(membership.role)
|
||||
|
||||
def clean_user(self):
|
||||
"""Check that the user is not trying to add a user already in the club.
|
||||
@@ -296,13 +288,11 @@ class JoinClubForm(ClubMemberForm):
|
||||
|
||||
def __init__(self, *args, club: Club, request_user: User, **kwargs):
|
||||
super().__init__(*args, club=club, request_user=request_user, **kwargs)
|
||||
# this form doesn't manage the user who will join the club,
|
||||
# so we must set this here to avoid errors
|
||||
self.instance.user = self.request_user
|
||||
|
||||
@cached_property
|
||||
def max_available_role(self):
|
||||
return settings.SITH_MAXIMUM_FREE_ROLE
|
||||
def available_roles(self):
|
||||
return self.club.roles.filter(is_board=False)
|
||||
|
||||
def clean(self):
|
||||
"""Check that the user is subscribed and isn't already in the club."""
|
||||
|
||||
@@ -5,24 +5,33 @@ from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
from django.db.migrations.state import StateApps
|
||||
from django.db.models import Case, When
|
||||
from django.utils import translation
|
||||
|
||||
PRESIDENT_ROLE = 10
|
||||
SITH_CLUB_ROLES = {
|
||||
10: "Président⸱e",
|
||||
9: "Vice-Président⸱e",
|
||||
7: "Trésorier⸱e",
|
||||
5: "Responsable communication",
|
||||
4: "Secrétaire",
|
||||
3: "Responsable info",
|
||||
2: "Membre du bureau",
|
||||
1: "Membre actif⸱ve",
|
||||
0: "Curieux⸱euse",
|
||||
}
|
||||
|
||||
|
||||
def migrate_roles(apps: StateApps, schema_editor):
|
||||
ClubRole = apps.get_model("club", "ClubRole")
|
||||
Membership = apps.get_model("club", "Membership")
|
||||
|
||||
translation.activate("fr")
|
||||
|
||||
updates = []
|
||||
presidency = settings.SITH_CLUB_ROLES_ID["President"]
|
||||
for club_id, role in Membership.objects.values_list("club", "role").distinct():
|
||||
new_role = ClubRole.objects.create(
|
||||
name=SITH_CLUB_ROLES[role],
|
||||
is_board=role > settings.SITH_MAXIMUM_FREE_ROLE,
|
||||
is_presidency=role == presidency,
|
||||
is_presidency=role == PRESIDENT_ROLE,
|
||||
club_id=club_id,
|
||||
order=presidency - role,
|
||||
order=PRESIDENT_ROLE - role,
|
||||
)
|
||||
updates.append(When(role=role, then=new_role.id))
|
||||
# all updates must happen at the same time
|
||||
|
||||
@@ -138,9 +138,7 @@ class Club(models.Model):
|
||||
@cached_property
|
||||
def president(self) -> Membership | None:
|
||||
"""Fetch the membership of the current president of this club."""
|
||||
return self.members.filter(
|
||||
role=settings.SITH_CLUB_ROLES_ID["President"], end_date=None
|
||||
).first()
|
||||
return self.members.filter(end_date=None).order_by("role__order").first()
|
||||
|
||||
def check_loop(self):
|
||||
"""Raise a validation error when a loop is found within the parent list."""
|
||||
@@ -208,7 +206,9 @@ class Club(models.Model):
|
||||
|
||||
@cached_property
|
||||
def current_members(self) -> list[Membership]:
|
||||
return list(self.members.ongoing().select_related("user").order_by("-role"))
|
||||
return list(
|
||||
self.members.ongoing().select_related("user", "role").order_by("-role")
|
||||
)
|
||||
|
||||
def get_membership_for(self, user: User) -> Membership | None:
|
||||
"""Return the current membership of the given user."""
|
||||
@@ -256,7 +256,7 @@ class ClubRole(OrderedModel):
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} - {self.club.name}"
|
||||
return self.name
|
||||
|
||||
def get_display_name(self):
|
||||
return f"{self.name} - {self.club.name}"
|
||||
@@ -265,14 +265,29 @@ class ClubRole(OrderedModel):
|
||||
return reverse("club:club_roles", kwargs={"club_id": self.club_id})
|
||||
|
||||
def clean(self):
|
||||
errors = []
|
||||
if self.is_presidency and not self.is_board:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Role %(name)s was declared as a presidency role "
|
||||
"without being a board role"
|
||||
errors.append(
|
||||
ValidationError(
|
||||
_(
|
||||
"Role %(name)s was declared as a presidency role "
|
||||
"without being a board role"
|
||||
)
|
||||
% {"name": self.name}
|
||||
)
|
||||
% {"name": self.name}
|
||||
)
|
||||
if (
|
||||
self.is_board
|
||||
and self.club.roles.filter(is_board=False, order__lt=self.order).exists()
|
||||
):
|
||||
errors.append(
|
||||
ValidationError(
|
||||
_("Board role %(role)s cannot be placed below a member role")
|
||||
% {"role": self.name}
|
||||
)
|
||||
)
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
return super().clean()
|
||||
|
||||
|
||||
@@ -321,7 +336,7 @@ class MembershipQuerySet(models.QuerySet):
|
||||
user=user,
|
||||
club=OuterRef("club"),
|
||||
role__is_board=True,
|
||||
role__order__gt=OuterRef("role__order"),
|
||||
role__order__lt=OuterRef("role__order"),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
{% for m in members %}
|
||||
<tr>
|
||||
<td>{{ user_profile_link(m.user) }}</td>
|
||||
<td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
|
||||
<td>{{ m.role.name }}</td>
|
||||
<td>{{ m.description }}</td>
|
||||
<td>{{ m.start_date }}</td>
|
||||
{%- if can_end_membership -%}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{% for member in old_members %}
|
||||
<tr>
|
||||
<td>{{ user_profile_link(member.user) }}</td>
|
||||
<td>{{ settings.SITH_CLUB_ROLES[member.role] }}</td>
|
||||
<td>{{ member.role.name }}</td>
|
||||
<td>{{ member.description }}</td>
|
||||
<td>{{ member.start_date }}</td>
|
||||
<td>{{ member.end_date }}</td>
|
||||
|
||||
@@ -9,7 +9,7 @@ from model_bakery import baker
|
||||
from model_bakery.recipe import Recipe
|
||||
from pytest_django.asserts import assertNumQueries
|
||||
|
||||
from club.models import Club, Membership, ClubRole
|
||||
from club.models import Club, ClubRole, Membership
|
||||
from core.baker_recipes import subscriber_user
|
||||
from core.models import Group, Page, User
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import csv
|
||||
import itertools
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError
|
||||
@@ -318,7 +317,7 @@ class ClubMembersView(
|
||||
membership = self.object.get_membership_for(self.request.user)
|
||||
if (
|
||||
membership
|
||||
and membership.role <= settings.SITH_MAXIMUM_FREE_ROLE
|
||||
and not membership.role.is_board
|
||||
and not self.request.user.has_perm("club.add_membership")
|
||||
):
|
||||
# Simple club members won't see the form anymore.
|
||||
@@ -343,8 +342,8 @@ class ClubMembersView(
|
||||
kwargs["members"] = list(
|
||||
self.object.members.ongoing()
|
||||
.annotate(is_editable=Q(id__in=editable))
|
||||
.order_by("-role")
|
||||
.select_related("user")
|
||||
.order_by("role__order")
|
||||
.select_related("user", "role")
|
||||
)
|
||||
kwargs["can_end_membership"] = len(editable) > 0
|
||||
return kwargs
|
||||
@@ -372,8 +371,8 @@ class ClubOldMembersView(ClubTabsMixin, PermissionRequiredMixin, DetailView):
|
||||
return super().get_context_data(**kwargs) | {
|
||||
"old_members": (
|
||||
self.object.members.exclude(end_date=None)
|
||||
.order_by("-role", "description", "-end_date")
|
||||
.select_related("user")
|
||||
.order_by("role__order", "description", "-end_date")
|
||||
.select_related("user", "role")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -724,9 +723,7 @@ class MailingAutoGenerationView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
club = self.mailing.club
|
||||
self.mailing.subscriptions.all().delete()
|
||||
members = club.members.filter(
|
||||
role__gte=settings.SITH_CLUB_ROLES_ID["Board member"]
|
||||
).exclude(end_date__lte=timezone.now())
|
||||
members = club.members.ongoing().filter(role__is_board=True)
|
||||
for member in members.all():
|
||||
MailingSubscription(user=member.user, mailing=self.mailing).save()
|
||||
return redirect("club:mailing", club_id=club.id)
|
||||
|
||||
Reference in New Issue
Block a user