mirror of
https://github.com/ae-utbm/sith.git
synced 2026-04-20 02:08:22 +00:00
create default club roles on club creation
This commit is contained in:
@@ -13,6 +13,8 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.forms.models import ModelForm
|
||||||
|
from django.http import HttpRequest
|
||||||
|
|
||||||
from club.models import Club, ClubRole, Membership
|
from club.models import Club, ClubRole, Membership
|
||||||
|
|
||||||
@@ -29,6 +31,17 @@ class ClubAdmin(admin.ModelAdmin):
|
|||||||
"page",
|
"page",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def save_model(
|
||||||
|
self,
|
||||||
|
request: HttpRequest,
|
||||||
|
obj: Club,
|
||||||
|
form: ModelForm,
|
||||||
|
change: bool, # noqa: FBT001
|
||||||
|
):
|
||||||
|
super().save_model(request, obj, form, change)
|
||||||
|
if not change:
|
||||||
|
obj.create_default_roles()
|
||||||
|
|
||||||
|
|
||||||
@admin.register(ClubRole)
|
@admin.register(ClubRole)
|
||||||
class ClubRoleAdmin(admin.ModelAdmin):
|
class ClubRoleAdmin(admin.ModelAdmin):
|
||||||
|
|||||||
@@ -121,6 +121,26 @@ class Migration(migrations.Migration):
|
|||||||
"verbose_name_plural": "club roles",
|
"verbose_name_plural": "club roles",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="club",
|
||||||
|
name="board_group",
|
||||||
|
field=models.OneToOneField(
|
||||||
|
editable=False,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="club_board",
|
||||||
|
to="core.group",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="club",
|
||||||
|
name="members_group",
|
||||||
|
field=models.OneToOneField(
|
||||||
|
editable=False,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="club",
|
||||||
|
to="core.group",
|
||||||
|
),
|
||||||
|
),
|
||||||
migrations.AddConstraint(
|
migrations.AddConstraint(
|
||||||
model_name="clubrole",
|
model_name="clubrole",
|
||||||
constraint=models.CheckConstraint(
|
constraint=models.CheckConstraint(
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from typing import Iterable, Self
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from django.core.validators import RegexValidator, validate_email
|
from django.core.validators import RegexValidator, validate_email
|
||||||
from django.db import models, transaction
|
from django.db import ProgrammingError, models, transaction
|
||||||
from django.db.models import Exists, F, OuterRef, Q
|
from django.db.models import Exists, F, OuterRef, Q
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -92,10 +92,10 @@ class Club(models.Model):
|
|||||||
Page, related_name="club", blank=True, on_delete=models.PROTECT
|
Page, related_name="club", blank=True, on_delete=models.PROTECT
|
||||||
)
|
)
|
||||||
members_group = models.OneToOneField(
|
members_group = models.OneToOneField(
|
||||||
Group, related_name="club", on_delete=models.PROTECT
|
Group, related_name="club", on_delete=models.PROTECT, editable=False
|
||||||
)
|
)
|
||||||
board_group = models.OneToOneField(
|
board_group = models.OneToOneField(
|
||||||
Group, related_name="club_board", on_delete=models.PROTECT
|
Group, related_name="club_board", on_delete=models.PROTECT, editable=False
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = ClubQuerySet.as_manager()
|
objects = ClubQuerySet.as_manager()
|
||||||
@@ -183,6 +183,40 @@ class Club(models.Model):
|
|||||||
self.page.parent = self.parent.page
|
self.page.parent = self.parent.page
|
||||||
self.page.save(force_lock=True)
|
self.page.save(force_lock=True)
|
||||||
|
|
||||||
|
def create_default_roles(self):
|
||||||
|
"""Create some roles that should exist by default for this club.
|
||||||
|
|
||||||
|
The created roles are : president, treasurer, active member and curious.
|
||||||
|
|
||||||
|
Warnings:
|
||||||
|
When calling this method, no club must exist yet for this club.
|
||||||
|
"""
|
||||||
|
if self.roles.exists():
|
||||||
|
raise ProgrammingError(
|
||||||
|
"Default roles can be created only for clubs "
|
||||||
|
"that don't have associated roles yet"
|
||||||
|
)
|
||||||
|
# The names are written in French, because there is no gettext involved
|
||||||
|
# for strings stored in database, and the majority of users are french.
|
||||||
|
roles = [
|
||||||
|
ClubRole(name="Président⸱e", is_board=True, is_presidency=True),
|
||||||
|
ClubRole(name="Trésorier⸱e", is_board=True, is_presidency=False),
|
||||||
|
ClubRole(name="Membre actif⸱ve", is_board=False, is_presidency=False),
|
||||||
|
ClubRole(
|
||||||
|
name="Curieux⸱euse",
|
||||||
|
description=(
|
||||||
|
"Les gens qui suivent l'activité "
|
||||||
|
"du club sans forcément y participer"
|
||||||
|
),
|
||||||
|
is_board=False,
|
||||||
|
is_presidency=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
for i, role in enumerate(roles):
|
||||||
|
role.club = self
|
||||||
|
role.order = i
|
||||||
|
ClubRole.objects.bulk_create(roles)
|
||||||
|
|
||||||
def delete(self, *args, **kwargs) -> tuple[int, dict[str, int]]:
|
def delete(self, *args, **kwargs) -> tuple[int, dict[str, int]]:
|
||||||
self.board_group.delete()
|
self.board_group.delete()
|
||||||
self.members_group.delete()
|
self.members_group.delete()
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import ProgrammingError
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.timezone import localdate
|
from django.utils.timezone import localdate
|
||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
from model_bakery.recipe import Recipe
|
from model_bakery.recipe import Recipe
|
||||||
|
from pytest_django.asserts import assertRedirects
|
||||||
|
|
||||||
from club.models import Club, ClubRole, Membership
|
from club.models import Club, ClubRole, Membership
|
||||||
from core.baker_recipes import subscriber_user
|
from core.baker_recipes import subscriber_user
|
||||||
@@ -47,3 +50,78 @@ def test_club_list(client: Client, nb_additional_clubs: int, is_fragment):
|
|||||||
headers = {"HX-Request": True} if is_fragment else {}
|
headers = {"HX-Request": True} if is_fragment else {}
|
||||||
res = client.get(reverse("club:club_list"), headers=headers)
|
res = client.get(reverse("club:club_list"), headers=headers)
|
||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def assert_club_created(club_name: str):
|
||||||
|
club = Club.objects.last()
|
||||||
|
assert club.name == club_name
|
||||||
|
assert club.board_group.name == f"{club_name} - Bureau"
|
||||||
|
assert club.members_group.name == f"{club_name} - Membres"
|
||||||
|
# default roles should be added on club creation,
|
||||||
|
# whether the creation happens on the admin site or on the user site
|
||||||
|
assert list(club.roles.values("name", "is_presidency", "is_board")) == [
|
||||||
|
{"name": "Président⸱e", "is_presidency": True, "is_board": True},
|
||||||
|
{"name": "Trésorier⸱e", "is_presidency": False, "is_board": True},
|
||||||
|
{"name": "Membre actif⸱ve", "is_presidency": False, "is_board": False},
|
||||||
|
{"name": "Curieux⸱euse", "is_presidency": False, "is_board": False},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_create_view(admin_client: Client):
|
||||||
|
"""Test that the club creation view works well"""
|
||||||
|
res = admin_client.get(reverse("club:club_new"))
|
||||||
|
assert res.status_code == 200
|
||||||
|
res = admin_client.post(
|
||||||
|
reverse("club:club_new"),
|
||||||
|
data={"name": "foo", "parent": settings.SITH_MAIN_CLUB_ID},
|
||||||
|
)
|
||||||
|
club = Club.objects.last()
|
||||||
|
assertRedirects(res, club.get_absolute_url())
|
||||||
|
assert_club_created("foo")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_default_roles_for_club_with_roles_fails():
|
||||||
|
"""Test that an Error is raised if trying to create
|
||||||
|
default roles for a club that already has roles.
|
||||||
|
"""
|
||||||
|
club = baker.make(Club)
|
||||||
|
baker.make(ClubRole, club=club)
|
||||||
|
with pytest.raises(ProgrammingError):
|
||||||
|
club.create_default_roles()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestAdminInterface:
|
||||||
|
def test_create(self, admin_client: Client):
|
||||||
|
"""Test the creation of a club via the admin interface."""
|
||||||
|
res = admin_client.post(
|
||||||
|
reverse("admin:club_club_add"),
|
||||||
|
data={
|
||||||
|
"name": "foo",
|
||||||
|
"parent": settings.SITH_MAIN_CLUB_ID,
|
||||||
|
"address": "Rome",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assertRedirects(res, reverse("admin:club_club_changelist"))
|
||||||
|
assert_club_created("foo")
|
||||||
|
|
||||||
|
def test_change(self, admin_client: Client):
|
||||||
|
"""Test the edition of a club via the admin interface."""
|
||||||
|
club = baker.make(Club)
|
||||||
|
res = admin_client.post(
|
||||||
|
reverse("admin:club_club_change", kwargs={"object_id": club.id}),
|
||||||
|
data={
|
||||||
|
"name": "foo",
|
||||||
|
"page": club.page_id,
|
||||||
|
"home": club.home_id,
|
||||||
|
"address": club.address,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assertRedirects(res, reverse("admin:club_club_changelist"))
|
||||||
|
club.refresh_from_db()
|
||||||
|
assert club.name == "foo"
|
||||||
|
# Club roles shouldn't be modified when editing the club on the admin interface
|
||||||
|
# This club had no roles beforehand, therefore it shouldn't have roles now.
|
||||||
|
assert not club.roles.exists()
|
||||||
|
|||||||
@@ -580,6 +580,11 @@ class ClubCreateView(PermissionRequiredMixin, CreateView):
|
|||||||
template_name = "core/create.jinja"
|
template_name = "core/create.jinja"
|
||||||
permission_required = "club.add_club"
|
permission_required = "club.add_club"
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
res = super().form_valid(form)
|
||||||
|
self.object.create_default_roles()
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
class MembershipSetOldView(CanEditMixin, SingleObjectMixin, View):
|
class MembershipSetOldView(CanEditMixin, SingleObjectMixin, View):
|
||||||
"""Set a membership as being old."""
|
"""Set a membership as being old."""
|
||||||
|
|||||||
Reference in New Issue
Block a user