From f502a538c266ce800f228b099a843db19a1b39ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 16 Mar 2016 11:00:12 +0100 Subject: [PATCH] Add 'max_memberships', 'total_memberships' and 'can_is_private_be_updated' to the project serializer --- taiga/projects/api.py | 1 + taiga/projects/serializers.py | 13 ++ taiga/projects/services/__init__.py | 2 + taiga/projects/services/members.py | 49 +++++++ tests/integration/test_projects.py | 192 ++++++++++++++++++++++++++++ 5 files changed, 257 insertions(+) diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 41c5268e..43388afa 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -109,6 +109,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, # Prefetch doesn"t work correctly if then if the field is filtered later (it generates more queries) # so we add some custom prefetching qs = qs.prefetch_related("members") + qs = qs.prefetch_related("memberships") qs = qs.prefetch_related(Prefetch("notify_policies", NotifyPolicy.objects.exclude(notify_level=NotifyLevel.none), to_attr="valid_notify_policies")) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index e892f93e..28e3593c 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -352,11 +352,24 @@ class ProjectDetailSerializer(ProjectSerializer): class ProjectDetailAdminSerializer(ProjectDetailSerializer): + max_memberships = serializers.SerializerMethodField(method_name="get_max_memberships") + total_memberships = serializers.SerializerMethodField(method_name="get_total_memberships") + can_is_private_be_updated = serializers.SerializerMethodField(method_name="get_can_is_private_be_updated") + class Meta: model = models.Project read_only_fields = ("created_date", "modified_date", "slug", "blocked_code") exclude = ("logo", "last_us_ref", "last_task_ref", "last_issue_ref") + def get_max_memberships(self, obj): + return services.get_max_memberships_for_project(obj) + + def get_total_memberships(self, obj): + return services.get_total_project_memberships(obj) + + def get_can_is_private_be_updated(self, obj): + return services.check_if_project_privacity_can_be_changed(obj) + ###################################################### ## Liked diff --git a/taiga/projects/services/__init__.py b/taiga/projects/services/__init__.py index c50e8386..6227cc54 100644 --- a/taiga/projects/services/__init__.py +++ b/taiga/projects/services/__init__.py @@ -38,6 +38,8 @@ from .logo import get_logo_big_thumbnail_url from .members import create_members_in_bulk from .members import get_members_from_bulk from .members import remove_user_from_project, project_has_valid_admins, can_user_leave_project +from .members import get_max_memberships_for_project, get_total_project_memberships +from .members import check_if_project_privacity_can_be_changed from .modules_config import get_modules_config diff --git a/taiga/projects/services/members.py b/taiga/projects/services/members.py index 4d6ed0ec..a141f8e7 100644 --- a/taiga/projects/services/members.py +++ b/taiga/projects/services/members.py @@ -60,3 +60,52 @@ def can_user_leave_project(user, project): return False return True + + +def get_max_memberships_for_project(project): + """Return tha maximun of membersh for a concrete project. + + :param project: A project object. + + :return: a number or null. + """ + if project.is_private: + return project.owner.max_memberships_private_projects + return project.owner.max_memberships_public_projects + + +def get_total_project_memberships(project): + """Return tha total of memberships of a project (members and unaccepted invitations). + + :param project: A project object. + + :return: a number. + """ + return project.memberships.count() + + +def check_if_project_privacity_can_be_changed(project): + """Return if the project privacity can be changed from private to public or viceversa. + + :param project: A project object. + + :return: True if it can be changed or False if can't. + """ + if project.is_private: + current_projects = project.owner.owned_projects.filter(is_private=False).count() + max_projects = project.owner.max_public_projects + max_memberships = project.owner.max_memberships_public_projects + else: + current_projects = project.owner.owned_projects.filter(is_private=True).count() + max_projects = project.owner.max_private_projects + max_memberships = project.owner.max_memberships_private_projects + + if max_projects is not None and current_projects >= max_projects: + return False + + current_memberships = project.memberships.count() + + if max_memberships is not None and current_memberships > max_memberships: + return False + + return True diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index e8db7810..77592abc 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -1393,3 +1393,195 @@ def test_project_transfer_validate_token_from_admin_member_with_valid_token(clie response = client.json.post(url, json.dumps(data)) assert response.status_code == 200 + + +#################################################################################### +# Test taiga.projects.services.members.check_if_project_privacity_can_be_changed +#################################################################################### + +from taiga.projects.services import check_if_project_privacity_can_be_changed + +# private to public + +def test_private_project_cant_be_public_because_owner_doesnt_have_enought_slot_and_too_much_members(client): + project = f.create_project(is_private=True) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_public_projects = 0 + project.owner.max_memberships_public_projects = 3 + + assert check_if_project_privacity_can_be_changed(project) == False + + +def test_private_project_cant_be_public_because_owner_doesnt_have_enought_slot(client): + project = f.create_project(is_private=True) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_public_projects = 0 + project.owner.max_memberships_public_projects = 6 + + assert check_if_project_privacity_can_be_changed(project) == False + + +def test_private_project_cant_be_public_because_too_much_members(client): + project = f.create_project(is_private=True) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_public_projects = 2 + project.owner.max_memberships_public_projects = 3 + + assert check_if_project_privacity_can_be_changed(project) == False + + +def test_private_project_can_be_public_because_owner_has_enought_slot_and_project_has_enought_members(client): + project = f.create_project(is_private=True) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_public_projects = 2 + project.owner.max_memberships_public_projects = 6 + + assert check_if_project_privacity_can_be_changed(project) == True + + +def test_private_project_can_be_public_because_owner_has_unlimited_slot_and_project_has_unlimited_members(client): + project = f.create_project(is_private=True) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_public_projects = None + project.owner.max_memberships_public_projects = None + + assert check_if_project_privacity_can_be_changed(project) == True + + +def test_private_project_can_be_public_because_owner_has_unlimited_slot(client): + project = f.create_project(is_private=True) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_public_projects = None + project.owner.max_memberships_public_projects = 6 + + assert check_if_project_privacity_can_be_changed(project) == True + + +def test_private_project_can_be_public_because_project_has_unlimited_members(client): + project = f.create_project(is_private=True) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_public_projects = 2 + project.owner.max_memberships_public_projects = None + + assert check_if_project_privacity_can_be_changed(project) == True + + +# public to private + +def test_public_project_cant_be_private_because_owner_doesnt_have_enought_slot_and_too_much_members(client): + project = f.create_project(is_private=False) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_private_projects = 0 + project.owner.max_memberships_private_projects = 3 + + assert check_if_project_privacity_can_be_changed(project) == False + + +def test_public_project_cant_be_private_because_owner_doesnt_have_enought_slot(client): + project = f.create_project(is_private=False) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_private_projects = 0 + project.owner.max_memberships_private_projects = 6 + + assert check_if_project_privacity_can_be_changed(project) == False + + +def test_public_project_cant_be_private_because_too_much_members(client): + project = f.create_project(is_private=False) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_private_projects = 2 + project.owner.max_memberships_private_projects = 3 + + assert check_if_project_privacity_can_be_changed(project) == False + + +def test_public_project_can_be_private_because_owner_has_enought_slot_and_project_has_enought_members(client): + project = f.create_project(is_private=False) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_private_projects = 2 + project.owner.max_memberships_private_projects = 6 + + assert check_if_project_privacity_can_be_changed(project) == True + + +def test_public_project_can_be_private_because_owner_has_unlimited_slot_and_project_has_unlimited_members(client): + project = f.create_project(is_private=False) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_private_projects = None + project.owner.max_memberships_private_projects = None + + assert check_if_project_privacity_can_be_changed(project) == True + + +def test_public_project_can_be_private_because_owner_has_unlimited_slot(client): + project = f.create_project(is_private=False) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_private_projects = None + project.owner.max_memberships_private_projects = 6 + + assert check_if_project_privacity_can_be_changed(project) == True + + +def test_public_project_can_be_private_because_project_has_unlimited_members(client): + project = f.create_project(is_private=False) + f.MembershipFactory(project=project, user=project.owner) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + f.MembershipFactory(project=project) + + project.owner.max_private_projects = 2 + project.owner.max_memberships_private_projects = None + + assert check_if_project_privacity_can_be_changed(project) == True