diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 9c00901c..958ed3b3 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -405,6 +405,67 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, services.reject_project_transfer(project, request.user, token, reason) return response.Ok() + @detail_route(methods=["POST"]) + def create_tag(self, request, pk=None): + project = self.get_object() + self.check_permissions(request, "create_tag", project) + self._raise_if_blocked(project) + serializer = serializers.CreateTagSerializer(data=request.DATA, project=project) + if not serializer.is_valid(): + return response.BadRequest(serializer.errors) + + data = serializer.data + services.create_tag(project, data.get("tag"), data.get("color")) + return response.Ok() + + + @detail_route(methods=["POST"]) + def edit_tag(self, request, pk=None): + project = self.get_object() + self.check_permissions(request, "edit_tag", project) + self._raise_if_blocked(project) + serializer = serializers.EditTagTagSerializer(data=request.DATA, project=project) + if not serializer.is_valid(): + return response.BadRequest(serializer.errors) + + data = serializer.data + services.edit_tag(project, data.get("from_tag"), + to_tag=data.get("to_tag", None), + color=data.get("color", None)) + + return response.Ok() + + + @detail_route(methods=["POST"]) + def delete_tag(self, request, pk=None): + project = self.get_object() + self.check_permissions(request, "delete_tag", project) + self._raise_if_blocked(project) + serializer = serializers.DeleteTagSerializer(data=request.DATA, project=project) + if not serializer.is_valid(): + return response.BadRequest(serializer.errors) + + data = serializer.data + services.delete_tag(project, data.get("tag")) + return response.Ok() + + @detail_route(methods=["POST"]) + def mix_tags(self, request, pk=None): + project = self.get_object() + self.check_permissions(request, "mix_tags", project) + self._raise_if_blocked(project) + serializer = serializers.MixTagsSerializer(data=request.DATA, project=project) + if not serializer.is_valid(): + return response.BadRequest(serializer.errors) + + data = serializer.data + services.mix_tags(project, data.get("from_tags"), data.get("to_tag")) + return response.Ok() + + def _raise_if_blocked(self, project): + if self.is_blocked(project): + raise exc.Blocked(_("Blocked element")) + def _set_base_permissions(self, obj): update_permissions = False if not obj.id: diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index c43e842f..0cc95427 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -78,6 +78,10 @@ class ProjectPermission(TaigaResourcePermission): transfer_start_perms = IsObjectOwner() transfer_reject_perms = IsAuthenticated() & HasProjectPerm('view_project') transfer_accept_perms = IsAuthenticated() & HasProjectPerm('view_project') + create_tag_perms = IsProjectAdmin() + edit_tag_perms = IsProjectAdmin() + delete_tag_perms = IsProjectAdmin() + mix_tags_perms = IsProjectAdmin() class ProjectFansPermission(TaigaResourcePermission): diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 1b271590..f7403389 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import re from django.utils.translation import ugettext as _ from django.db.models import Q @@ -256,7 +257,7 @@ class ProjectSerializer(FanResourceSerializerMixin, WatchedResourceModelSerializ i_am_member = serializers.SerializerMethodField("get_i_am_member") tags = TagsField(default=[], required=False) - tags_colors = TagsColorsField(required=False) + tags_colors = TagsColorsField(required=False, read_only=True) notify_level = serializers.SerializerMethodField("get_notify_level") total_closed_milestones = serializers.SerializerMethodField("get_total_closed_milestones") @@ -416,3 +417,94 @@ class ProjectTemplateSerializer(serializers.ModelSerializer): class UpdateProjectOrderBulkSerializer(ProjectExistsValidator, serializers.Serializer): project_id = serializers.IntegerField() order = serializers.IntegerField() + + +###################################################### +## Project tags serializers +###################################################### + + +class ProjectTagSerializer(serializers.Serializer): + def __init__(self, *args, **kwargs): + # Don't pass the extra project arg + self.project = kwargs.pop("project") + + # Instantiate the superclass normally + super().__init__(*args, **kwargs) + + +class CreateTagSerializer(ProjectTagSerializer): + tag = serializers.CharField() + color = serializers.CharField(required=False) + + def validate_tag(self, attrs, source): + tag = attrs.get(source, None) + if services.tag_exist_for_project_elements(self.project, tag): + raise serializers.ValidationError(_("The tag exists.")) + + return attrs + + def validate_color(self, attrs, source): + color = attrs.get(source, None) + if not re.match('^\#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$', color): + raise serializers.ValidationError(_("The color is not a valid HEX color.")) + + return attrs + + +class EditTagTagSerializer(ProjectTagSerializer): + from_tag = serializers.CharField() + to_tag = serializers.CharField(required=False) + color = serializers.CharField(required=False) + + def validate_from_tag(self, attrs, source): + tag = attrs.get(source, None) + if not services.tag_exist_for_project_elements(self.project, tag): + raise serializers.ValidationError(_("The tag doesn't exist.")) + + return attrs + + def validate_to_tag(self, attrs, source): + tag = attrs.get(source, None) + if services.tag_exist_for_project_elements(self.project, tag): + raise serializers.ValidationError(_("The tag exists yet")) + + return attrs + + def validate_color(self, attrs, source): + color = attrs.get(source, None) + if len(color) != 7 or not re.match('^\#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$', color): + raise serializers.ValidationError(_("The color is not a valid HEX color.")) + + return attrs + + +class DeleteTagSerializer(ProjectTagSerializer): + tag = serializers.CharField() + + def validate_tag(self, attrs, source): + tag = attrs.get(source, None) + if not services.tag_exist_for_project_elements(self.project, tag): + raise serializers.ValidationError(_("The tag doesn't exist.")) + + return attrs + + +class MixTagsSerializer(ProjectTagSerializer): + from_tags = TagsField() + to_tag = serializers.CharField() + + def validate_from_tags(self, attrs, source): + tags = attrs.get(source, None) + for tag in tags: + if not services.tag_exist_for_project_elements(self.project, tag): + raise serializers.ValidationError(_("The tag doesn't exist.")) + + return attrs + + def validate_to_tag(self, attrs, source): + tag = attrs.get(source, None) + if not services.tag_exist_for_project_elements(self.project, tag): + raise serializers.ValidationError(_("The tag doesn't exist.")) + + return attrs diff --git a/taiga/projects/services/__init__.py b/taiga/projects/services/__init__.py index a115275b..f2fcc3c0 100644 --- a/taiga/projects/services/__init__.py +++ b/taiga/projects/services/__init__.py @@ -57,3 +57,6 @@ from .stats import get_member_stats_for_project from .transfer import request_project_transfer, start_project_transfer from .transfer import accept_project_transfer, reject_project_transfer + +from .tags import tag_exist_for_project_elements, create_tag +from .tags import edit_tag, delete_tag, mix_tags diff --git a/taiga/projects/services/tags.py b/taiga/projects/services/tags.py new file mode 100644 index 00000000..20e1946a --- /dev/null +++ b/taiga/projects/services/tags.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Jesús Espino +# Copyright (C) 2014-2016 David Barragán +# Copyright (C) 2014-2016 Alejandro Alonso +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.db import connection + +def tag_exist_for_project_elements(project, tag): + return tag in dict(project.tags_colors).keys() + + +def create_tag(project, tag, color): + project.tags_colors.append([tag, color]) + project.save() + + +def edit_tag(project, from_tag, to_tag=None, color=None): + tags_colors = dict(project.tags_colors) + + if color is not None: + tags_colors = dict(project.tags_colors) + tags_colors[from_tag] = color + + if to_tag is not None: + color = dict(project.tags_colors)[from_tag] + sql = """ + UPDATE userstories_userstory SET tags = array_distinct(array_replace(tags, '{from_tag}', '{to_tag}')) WHERE project_id={project_id}; + UPDATE tasks_task SET tags = array_distinct(array_replace(tags, '{from_tag}', '{to_tag}')) WHERE project_id={project_id}; + UPDATE issues_issue SET tags = array_distinct(array_replace(tags, '{from_tag}', '{to_tag}')) WHERE project_id={project_id}; + """ + sql = sql.format(project_id=project.id, from_tag=from_tag, to_tag=to_tag) + cursor = connection.cursor() + cursor.execute(sql) + + tags_colors[to_tag] = tags_colors.pop(from_tag) + + + project.tags_colors = list(tags_colors.items()) + project.save() + + +def rename_tag(project, from_tag, to_tag): + color = dict(project.tags_colors)[from_tag] + sql = """ + UPDATE userstories_userstory SET tags = array_distinct(array_replace(tags, '{from_tag}', '{to_tag}')) WHERE project_id={project_id}; + UPDATE tasks_task SET tags = array_distinct(array_replace(tags, '{from_tag}', '{to_tag}')) WHERE project_id={project_id}; + UPDATE issues_issue SET tags = array_distinct(array_replace(tags, '{from_tag}', '{to_tag}')) WHERE project_id={project_id}; + """ + sql = sql.format(project_id=project.id, from_tag=from_tag, to_tag=to_tag, color=color) + cursor = connection.cursor() + cursor.execute(sql) + + tags_colors = dict(project.tags_colors) + tags_colors[to_tag] = tags_colors.pop(from_tag) + project.tags_colors = list(tags_colors.items()) + project.save() + + +def delete_tag(project, tag): + sql = """ + UPDATE userstories_userstory SET tags = array_remove(tags, '{tag}') WHERE project_id={project_id}; + UPDATE tasks_task SET tags = array_remove(tags, '{tag}') WHERE project_id={project_id}; + UPDATE issues_issue SET tags = array_remove(tags, '{tag}') WHERE project_id={project_id}; + """ + sql = sql.format(project_id=project.id, tag=tag) + cursor = connection.cursor() + cursor.execute(sql) + + tags_colors = dict(project.tags_colors) + del tags_colors[tag] + project.tags_colors = list(tags_colors.items()) + project.save() + + +def mix_tags(project, from_tags, to_tag): + for from_tag in from_tags: + rename_tag(project, from_tag, to_tag) diff --git a/tests/integration/resources_permissions/test_projects_choices_resources.py b/tests/integration/resources_permissions/test_projects_choices_resources.py index 0115143a..2e95f731 100644 --- a/tests/integration/resources_permissions/test_projects_choices_resources.py +++ b/tests/integration/resources_permissions/test_projects_choices_resources.py @@ -27,20 +27,24 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=['view_project'], public_permissions=['view_project'], - owner=m.project_owner) + owner=m.project_owner, + tags_colors = [("tag1", "#123123"), ("tag2", "#456456"), ("tag3", "#111222")]) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=['view_project'], public_permissions=['view_project'], - owner=m.project_owner) + owner=m.project_owner, + tags_colors = [("tag1", "#123123"), ("tag2", "#456456"), ("tag3", "#111222")]) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], public_permissions=[], - owner=m.project_owner) + owner=m.project_owner, + tags_colors = [("tag1", "#123123"), ("tag2", "#456456"), ("tag3", "#111222")]) m.blocked_project = f.ProjectFactory(is_private=True, anon_permissions=[], public_permissions=[], owner=m.project_owner, - blocked_code=project_choices.BLOCKED_BY_STAFF) + blocked_code=project_choices.BLOCKED_BY_STAFF, + tags_colors = [("tag1", "#123123"), ("tag2", "#456456"), ("tag3", "#111222")]) m.public_membership = f.MembershipFactory(project=m.public_project, user=m.project_member_with_perms, @@ -1911,3 +1915,127 @@ def test_project_template_patch(client, data): results = helper_test_http_method(client, 'patch', url, '{"name": "Test"}', users) assert results == [401, 403, 200] + + +def test_create_tag(client, data): + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + post_data = json.dumps({ + "tag": "testtest", + "color": "#123123" + }) + + url = reverse('projects-create-tag', kwargs={"pk": data.public_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [401, 403, 403, 403, 200] + + url = reverse('projects-create-tag', kwargs={"pk": data.private_project1.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [401, 403, 403, 403, 200] + + url = reverse('projects-create-tag', kwargs={"pk": data.private_project2.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [404, 404, 404, 403, 200] + + url = reverse('projects-create-tag', kwargs={"pk": data.blocked_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [404, 404, 404, 403, 451] + + +def test_edit_tag(client, data): + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + post_data = json.dumps({ + "from_tag": "tag1", + "to_tag": "renamedtag1", + "color": "#123123" + }) + + url = reverse('projects-edit-tag', kwargs={"pk": data.public_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [401, 403, 403, 403, 200] + + url = reverse('projects-edit-tag', kwargs={"pk": data.private_project1.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [401, 403, 403, 403, 200] + + url = reverse('projects-edit-tag', kwargs={"pk": data.private_project2.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [404, 404, 404, 403, 200] + + url = reverse('projects-edit-tag', kwargs={"pk": data.blocked_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [404, 404, 404, 403, 451] + + +def test_delete_tag(client, data): + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + post_data = json.dumps({ + "tag": "tag2", + }) + + url = reverse('projects-delete-tag', kwargs={"pk": data.public_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [401, 403, 403, 403, 200] + + url = reverse('projects-delete-tag', kwargs={"pk": data.private_project1.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [401, 403, 403, 403, 200] + + url = reverse('projects-delete-tag', kwargs={"pk": data.private_project2.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [404, 404, 404, 403, 200] + + url = reverse('projects-delete-tag', kwargs={"pk": data.blocked_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [404, 404, 404, 403, 451] + + +def test_mix_tags(client, data): + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + post_data = json.dumps({ + "from_tags": ["tag1"], + "to_tag": "tag3" + }) + + url = reverse('projects-mix-tags', kwargs={"pk": data.public_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [401, 403, 403, 403, 200] + + url = reverse('projects-mix-tags', kwargs={"pk": data.private_project1.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [401, 403, 403, 403, 200] + + url = reverse('projects-mix-tags', kwargs={"pk": data.private_project2.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [404, 404, 404, 403, 200] + + url = reverse('projects-mix-tags', kwargs={"pk": data.blocked_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [404, 404, 404, 403, 451] diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index 4c2e121e..29d57c50 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -10,6 +10,9 @@ from taiga.projects.services import stats as stats_services from taiga.projects.history.services import take_snapshot from taiga.permissions.choices import ANON_PERMISSIONS from taiga.projects.models import Project +from taiga.projects.userstories.models import UserStory +from taiga.projects.tasks.models import Task +from taiga.projects.issues.models import Issue from taiga.projects.choices import BLOCKED_BY_DELETING from .. import factories as f @@ -1854,6 +1857,189 @@ def test_delete_project_with_celery_disabled(client, settings): assert Project.objects.filter(id=project.id).count() == 0 +def test_create_tag(client, settings): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True) + url = reverse("projects-create-tag", args=(project.id,)) + client.login(user) + data = { + "tag": "newtag", + "color": "#123123" + } + + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 200 + project = Project.objects.get(id=project.pk) + assert project.tags_colors == [["newtag", "#123123"]] + + +def test_create_tag_without_color(client, settings): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True) + url = reverse("projects-create-tag", args=(project.id,)) + client.login(user) + data = { + "tag": "newtag", + } + + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 200 + project = Project.objects.get(id=project.pk) + assert project.tags_colors[0][0] == "newtag" + + +def test_edit_tag_only_name(client, settings): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user, tags_colors=[("tag", "#123123")]) + user_story = f.UserStoryFactory.create(project=project, tags=["tag"]) + task = f.TaskFactory.create(project=project, tags=["tag"]) + issue = f.IssueFactory.create(project=project, tags=["tag"]) + + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True) + url = reverse("projects-edit-tag", args=(project.id,)) + client.login(user) + data = { + "from_tag": "tag", + "to_tag": "renamed_tag" + } + + client.login(user) + response = client.json.post(url, json.dumps(data)) + print(response.data) + assert response.status_code == 200 + project = Project.objects.get(id=project.pk) + assert project.tags_colors == [["renamed_tag", "#123123"]] + user_story = UserStory.objects.get(id=user_story.pk) + assert user_story.tags == ["renamed_tag"] + task = Task.objects.get(id=task.pk) + assert task.tags == ["renamed_tag"] + issue = Issue.objects.get(id=issue.pk) + assert issue.tags == ["renamed_tag"] + + +def test_edit_tag_only_color(client, settings): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user, tags_colors=[("tag", "#123123")]) + user_story = f.UserStoryFactory.create(project=project, tags=["tag"]) + task = f.TaskFactory.create(project=project, tags=["tag"]) + issue = f.IssueFactory.create(project=project, tags=["tag"]) + + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True) + url = reverse("projects-edit-tag", args=(project.id,)) + client.login(user) + data = { + "from_tag": "tag", + "color": "#AAABBB" + } + + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 200 + project = Project.objects.get(id=project.pk) + assert project.tags_colors == [["tag", "#AAABBB"]] + user_story = UserStory.objects.get(id=user_story.pk) + assert user_story.tags == ["tag"] + task = Task.objects.get(id=task.pk) + assert task.tags == ["tag"] + issue = Issue.objects.get(id=issue.pk) + assert issue.tags == ["tag"] + + +def test_edit_tag(client, settings): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user, tags_colors=[("tag", "#123123")]) + user_story = f.UserStoryFactory.create(project=project, tags=["tag"]) + task = f.TaskFactory.create(project=project, tags=["tag"]) + issue = f.IssueFactory.create(project=project, tags=["tag"]) + + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True) + url = reverse("projects-edit-tag", args=(project.id,)) + client.login(user) + data = { + "from_tag": "tag", + "to_tag": "renamed_tag", + "color": "#AAABBB" + } + + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 200 + project = Project.objects.get(id=project.pk) + assert project.tags_colors == [["renamed_tag", "#AAABBB"]] + user_story = UserStory.objects.get(id=user_story.pk) + assert user_story.tags == ["renamed_tag"] + task = Task.objects.get(id=task.pk) + assert task.tags == ["renamed_tag"] + issue = Issue.objects.get(id=issue.pk) + assert issue.tags == ["renamed_tag"] + + +def test_delete_tag(client, settings): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user, tags_colors=[("tag", "#123123")]) + user_story = f.UserStoryFactory.create(project=project, tags=["tag"]) + task = f.TaskFactory.create(project=project, tags=["tag"]) + issue = f.IssueFactory.create(project=project, tags=["tag"]) + + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True) + url = reverse("projects-delete-tag", args=(project.id,)) + client.login(user) + data = { + "tag": "tag" + } + + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 200 + project = Project.objects.get(id=project.pk) + assert project.tags_colors == [] + user_story = UserStory.objects.get(id=user_story.pk) + assert user_story.tags == [] + task = Task.objects.get(id=task.pk) + assert task.tags == [] + issue = Issue.objects.get(id=issue.pk) + assert issue.tags == [] + + +def test_mix_tags(client, settings): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user, tags_colors=[("tag1", "#123123"), ("tag2", "#123123"), ("tag3", "#123123")]) + user_story = f.UserStoryFactory.create(project=project, tags=["tag1", "tag3"]) + task = f.TaskFactory.create(project=project, tags=["tag2", "tag3"]) + issue = f.IssueFactory.create(project=project, tags=["tag1", "tag2", "tag3"]) + + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + membership = f.MembershipFactory.create(project=project, user=user, role=role, is_admin=True) + url = reverse("projects-mix-tags", args=(project.id,)) + client.login(user) + data = { + "from_tags": ["tag1", "tag2"], + "to_tag": "tag2" + } + + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 200 + project = Project.objects.get(id=project.pk) + assert set(["tag2", "tag3"]) == set(dict(project.tags_colors).keys()) + user_story = UserStory.objects.get(id=user_story.pk) + assert set(user_story.tags) == set(["tag2", "tag3"]) + task = Task.objects.get(id=task.pk) + assert set(task.tags) == set(["tag2", "tag3"]) + issue = Issue.objects.get(id=issue.pk) + assert set(issue.tags) == set(["tag2", "tag3"]) + + def test_color_tags_project_fired_on_element_create(): user_story = f.UserStoryFactory.create(tags=["tag"]) project = Project.objects.get(id=user_story.project.id) @@ -1875,4 +2061,3 @@ def test_color_tags_project_fired_on_element_update_respecting_color(): user_story.save() project = Project.objects.get(id=user_story.project.id) assert project.tags_colors == [["tag", "#123123"]] ->>>>>>> d64d158... WIP: migrations, removing automatic color generation diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index d65b2451..2c25b29d 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -481,7 +481,7 @@ def test_get_watched_list_valid_info_for_project(): fav_user = f.UserFactory() viewer_user = f.UserFactory() - project = f.ProjectFactory(is_private=False, name="Testing project", tags=['test', 'tag']) + project = f.ProjectFactory(is_private=False, name="Testing project") role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) project.add_watcher(fav_user) @@ -499,11 +499,6 @@ def test_get_watched_list_valid_info_for_project(): assert project_watch_info["assigned_to"] == None assert project_watch_info["status"] == None assert project_watch_info["status_color"] == None - - tags_colors = {tc["name"]:tc["color"] for tc in project_watch_info["tags_colors"]} - assert "test" in tags_colors - assert "tag" in tags_colors - assert project_watch_info["is_private"] == project.is_private assert project_watch_info["logo_small_url"] == get_thumbnail_url(project.logo, settings.THN_LOGO_SMALL) assert project_watch_info["is_fan"] == False @@ -540,7 +535,7 @@ def test_get_liked_list_valid_info(): fan_user = f.UserFactory() viewer_user = f.UserFactory() - project = f.ProjectFactory(is_private=False, name="Testing project", tags=['test', 'tag']) + project = f.ProjectFactory(is_private=False, name="Testing project") content_type = ContentType.objects.get_for_model(project) like = f.LikeFactory(content_type=content_type, object_id=project.id, user=fan_user) project.refresh_totals() @@ -558,11 +553,6 @@ def test_get_liked_list_valid_info(): assert project_like_info["assigned_to"] == None assert project_like_info["status"] == None assert project_like_info["status_color"] == None - - tags_colors = {tc["name"]:tc["color"] for tc in project_like_info["tags_colors"]} - assert "test" in tags_colors - assert "tag" in tags_colors - assert project_like_info["is_private"] == project.is_private assert project_like_info["logo_small_url"] == get_thumbnail_url(project.logo, settings.THN_LOGO_SMALL)