US #55: Custom fields - Import/Export custom attributes

remotes/origin/enhancement/email-actions
David Barragán Merino 2015-02-05 12:30:13 +01:00
parent 84db9956d5
commit ebc3388d03
5 changed files with 146 additions and 12 deletions

View File

@ -127,6 +127,21 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
"severities" in data):
service.store_default_choices(project_serialized.object, data)
if "userstorycustomattributes" in data:
service.store_custom_attributes(project_serialized.object, data,
"userstorycustomattributes",
serializers.UserStoryCustomAttributeExportSerializer)
if "taskcustomattributes" in data:
service.store_custom_attributes(project_serialized.object, data,
"taskcustomattributes",
serializers.TaskCustomAttributeExportSerializer)
if "issuecustomattributes" in data:
service.store_custom_attributes(project_serialized.object, data,
"issuecustomattributes",
serializers.IssueCustomAttributeExportSerializer)
if "roles" in data:
service.store_roles(project_serialized.object, data)

View File

@ -103,6 +103,16 @@ def dict_to_project(data, owner=None):
if service.get_errors(clear=False):
raise TaigaImportError('error importing default choices')
service.store_custom_attributes(proj, data, "userstorycustomattributes",
serializers.UserStoryCustomAttributeExportSerializer)
service.store_custom_attributes(proj, data, "taskcustomattributes",
serializers.TaskCustomAttributeExportSerializer)
service.store_custom_attributes(proj, data, "issuecustomattributes",
serializers.IssueCustomAttributeExportSerializer)
if service.get_errors(clear=False):
raise TaigaImportError('error importing custom attributes')
service.store_roles(proj, data)
if service.get_errors(clear=False):

View File

@ -20,11 +20,13 @@ from collections import OrderedDict
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError
from rest_framework import serializers
from taiga.projects import models as projects_models
from taiga.projects.custom_attributes import models as custom_attributes_models
from taiga.projects.userstories import models as userstories_models
from taiga.projects.tasks import models as tasks_models
from taiga.projects.issues import models as issues_models
@ -81,14 +83,15 @@ class RelatedNoneSafeField(serializers.RelatedField):
return
value = self.get_default_value()
key = self.source or field_name
if value in self.null_values:
if self.required:
raise ValidationError(self.error_messages['required'])
into[(self.source or field_name)] = None
into[key] = None
elif self.many:
into[(self.source or field_name)] = [self.from_native(item) for item in value if self.from_native(item) is not None]
into[key] = [self.from_native(item) for item in value if self.from_native(item) is not None]
else:
into[(self.source or field_name)] = self.from_native(value)
into[key] = self.from_native(value)
class UserRelatedField(RelatedNoneSafeField):
@ -251,7 +254,8 @@ class AttachmentExportSerializerMixin(serializers.ModelSerializer):
def get_attachments(self, obj):
content_type = ContentType.objects.get_for_model(obj.__class__)
attachments_qs = attachments_models.Attachment.objects.filter(object_id=obj.pk, content_type=content_type)
attachments_qs = attachments_models.Attachment.objects.filter(object_id=obj.pk,
content_type=content_type)
return AttachmentExportSerializer(attachments_qs, many=True).data
@ -305,6 +309,30 @@ class RoleExportSerializer(serializers.ModelSerializer):
exclude = ('id', 'project')
class UserStoryCustomAttributeExportSerializer(serializers.ModelSerializer):
modified_date = serializers.DateTimeField(required=False)
class Meta:
model = custom_attributes_models.UserStoryCustomAttribute
exclude = ('id', 'project')
class TaskCustomAttributeExportSerializer(serializers.ModelSerializer):
modified_date = serializers.DateTimeField(required=False)
class Meta:
model = custom_attributes_models.TaskCustomAttribute
exclude = ('id', 'project')
class IssueCustomAttributeExportSerializer(serializers.ModelSerializer):
modified_date = serializers.DateTimeField(required=False)
class Meta:
model = custom_attributes_models.IssueCustomAttribute
exclude = ('id', 'project')
class MembershipExportSerializer(serializers.ModelSerializer):
user = UserRelatedField(required=False)
role = ProjectRelatedField(slug_field="name")
@ -354,7 +382,8 @@ class MilestoneExportSerializer(serializers.ModelSerializer):
exclude = ('id', 'project')
class TaskExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin, serializers.ModelSerializer):
class TaskExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin,
serializers.ModelSerializer):
owner = UserRelatedField(required=False)
status = ProjectRelatedField(slug_field="name")
user_story = ProjectRelatedField(slug_field="ref", required=False)
@ -368,7 +397,8 @@ class TaskExportSerializer(HistoryExportSerializerMixin, AttachmentExportSeriali
exclude = ('id', 'project')
class UserStoryExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin, serializers.ModelSerializer):
class UserStoryExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin,
serializers.ModelSerializer):
role_points = RolePointsExportSerializer(many=True, required=False)
owner = UserRelatedField(required=False)
assigned_to = UserRelatedField(required=False)
@ -383,7 +413,8 @@ class UserStoryExportSerializer(HistoryExportSerializerMixin, AttachmentExportSe
exclude = ('id', 'project', 'points', 'tasks')
class IssueExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin, serializers.ModelSerializer):
class IssueExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin,
serializers.ModelSerializer):
owner = UserRelatedField(required=False)
status = ProjectRelatedField(slug_field="name")
assigned_to = UserRelatedField(required=False)
@ -403,7 +434,8 @@ class IssueExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerial
exclude = ('id', 'project')
class WikiPageExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin, serializers.ModelSerializer):
class WikiPageExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin,
serializers.ModelSerializer):
owner = UserRelatedField(required=False)
last_modifier = UserRelatedField(required=False)
watchers = UserRelatedField(many=True, required=False)
@ -437,6 +469,9 @@ class ProjectExportSerializer(serializers.ModelSerializer):
priorities = PriorityExportSerializer(many=True, required=False)
severities = SeverityExportSerializer(many=True, required=False)
issue_types = IssueTypeExportSerializer(many=True, required=False)
userstorycustomattributes = UserStoryCustomAttributeExportSerializer(many=True, required=False)
taskcustomattributes = TaskCustomAttributeExportSerializer(many=True, required=False)
issuecustomattributes = IssueCustomAttributeExportSerializer(many=True, required=False)
roles = RoleExportSerializer(many=True, required=False)
milestones = MilestoneExportSerializer(many=True, required=False)
wiki_pages = WikiPageExportSerializer(many=True, required=False)

View File

@ -57,7 +57,8 @@ def store_project(data):
"default_priority", "default_severity", "default_issue_status",
"default_issue_type", "memberships", "points", "us_statuses",
"task_statuses", "issue_statuses", "priorities", "severities",
"issue_types", "roles", "milestones", "wiki_pages",
"issue_types", "userstorycustomattributes", "taskcustomattributes",
"issuecustomattributes", "roles", "milestones", "wiki_pages",
"wiki_links", "notify_policies", "user_stories", "issues", "tasks",
]
if key not in excluded_fields:
@ -72,7 +73,7 @@ def store_project(data):
return None
def store_choice(project, data, field, serializer):
def _store_choice(project, data, field, serializer):
serialized = serializer(data=data)
if serialized.is_valid():
serialized.object.project = project
@ -86,7 +87,25 @@ def store_choice(project, data, field, serializer):
def store_choices(project, data, field, serializer):
result = []
for choice_data in data.get(field, []):
result.append(store_choice(project, choice_data, field, serializer))
result.append(_store_choice(project, choice_data, field, serializer))
return result
def _store_custom_attribute(project, data, field, serializer):
serialized = serializer(data=data)
if serialized.is_valid():
serialized.object.project = project
serialized.object._importing = True
serialized.save()
return serialized.object
add_errors(field, serialized.errors)
return None
def store_custom_attributes(project, data, field, serializer):
result = []
for custom_attribute_data in data.get(field, []):
result.append(_store_custom_attribute(project, custom_attribute_data, field, serializer))
return result

View File

@ -167,6 +167,61 @@ def test_invalid_project_import_with_extra_data(client):
assert Project.objects.filter(slug="imported-project").count() == 0
def test_valid_project_import_with_custom_attributes(client):
user = f.UserFactory.create()
url = reverse("importer-list")
data = {
"name": "Imported project",
"description": "Imported project",
"userstorycustomattributes": [{
"name": "custom attribute example 1",
"description": "short description 1",
"order": 1
}],
"taskcustomattributes": [{
"name": "custom attribute example 1",
"description": "short description 1",
"order": 1
}],
"issuecustomattributes": [{
"name": "custom attribute example 1",
"description": "short description 1",
"order": 1
}]
}
must_empty_children = ["issues", "user_stories", "wiki_pages", "milestones", "wiki_links"]
must_one_instance_children = ["userstorycustomattributes", "taskcustomattributes", "issuecustomattributes"]
client.login(user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201
assert all(map(lambda x: len(response.data[x]) == 0, must_empty_children))
# Allwais is created at least the owner membership
assert all(map(lambda x: len(response.data[x]) == 1, must_one_instance_children))
assert response.data["owner"] == user.email
def test_invalid_project_import_with_custom_attributes(client):
user = f.UserFactory.create()
url = reverse("importer-list")
data = {
"name": "Imported project",
"description": "Imported project",
"userstorycustomattributes": [{ }],
"taskcustomattributes": [{ }],
"issuecustomattributes": [{ }]
}
client.login(user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
assert len(response.data) == 3
assert Project.objects.filter(slug="imported-project").count() == 0
def test_invalid_issue_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)