diff --git a/taiga/base/filters.py b/taiga/base/filters.py index fa4b55e8..415d8b4c 100644 --- a/taiga/base/filters.py +++ b/taiga/base/filters.py @@ -452,11 +452,15 @@ class AssignedToFilter(BaseRelatedFieldsFilter): class AssignedUsersFilter(FilterModelAssignedUsers, BaseRelatedFieldsFilter): filter_name = 'assigned_users' + exclude_param_name = 'exclude_assigned_users' def _get_queryparams(self, params, mode=''): - param_name = self.param_name or self.filter_name - raw_value = params.get(param_name, None) + if mode == 'exclude': + param_name = self.exclude_param_name + else: + param_name = self.param_name or self.filter_name + raw_value = params.get(param_name, None) if raw_value: value = self._prepare_filter_data(raw_value) UserStoryModel = apps.get_model("userstories", "UserStory") @@ -699,20 +703,27 @@ class RoleFilter(BaseRelatedFieldsFilter): class UserStoriesRoleFilter(FilterModelAssignedUsers, BaseRelatedFieldsFilter): filter_name = "role_id" param_name = "role" + exclude_param_name = 'exclude_role' def filter_queryset(self, request, queryset, view): Membership = apps.get_model('projects', 'Membership') - query = self._get_queryparams(request.QUERY_PARAMS) - if query: + operations = { + "filter": queryset.filter, + "exclude": queryset.exclude, + } + + for mode, qs_method in operations.items(): + query = self._get_queryparams(request.QUERY_PARAMS, mode=mode) + if not query: + continue + if isinstance(query, dict): memberships = Membership.objects.filter(**query).values_list("user_id", flat=True) else: memberships = Membership.objects.filter(query).values_list("user_id", flat=True) if memberships: user_story_model = apps.get_model("userstories", "UserStory") - queryset = queryset.filter( - self.get_assigned_users_filter(user_story_model, memberships) - ) + queryset = qs_method(self.get_assigned_users_filter(user_story_model, memberships)) return FilterBackend.filter_queryset(self, request, queryset, view) diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 78f44a65..a97928ec 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -38,6 +38,71 @@ import pytest pytestmark = pytest.mark.django_db(transaction=True) +def create_uss_fixtures(): + data = {} + + data["project"] = f.ProjectFactory.create() + project = data["project"] + data["users"] = [f.UserFactory.create(is_superuser=True) for i in range(0, 3)] + data["roles"] = [f.RoleFactory.create() for i in range(0, 3)] + user_roles = zip(data["users"], data["roles"]) + # Add membership fixtures + [f.MembershipFactory.create(user=user, project=project, role=role) for (user, role) in user_roles] + + data["statuses"] = [f.UserStoryStatusFactory.create(project=project) for i in range(0, 4)] + data["epics"] = [f.EpicFactory.create(project=project) for i in range(0, 3)] + data["tags"] = ["test1test2test3", "test1", "test2", "test3"] + + # ---------------------------------------------------------------------------------------------------- + # | US | Status | Owner | Assigned To | Assigned Users | Tags | Epic | + # |-------#---------#--------#-------------#---------------------#---------------------#-------------- + # | 0 | status3 | user2 | None | None | tag1 | epic0 | + # | 1 | status3 | user1 | None | user1 | tag2 | None | + # | 2 | status1 | user3 | None | None | tag1 tag2 | epic1 | + # | 3 | status0 | user2 | None | None | tag3 | None | + # | 4 | status0 | user1 | user1 | None | tag1 tag2 tag3 | epic0 | + # | 5 | status2 | user3 | user1 | None | tag3 | None | + # | 6 | status3 | user2 | user1 | None | tag1 tag2 | epic0 epic2 | + # | 7 | status0 | user1 | user2 | None | tag3 | None | + # | 8 | status3 | user3 | user2 | None | tag1 | epic2 | + # | 9 | status1 | user2 | user3 | user1 | tag0 | None | + # ---------------------------------------------------------------------------------------------------- + + (user1, user2, user3, ) = data["users"] + (status0, status1, status2, status3 ) = data["statuses"] + (epic0, epic1, epic2) = data["epics"] + (tag0, tag1, tag2, tag3, ) = data["tags"] + + us0 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None, + status=status3, tags=[tag1]) + f.RelatedUserStory.create(user_story=us0, epic=epic0) + us1 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=None, + status=status3, tags=[tag2], assigned_users=[user1]) + us2 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=None, + status=status1, tags=[tag1, tag2]) + f.RelatedUserStory.create(user_story=us2, epic=epic1) + us3 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None, + status=status0, tags=[tag3]) + us4 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user1, + status=status0, tags=[tag1, tag2, tag3]) + f.RelatedUserStory.create(user_story=us4, epic=epic0) + us5 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user1, + status=status2, tags=[tag3]) + us6 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user1, + status=status3, tags=[tag1, tag2]) + f.RelatedUserStory.create(user_story=us6, epic=epic0) + f.RelatedUserStory.create(user_story=us6, epic=epic2) + us7 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user2, + status=status0, tags=[tag3]) + us8 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user2, + status=status3, tags=[tag1]) + f.RelatedUserStory.create(user_story=us8, epic=epic2) + us9 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user3, + status=status1, tags=[tag0], assigned_users=[user1]) + + return data + + def test_get_userstories_from_bulk(): data = "User Story #1\nUser Story #2\n" userstories = services.get_userstories_from_bulk(data) @@ -777,72 +842,14 @@ def test_api_filter_by_milestone__estimated_start_and_end(client, field_name): def test_api_filters_data(client): - project = f.ProjectFactory.create() - user1 = f.UserFactory.create(is_superuser=True) - f.MembershipFactory.create(user=user1, project=project) - user2 = f.UserFactory.create(is_superuser=True) - f.MembershipFactory.create(user=user2, project=project) - user3 = f.UserFactory.create(is_superuser=True) - f.MembershipFactory.create(user=user3, project=project) - - status0 = f.UserStoryStatusFactory.create(project=project) - status1 = f.UserStoryStatusFactory.create(project=project) - status2 = f.UserStoryStatusFactory.create(project=project) - status3 = f.UserStoryStatusFactory.create(project=project) - - epic0 = f.EpicFactory.create(project=project) - epic1 = f.EpicFactory.create(project=project) - epic2 = f.EpicFactory.create(project=project) - - tag0 = "test1test2test3" - tag1 = "test1" - tag2 = "test2" - tag3 = "test3" - - # ---------------------------------------------------------------------------------------------------- - # | US | Status | Owner | Assigned To | Assigned Users | Tags | Epic | - # |-------#---------#--------#-------------#---------------------#---------------------#-------------- - # | 0 | status3 | user2 | None | None | tag1 | epic0 | - # | 1 | status3 | user1 | None | user1 | tag2 | None | - # | 2 | status1 | user3 | None | None | tag1 tag2 | epic1 | - # | 3 | status0 | user2 | None | None | tag3 | None | - # | 4 | status0 | user1 | user1 | None | tag1 tag2 tag3 | epic0 | - # | 5 | status2 | user3 | user1 | None | tag3 | None | - # | 6 | status3 | user2 | user1 | None | tag1 tag2 | epic0 epic2 | - # | 7 | status0 | user1 | user2 | None | tag3 | None | - # | 8 | status3 | user3 | user2 | None | tag1 | epic2 | - # | 9 | status1 | user2 | user3 | user1 | tag0 | None | - # ---------------------------------------------------------------------------------------------------- - - us0 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None, - status=status3, tags=[tag1]) - f.RelatedUserStory.create(user_story=us0, epic=epic0) - us1 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=None, - status=status3, tags=[tag2], assigned_users=[user1]) - us2 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=None, - status=status1, tags=[tag1, tag2]) - f.RelatedUserStory.create(user_story=us2, epic=epic1) - us3 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None, - status=status0, tags=[tag3]) - us4 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user1, - status=status0, tags=[tag1, tag2, tag3]) - f.RelatedUserStory.create(user_story=us4, epic=epic0) - us5 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user1, - status=status2, tags=[tag3]) - us6 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user1, - status=status3, tags=[tag1, tag2]) - f.RelatedUserStory.create(user_story=us6, epic=epic0) - f.RelatedUserStory.create(user_story=us6, epic=epic2) - us7 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user2, - status=status0, tags=[tag3]) - us8 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user2, - status=status3, tags=[tag1]) - f.RelatedUserStory.create(user_story=us8, epic=epic2) - us9 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user3, - status=status1, tags=[tag0], assigned_users=[user1]) + data = create_uss_fixtures() + project = data["project"] + (user1, user2, user3, ) = data["users"] + (status0, status1, status2, status3, ) = data["statuses"] + (epic0, epic1, epic2, ) = data["epics"] + (tag0, tag1, tag2, tag3, ) = data["tags"] url = reverse("userstories-filters-data") + "?project={}".format(project.id) - client.login(user1) # No filter @@ -961,6 +968,37 @@ def test_api_filters_data(client): assert next(filter(lambda i: i['id'] == epic2.id, response.data["epics"]))["count"] == 2 +@pytest.mark.parametrize("filter_name,collection,expected,exclude_expected,is_text", [ + ('status', 'statuses', 3, 7, False), + ('tags', 'tags', 1, 9, True), + ('owner', 'users', 3, 7, False), + ('role', 'roles', 5, 5, False), + ('assigned_users', 'users', 5, 5, False), +]) +def test_api_filters(client, filter_name, collection, expected, exclude_expected, is_text): + data = create_uss_fixtures() + project = data["project"] + options = data[collection] + + client.login(data["users"][0]) + if is_text: + param = options[0] + else: + param = options[0].id + + # include test + url = f"{reverse('userstories-list')}?project={project.id}&{filter_name}={param}" + response = client.get(url) + assert response.status_code == 200 + assert len(response.data) == expected + + # exclude test + url = f"{reverse('userstories-list')}?project={project.id}&exclude_{filter_name}={param}" + response = client.get(url) + assert response.status_code == 200 + assert len(response.data) == exclude_expected + + def test_api_filters_data_with_assigned_users(client): project = f.ProjectFactory.create() user1 = f.UserFactory.create(is_superuser=True)