Import export including epics
parent
0ff7ce8975
commit
9d0e0180ef
|
@ -23,7 +23,7 @@ _cache_user_by_email = {}
|
||||||
_custom_tasks_attributes_cache = {}
|
_custom_tasks_attributes_cache = {}
|
||||||
_custom_issues_attributes_cache = {}
|
_custom_issues_attributes_cache = {}
|
||||||
_custom_userstories_attributes_cache = {}
|
_custom_userstories_attributes_cache = {}
|
||||||
|
_custom_epics_attributes_cache = {}
|
||||||
|
|
||||||
def cached_get_user_by_pk(pk):
|
def cached_get_user_by_pk(pk):
|
||||||
if pk not in _cache_user_by_pk:
|
if pk not in _cache_user_by_pk:
|
||||||
|
|
|
@ -29,6 +29,7 @@ from .mixins import (HistoryExportSerializerMixin,
|
||||||
WatcheableObjectLightSerializerMixin)
|
WatcheableObjectLightSerializerMixin)
|
||||||
from .cache import (_custom_tasks_attributes_cache,
|
from .cache import (_custom_tasks_attributes_cache,
|
||||||
_custom_userstories_attributes_cache,
|
_custom_userstories_attributes_cache,
|
||||||
|
_custom_epics_attributes_cache,
|
||||||
_custom_issues_attributes_cache)
|
_custom_issues_attributes_cache)
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,6 +56,14 @@ class UserStoryStatusExportSerializer(RelatedExportSerializer):
|
||||||
wip_limit = Field()
|
wip_limit = Field()
|
||||||
|
|
||||||
|
|
||||||
|
class EpicStatusExportSerializer(RelatedExportSerializer):
|
||||||
|
name = Field()
|
||||||
|
slug = Field()
|
||||||
|
order = Field()
|
||||||
|
is_closed = Field()
|
||||||
|
color = Field()
|
||||||
|
|
||||||
|
|
||||||
class TaskStatusExportSerializer(RelatedExportSerializer):
|
class TaskStatusExportSerializer(RelatedExportSerializer):
|
||||||
name = Field()
|
name = Field()
|
||||||
slug = Field()
|
slug = Field()
|
||||||
|
@ -97,6 +106,15 @@ class RoleExportSerializer(RelatedExportSerializer):
|
||||||
permissions = Field()
|
permissions = Field()
|
||||||
|
|
||||||
|
|
||||||
|
class EpicCustomAttributesExportSerializer(RelatedExportSerializer):
|
||||||
|
name = Field()
|
||||||
|
description = Field()
|
||||||
|
type = Field()
|
||||||
|
order = Field()
|
||||||
|
created_date = DateTimeField()
|
||||||
|
modified_date = DateTimeField()
|
||||||
|
|
||||||
|
|
||||||
class UserStoryCustomAttributeExportSerializer(RelatedExportSerializer):
|
class UserStoryCustomAttributeExportSerializer(RelatedExportSerializer):
|
||||||
name = Field()
|
name = Field()
|
||||||
description = Field()
|
description = Field()
|
||||||
|
@ -238,6 +256,45 @@ class UserStoryExportSerializer(CustomAttributesValuesExportSerializerMixin,
|
||||||
return _custom_userstories_attributes_cache[project.id]
|
return _custom_userstories_attributes_cache[project.id]
|
||||||
|
|
||||||
|
|
||||||
|
class EpicRelatedUserStoryExportSerializer(RelatedExportSerializer):
|
||||||
|
user_story = SlugRelatedField(slug_field="ref")
|
||||||
|
order = Field()
|
||||||
|
|
||||||
|
|
||||||
|
class EpicExportSerializer(CustomAttributesValuesExportSerializerMixin,
|
||||||
|
HistoryExportSerializerMixin,
|
||||||
|
AttachmentExportSerializerMixin,
|
||||||
|
WatcheableObjectLightSerializerMixin,
|
||||||
|
RelatedExportSerializer):
|
||||||
|
ref = Field()
|
||||||
|
owner = UserRelatedField()
|
||||||
|
status = SlugRelatedField(slug_field="name")
|
||||||
|
epics_order = Field()
|
||||||
|
created_date = DateTimeField()
|
||||||
|
modified_date = DateTimeField()
|
||||||
|
subject = Field()
|
||||||
|
description = Field()
|
||||||
|
color = Field()
|
||||||
|
assigned_to = UserRelatedField()
|
||||||
|
client_requirement = Field()
|
||||||
|
team_requirement = Field()
|
||||||
|
version = Field()
|
||||||
|
blocked_note = Field()
|
||||||
|
is_blocked = Field()
|
||||||
|
tags = Field()
|
||||||
|
related_user_stories = MethodField()
|
||||||
|
|
||||||
|
def get_related_user_stories(self, obj):
|
||||||
|
return EpicRelatedUserStoryExportSerializer(obj.relateduserstory_set.all(), many=True).data
|
||||||
|
|
||||||
|
def custom_attributes_queryset(self, project):
|
||||||
|
if project.id not in _custom_epics_attributes_cache:
|
||||||
|
_custom_epics_attributes_cache[project.id] = list(
|
||||||
|
project.userstorycustomattributes.all().values('id', 'name')
|
||||||
|
)
|
||||||
|
return _custom_epics_attributes_cache[project.id]
|
||||||
|
|
||||||
|
|
||||||
class IssueExportSerializer(CustomAttributesValuesExportSerializerMixin,
|
class IssueExportSerializer(CustomAttributesValuesExportSerializerMixin,
|
||||||
HistoryExportSerializerMixin,
|
HistoryExportSerializerMixin,
|
||||||
AttachmentExportSerializerMixin,
|
AttachmentExportSerializerMixin,
|
||||||
|
@ -307,6 +364,7 @@ class ProjectExportSerializer(WatcheableObjectLightSerializerMixin):
|
||||||
logo = FileField()
|
logo = FileField()
|
||||||
total_milestones = Field()
|
total_milestones = Field()
|
||||||
total_story_points = Field()
|
total_story_points = Field()
|
||||||
|
is_epics_activated = Field()
|
||||||
is_backlog_activated = Field()
|
is_backlog_activated = Field()
|
||||||
is_kanban_activated = Field()
|
is_kanban_activated = Field()
|
||||||
is_wiki_activated = Field()
|
is_wiki_activated = Field()
|
||||||
|
@ -318,6 +376,7 @@ class ProjectExportSerializer(WatcheableObjectLightSerializerMixin):
|
||||||
is_featured = Field()
|
is_featured = Field()
|
||||||
is_looking_for_people = Field()
|
is_looking_for_people = Field()
|
||||||
looking_for_people_note = Field()
|
looking_for_people_note = Field()
|
||||||
|
epics_csv_uuid = Field()
|
||||||
userstories_csv_uuid = Field()
|
userstories_csv_uuid = Field()
|
||||||
tasks_csv_uuid = Field()
|
tasks_csv_uuid = Field()
|
||||||
issues_csv_uuid = Field()
|
issues_csv_uuid = Field()
|
||||||
|
@ -339,6 +398,7 @@ class ProjectExportSerializer(WatcheableObjectLightSerializerMixin):
|
||||||
owner = UserRelatedField()
|
owner = UserRelatedField()
|
||||||
memberships = MembershipExportSerializer(many=True)
|
memberships = MembershipExportSerializer(many=True)
|
||||||
points = PointsExportSerializer(many=True)
|
points = PointsExportSerializer(many=True)
|
||||||
|
epic_statuses = EpicStatusExportSerializer(many=True)
|
||||||
us_statuses = UserStoryStatusExportSerializer(many=True)
|
us_statuses = UserStoryStatusExportSerializer(many=True)
|
||||||
task_statuses = TaskStatusExportSerializer(many=True)
|
task_statuses = TaskStatusExportSerializer(many=True)
|
||||||
issue_types = IssueTypeExportSerializer(many=True)
|
issue_types = IssueTypeExportSerializer(many=True)
|
||||||
|
@ -347,15 +407,18 @@ class ProjectExportSerializer(WatcheableObjectLightSerializerMixin):
|
||||||
severities = SeverityExportSerializer(many=True)
|
severities = SeverityExportSerializer(many=True)
|
||||||
tags_colors = Field()
|
tags_colors = Field()
|
||||||
default_points = SlugRelatedField(slug_field="name")
|
default_points = SlugRelatedField(slug_field="name")
|
||||||
|
default_epic_status = SlugRelatedField(slug_field="name")
|
||||||
default_us_status = SlugRelatedField(slug_field="name")
|
default_us_status = SlugRelatedField(slug_field="name")
|
||||||
default_task_status = SlugRelatedField(slug_field="name")
|
default_task_status = SlugRelatedField(slug_field="name")
|
||||||
default_priority = SlugRelatedField(slug_field="name")
|
default_priority = SlugRelatedField(slug_field="name")
|
||||||
default_severity = SlugRelatedField(slug_field="name")
|
default_severity = SlugRelatedField(slug_field="name")
|
||||||
default_issue_status = SlugRelatedField(slug_field="name")
|
default_issue_status = SlugRelatedField(slug_field="name")
|
||||||
default_issue_type = SlugRelatedField(slug_field="name")
|
default_issue_type = SlugRelatedField(slug_field="name")
|
||||||
|
epiccustomattributes = EpicCustomAttributesExportSerializer(many=True)
|
||||||
userstorycustomattributes = UserStoryCustomAttributeExportSerializer(many=True)
|
userstorycustomattributes = UserStoryCustomAttributeExportSerializer(many=True)
|
||||||
taskcustomattributes = TaskCustomAttributeExportSerializer(many=True)
|
taskcustomattributes = TaskCustomAttributeExportSerializer(many=True)
|
||||||
issuecustomattributes = IssueCustomAttributeExportSerializer(many=True)
|
issuecustomattributes = IssueCustomAttributeExportSerializer(many=True)
|
||||||
|
epics = EpicExportSerializer(many=True)
|
||||||
user_stories = UserStoryExportSerializer(many=True)
|
user_stories = UserStoryExportSerializer(many=True)
|
||||||
tasks = TaskExportSerializer(many=True)
|
tasks = TaskExportSerializer(many=True)
|
||||||
milestones = MilestoneExportSerializer(many=True)
|
milestones = MilestoneExportSerializer(many=True)
|
||||||
|
|
|
@ -45,12 +45,16 @@ def render_project(project, outfile, chunk_size=8190):
|
||||||
# field.initialize(parent=serializer, field_name=field_name)
|
# field.initialize(parent=serializer, field_name=field_name)
|
||||||
|
|
||||||
# These four "special" fields hava attachments so we use them in a special way
|
# These four "special" fields hava attachments so we use them in a special way
|
||||||
if field_name in ["wiki_pages", "user_stories", "tasks", "issues"]:
|
if field_name in ["wiki_pages", "user_stories", "tasks", "issues", "epics"]:
|
||||||
value = get_component(project, field_name)
|
value = get_component(project, field_name)
|
||||||
if field_name != "wiki_pages":
|
if field_name != "wiki_pages":
|
||||||
value = value.select_related('owner', 'status', 'milestone',
|
value = value.select_related('owner', 'status',
|
||||||
'project', 'assigned_to',
|
'project', 'assigned_to',
|
||||||
'custom_attributes_values')
|
'custom_attributes_values')
|
||||||
|
|
||||||
|
if field_name in ["user_stories", "tasks", "issues"]:
|
||||||
|
value = value.select_related('milestone')
|
||||||
|
|
||||||
if field_name == "issues":
|
if field_name == "issues":
|
||||||
value = value.select_related('severity', 'priority', 'type')
|
value = value.select_related('severity', 'priority', 'type')
|
||||||
value = value.prefetch_related('history_entry', 'attachments')
|
value = value.prefetch_related('history_entry', 'attachments')
|
||||||
|
|
|
@ -80,11 +80,17 @@ def store_project(data):
|
||||||
excluded_fields = [
|
excluded_fields = [
|
||||||
"default_points", "default_us_status", "default_task_status",
|
"default_points", "default_us_status", "default_task_status",
|
||||||
"default_priority", "default_severity", "default_issue_status",
|
"default_priority", "default_severity", "default_issue_status",
|
||||||
"default_issue_type", "memberships", "points", "us_statuses",
|
"default_issue_type", "default_epic_status",
|
||||||
"task_statuses", "issue_statuses", "priorities", "severities",
|
"memberships", "points",
|
||||||
"issue_types", "userstorycustomattributes", "taskcustomattributes",
|
"epic_statuses", "us_statuses", "task_statuses", "issue_statuses",
|
||||||
"issuecustomattributes", "roles", "milestones", "wiki_pages",
|
"priorities", "severities",
|
||||||
"wiki_links", "notify_policies", "user_stories", "issues", "tasks",
|
"issue_types",
|
||||||
|
"epiccustomattributes", "userstorycustomattributes",
|
||||||
|
"taskcustomattributes", "issuecustomattributes",
|
||||||
|
"roles", "milestones",
|
||||||
|
"wiki_pages", "wiki_links",
|
||||||
|
"notify_policies",
|
||||||
|
"epics", "user_stories", "issues", "tasks",
|
||||||
"is_featured"
|
"is_featured"
|
||||||
]
|
]
|
||||||
if key not in excluded_fields:
|
if key not in excluded_fields:
|
||||||
|
@ -219,6 +225,7 @@ def _store_project_attribute_value(project, data, field, serializer):
|
||||||
validator.object._importing = True
|
validator.object._importing = True
|
||||||
validator.save()
|
validator.save()
|
||||||
return validator.object
|
return validator.object
|
||||||
|
|
||||||
add_errors(field, validator.errors)
|
add_errors(field, validator.errors)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -239,10 +246,10 @@ def store_default_project_attributes_values(project, data):
|
||||||
else:
|
else:
|
||||||
value = related.all().first()
|
value = related.all().first()
|
||||||
setattr(project, field, value)
|
setattr(project, field, value)
|
||||||
|
|
||||||
helper(project, "default_points", project.points, data)
|
helper(project, "default_points", project.points, data)
|
||||||
helper(project, "default_issue_type", project.issue_types, data)
|
helper(project, "default_issue_type", project.issue_types, data)
|
||||||
helper(project, "default_issue_status", project.issue_statuses, data)
|
helper(project, "default_issue_status", project.issue_statuses, data)
|
||||||
|
helper(project, "default_epic_status", project.epic_statuses, data)
|
||||||
helper(project, "default_us_status", project.us_statuses, data)
|
helper(project, "default_us_status", project.us_statuses, data)
|
||||||
helper(project, "default_task_status", project.task_statuses, data)
|
helper(project, "default_task_status", project.task_statuses, data)
|
||||||
helper(project, "default_priority", project.priorities, data)
|
helper(project, "default_priority", project.priorities, data)
|
||||||
|
@ -317,12 +324,14 @@ def _store_role_point(project, us, role_point):
|
||||||
add_errors("role_points", validator.errors)
|
add_errors("role_points", validator.errors)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def store_user_story(project, data):
|
def store_user_story(project, data):
|
||||||
if "status" not in data and project.default_us_status:
|
if "status" not in data and project.default_us_status:
|
||||||
data["status"] = project.default_us_status.name
|
data["status"] = project.default_us_status.name
|
||||||
|
|
||||||
us_data = {key: value for key, value in data.items() if key not in
|
us_data = {key: value for key, value in data.items() if key not in
|
||||||
["role_points", "custom_attributes_values"]}
|
["role_points", "custom_attributes_values"]}
|
||||||
|
|
||||||
validator = validators.UserStoryExportValidator(data=us_data, context={"project": project})
|
validator = validators.UserStoryExportValidator(data=us_data, context={"project": project})
|
||||||
|
|
||||||
if validator.is_valid():
|
if validator.is_valid():
|
||||||
|
@ -360,10 +369,13 @@ def store_user_story(project, data):
|
||||||
custom_attributes_values = data.get("custom_attributes_values", None)
|
custom_attributes_values = data.get("custom_attributes_values", None)
|
||||||
if custom_attributes_values:
|
if custom_attributes_values:
|
||||||
custom_attributes = validator.object.project.userstorycustomattributes.all().values('id', 'name')
|
custom_attributes = validator.object.project.userstorycustomattributes.all().values('id', 'name')
|
||||||
custom_attributes_values = _use_id_instead_name_as_key_in_custom_attributes_values(
|
custom_attributes_values = \
|
||||||
custom_attributes, custom_attributes_values)
|
_use_id_instead_name_as_key_in_custom_attributes_values(custom_attributes,
|
||||||
|
custom_attributes_values)
|
||||||
|
|
||||||
_store_custom_attributes_values(validator.object, custom_attributes_values,
|
_store_custom_attributes_values(validator.object, custom_attributes_values,
|
||||||
"user_story", validators.UserStoryCustomAttributesValuesExportValidator)
|
"user_story",
|
||||||
|
validators.UserStoryCustomAttributesValuesExportValidator)
|
||||||
|
|
||||||
return validator
|
return validator
|
||||||
|
|
||||||
|
@ -379,6 +391,81 @@ def store_user_stories(project, data):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
## EPICS
|
||||||
|
|
||||||
|
def _store_epic_related_user_story(project, epic, related_user_story):
|
||||||
|
validator = validators.EpicRelatedUserStoryExportValidator(data=related_user_story,
|
||||||
|
context={"project": project})
|
||||||
|
if validator.is_valid():
|
||||||
|
validator.object.epic = epic
|
||||||
|
validator.object.save()
|
||||||
|
return validator.object
|
||||||
|
|
||||||
|
add_errors("epic_related_user_stories", validator.errors)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def store_epic(project, data):
|
||||||
|
if "status" not in data and project.default_epic_status:
|
||||||
|
data["status"] = project.default_epic_status.name
|
||||||
|
|
||||||
|
validator = validators.EpicExportValidator(data=data, context={"project": project})
|
||||||
|
if validator.is_valid():
|
||||||
|
validator.object.project = project
|
||||||
|
if validator.object.owner is None:
|
||||||
|
validator.object.owner = validator.object.project.owner
|
||||||
|
validator.object._importing = True
|
||||||
|
validator.object._not_notify = True
|
||||||
|
|
||||||
|
validator.save()
|
||||||
|
validator.save_watchers()
|
||||||
|
|
||||||
|
if validator.object.ref:
|
||||||
|
sequence_name = refs.make_sequence_name(project)
|
||||||
|
if not seq.exists(sequence_name):
|
||||||
|
seq.create(sequence_name)
|
||||||
|
seq.set_max(sequence_name, validator.object.ref)
|
||||||
|
else:
|
||||||
|
validator.object.ref, _ = refs.make_reference(validator.object, project)
|
||||||
|
validator.object.save()
|
||||||
|
|
||||||
|
for epic_attachment in data.get("attachments", []):
|
||||||
|
_store_attachment(project, validator.object, epic_attachment)
|
||||||
|
|
||||||
|
for related_user_story in data.get("related_user_stories", []):
|
||||||
|
_store_epic_related_user_story(project, validator.object, related_user_story)
|
||||||
|
|
||||||
|
history_entries = data.get("history", [])
|
||||||
|
for history in history_entries:
|
||||||
|
_store_history(project, validator.object, history)
|
||||||
|
|
||||||
|
if not history_entries:
|
||||||
|
take_snapshot(validator.object, user=validator.object.owner)
|
||||||
|
|
||||||
|
custom_attributes_values = data.get("custom_attributes_values", None)
|
||||||
|
if custom_attributes_values:
|
||||||
|
custom_attributes = validator.object.project.epiccustomattributes.all().values('id', 'name')
|
||||||
|
custom_attributes_values = \
|
||||||
|
_use_id_instead_name_as_key_in_custom_attributes_values(custom_attributes,
|
||||||
|
custom_attributes_values)
|
||||||
|
_store_custom_attributes_values(validator.object, custom_attributes_values,
|
||||||
|
"epic",
|
||||||
|
validators.EpicCustomAttributesValuesExportValidator)
|
||||||
|
|
||||||
|
return validator
|
||||||
|
|
||||||
|
add_errors("epics", validator.errors)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def store_epics(project, data):
|
||||||
|
results = []
|
||||||
|
for epic in data.get("epics", []):
|
||||||
|
epic = store_epic(project, epic)
|
||||||
|
results.append(epic)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
## TASKS
|
## TASKS
|
||||||
|
|
||||||
def store_task(project, data):
|
def store_task(project, data):
|
||||||
|
@ -418,10 +505,13 @@ def store_task(project, data):
|
||||||
custom_attributes_values = data.get("custom_attributes_values", None)
|
custom_attributes_values = data.get("custom_attributes_values", None)
|
||||||
if custom_attributes_values:
|
if custom_attributes_values:
|
||||||
custom_attributes = validator.object.project.taskcustomattributes.all().values('id', 'name')
|
custom_attributes = validator.object.project.taskcustomattributes.all().values('id', 'name')
|
||||||
custom_attributes_values = _use_id_instead_name_as_key_in_custom_attributes_values(
|
custom_attributes_values = \
|
||||||
custom_attributes, custom_attributes_values)
|
_use_id_instead_name_as_key_in_custom_attributes_values(custom_attributes,
|
||||||
|
custom_attributes_values)
|
||||||
|
|
||||||
_store_custom_attributes_values(validator.object, custom_attributes_values,
|
_store_custom_attributes_values(validator.object, custom_attributes_values,
|
||||||
"task", validators.TaskCustomAttributesValuesExportValidator)
|
"task",
|
||||||
|
validators.TaskCustomAttributesValuesExportValidator)
|
||||||
|
|
||||||
return validator
|
return validator
|
||||||
|
|
||||||
|
@ -486,10 +576,12 @@ def store_issue(project, data):
|
||||||
custom_attributes_values = data.get("custom_attributes_values", None)
|
custom_attributes_values = data.get("custom_attributes_values", None)
|
||||||
if custom_attributes_values:
|
if custom_attributes_values:
|
||||||
custom_attributes = validator.object.project.issuecustomattributes.all().values('id', 'name')
|
custom_attributes = validator.object.project.issuecustomattributes.all().values('id', 'name')
|
||||||
custom_attributes_values = _use_id_instead_name_as_key_in_custom_attributes_values(
|
custom_attributes_values = \
|
||||||
custom_attributes, custom_attributes_values)
|
_use_id_instead_name_as_key_in_custom_attributes_values(custom_attributes,
|
||||||
|
custom_attributes_values)
|
||||||
_store_custom_attributes_values(validator.object, custom_attributes_values,
|
_store_custom_attributes_values(validator.object, custom_attributes_values,
|
||||||
"issue", validators.IssueCustomAttributesValuesExportValidator)
|
"issue",
|
||||||
|
validators.IssueCustomAttributesValuesExportValidator)
|
||||||
|
|
||||||
return validator
|
return validator
|
||||||
|
|
||||||
|
@ -606,6 +698,7 @@ def _validate_if_owner_have_enought_space_to_this_project(owner, data):
|
||||||
is_private = data.get("is_private", False)
|
is_private = data.get("is_private", False)
|
||||||
total_memberships = len([m for m in data.get("memberships", [])
|
total_memberships = len([m for m in data.get("memberships", [])
|
||||||
if m.get("email", None) != data["owner"]])
|
if m.get("email", None) != data["owner"]])
|
||||||
|
|
||||||
total_memberships = total_memberships + 1 # 1 is the owner
|
total_memberships = total_memberships + 1 # 1 is the owner
|
||||||
(enough_slots, error_message) = users_service.has_available_slot_for_import_new_project(
|
(enough_slots, error_message) = users_service.has_available_slot_for_import_new_project(
|
||||||
owner,
|
owner,
|
||||||
|
@ -655,6 +748,7 @@ def _populate_project_object(project, data):
|
||||||
check_if_there_is_some_error(_("error importing memberships"), project)
|
check_if_there_is_some_error(_("error importing memberships"), project)
|
||||||
|
|
||||||
# Create project attributes values
|
# Create project attributes values
|
||||||
|
store_project_attributes_values(project, data, "epic_statuses", validators.EpicStatusExportValidator)
|
||||||
store_project_attributes_values(project, data, "us_statuses", validators.UserStoryStatusExportValidator)
|
store_project_attributes_values(project, data, "us_statuses", validators.UserStoryStatusExportValidator)
|
||||||
store_project_attributes_values(project, data, "points", validators.PointsExportValidator)
|
store_project_attributes_values(project, data, "points", validators.PointsExportValidator)
|
||||||
store_project_attributes_values(project, data, "task_statuses", validators.TaskStatusExportValidator)
|
store_project_attributes_values(project, data, "task_statuses", validators.TaskStatusExportValidator)
|
||||||
|
@ -669,6 +763,8 @@ def _populate_project_object(project, data):
|
||||||
check_if_there_is_some_error(_("error importing default project attributes values"), project)
|
check_if_there_is_some_error(_("error importing default project attributes values"), project)
|
||||||
|
|
||||||
# Create custom attributes
|
# Create custom attributes
|
||||||
|
store_custom_attributes(project, data, "epiccustomattributes",
|
||||||
|
validators.EpicCustomAttributeExportValidator)
|
||||||
store_custom_attributes(project, data, "userstorycustomattributes",
|
store_custom_attributes(project, data, "userstorycustomattributes",
|
||||||
validators.UserStoryCustomAttributeExportValidator)
|
validators.UserStoryCustomAttributeExportValidator)
|
||||||
store_custom_attributes(project, data, "taskcustomattributes",
|
store_custom_attributes(project, data, "taskcustomattributes",
|
||||||
|
@ -689,6 +785,10 @@ def _populate_project_object(project, data):
|
||||||
store_user_stories(project, data)
|
store_user_stories(project, data)
|
||||||
check_if_there_is_some_error(_("error importing user stories"), project)
|
check_if_there_is_some_error(_("error importing user stories"), project)
|
||||||
|
|
||||||
|
# Creat epics
|
||||||
|
store_epics(project, data)
|
||||||
|
check_if_there_is_some_error(_("error importing epics"), project)
|
||||||
|
|
||||||
# Createer tasks
|
# Createer tasks
|
||||||
store_tasks(project, data)
|
store_tasks(project, data)
|
||||||
check_if_there_is_some_error(_("error importing tasks"), project)
|
check_if_there_is_some_error(_("error importing tasks"), project)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from .validators import PointsExportValidator
|
from .validators import PointsExportValidator
|
||||||
|
from .validators import EpicStatusExportValidator
|
||||||
from .validators import UserStoryStatusExportValidator
|
from .validators import UserStoryStatusExportValidator
|
||||||
from .validators import TaskStatusExportValidator
|
from .validators import TaskStatusExportValidator
|
||||||
from .validators import IssueStatusExportValidator
|
from .validators import IssueStatusExportValidator
|
||||||
|
@ -6,6 +7,7 @@ from .validators import PriorityExportValidator
|
||||||
from .validators import SeverityExportValidator
|
from .validators import SeverityExportValidator
|
||||||
from .validators import IssueTypeExportValidator
|
from .validators import IssueTypeExportValidator
|
||||||
from .validators import RoleExportValidator
|
from .validators import RoleExportValidator
|
||||||
|
from .validators import EpicCustomAttributeExportValidator
|
||||||
from .validators import UserStoryCustomAttributeExportValidator
|
from .validators import UserStoryCustomAttributeExportValidator
|
||||||
from .validators import TaskCustomAttributeExportValidator
|
from .validators import TaskCustomAttributeExportValidator
|
||||||
from .validators import IssueCustomAttributeExportValidator
|
from .validators import IssueCustomAttributeExportValidator
|
||||||
|
@ -17,6 +19,8 @@ from .validators import MembershipExportValidator
|
||||||
from .validators import RolePointsExportValidator
|
from .validators import RolePointsExportValidator
|
||||||
from .validators import MilestoneExportValidator
|
from .validators import MilestoneExportValidator
|
||||||
from .validators import TaskExportValidator
|
from .validators import TaskExportValidator
|
||||||
|
from .validators import EpicRelatedUserStoryExportValidator
|
||||||
|
from .validators import EpicExportValidator
|
||||||
from .validators import UserStoryExportValidator
|
from .validators import UserStoryExportValidator
|
||||||
from .validators import IssueExportValidator
|
from .validators import IssueExportValidator
|
||||||
from .validators import WikiPageExportValidator
|
from .validators import WikiPageExportValidator
|
||||||
|
|
|
@ -22,6 +22,7 @@ _cache_user_by_pk = {}
|
||||||
_cache_user_by_email = {}
|
_cache_user_by_email = {}
|
||||||
_custom_tasks_attributes_cache = {}
|
_custom_tasks_attributes_cache = {}
|
||||||
_custom_issues_attributes_cache = {}
|
_custom_issues_attributes_cache = {}
|
||||||
|
_custom_epics_attributes_cache = {}
|
||||||
_custom_userstories_attributes_cache = {}
|
_custom_userstories_attributes_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ from taiga.base.exceptions import ValidationError
|
||||||
|
|
||||||
from taiga.projects import models as projects_models
|
from taiga.projects import models as projects_models
|
||||||
from taiga.projects.custom_attributes import models as custom_attributes_models
|
from taiga.projects.custom_attributes import models as custom_attributes_models
|
||||||
|
from taiga.projects.epics import models as epics_models
|
||||||
from taiga.projects.userstories import models as userstories_models
|
from taiga.projects.userstories import models as userstories_models
|
||||||
from taiga.projects.tasks import models as tasks_models
|
from taiga.projects.tasks import models as tasks_models
|
||||||
from taiga.projects.issues import models as issues_models
|
from taiga.projects.issues import models as issues_models
|
||||||
|
@ -38,6 +39,7 @@ from .fields import (FileField, UserRelatedField,
|
||||||
TimelineDataField, ContentTypeField)
|
TimelineDataField, ContentTypeField)
|
||||||
from .mixins import WatcheableObjectModelValidatorMixin
|
from .mixins import WatcheableObjectModelValidatorMixin
|
||||||
from .cache import (_custom_tasks_attributes_cache,
|
from .cache import (_custom_tasks_attributes_cache,
|
||||||
|
_custom_epics_attributes_cache,
|
||||||
_custom_userstories_attributes_cache,
|
_custom_userstories_attributes_cache,
|
||||||
_custom_issues_attributes_cache)
|
_custom_issues_attributes_cache)
|
||||||
|
|
||||||
|
@ -48,6 +50,12 @@ class PointsExportValidator(validators.ModelValidator):
|
||||||
exclude = ('id', 'project')
|
exclude = ('id', 'project')
|
||||||
|
|
||||||
|
|
||||||
|
class EpicStatusExportValidator(validators.ModelValidator):
|
||||||
|
class Meta:
|
||||||
|
model = projects_models.EpicStatus
|
||||||
|
exclude = ('id', 'project')
|
||||||
|
|
||||||
|
|
||||||
class UserStoryStatusExportValidator(validators.ModelValidator):
|
class UserStoryStatusExportValidator(validators.ModelValidator):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = projects_models.UserStoryStatus
|
model = projects_models.UserStoryStatus
|
||||||
|
@ -92,6 +100,14 @@ class RoleExportValidator(validators.ModelValidator):
|
||||||
exclude = ('id', 'project')
|
exclude = ('id', 'project')
|
||||||
|
|
||||||
|
|
||||||
|
class EpicCustomAttributeExportValidator(validators.ModelValidator):
|
||||||
|
modified_date = serializers.DateTimeField(required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = custom_attributes_models.EpicCustomAttribute
|
||||||
|
exclude = ('id', 'project')
|
||||||
|
|
||||||
|
|
||||||
class UserStoryCustomAttributeExportValidator(validators.ModelValidator):
|
class UserStoryCustomAttributeExportValidator(validators.ModelValidator):
|
||||||
modified_date = serializers.DateTimeField(required=False)
|
modified_date = serializers.DateTimeField(required=False)
|
||||||
|
|
||||||
|
@ -151,6 +167,15 @@ class BaseCustomAttributesValuesExportValidator(validators.ModelValidator):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class EpicCustomAttributesValuesExportValidator(BaseCustomAttributesValuesExportValidator):
|
||||||
|
_custom_attribute_model = custom_attributes_models.EpicCustomAttribute
|
||||||
|
_container_model = "epics.Epic"
|
||||||
|
_container_field = "epic"
|
||||||
|
|
||||||
|
class Meta(BaseCustomAttributesValuesExportValidator.Meta):
|
||||||
|
model = custom_attributes_models.EpicCustomAttributesValues
|
||||||
|
|
||||||
|
|
||||||
class UserStoryCustomAttributesValuesExportValidator(BaseCustomAttributesValuesExportValidator):
|
class UserStoryCustomAttributesValuesExportValidator(BaseCustomAttributesValuesExportValidator):
|
||||||
_custom_attribute_model = custom_attributes_models.UserStoryCustomAttribute
|
_custom_attribute_model = custom_attributes_models.UserStoryCustomAttribute
|
||||||
_container_model = "userstories.UserStory"
|
_container_model = "userstories.UserStory"
|
||||||
|
@ -244,6 +269,34 @@ class TaskExportValidator(WatcheableObjectModelValidatorMixin):
|
||||||
return _custom_tasks_attributes_cache[project.id]
|
return _custom_tasks_attributes_cache[project.id]
|
||||||
|
|
||||||
|
|
||||||
|
class EpicRelatedUserStoryExportValidator(validators.ModelValidator):
|
||||||
|
user_story = ProjectRelatedField(slug_field="ref")
|
||||||
|
order = serializers.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = epics_models.RelatedUserStory
|
||||||
|
exclude = ('id', 'epic')
|
||||||
|
|
||||||
|
|
||||||
|
class EpicExportValidator(WatcheableObjectModelValidatorMixin):
|
||||||
|
owner = UserRelatedField(required=False)
|
||||||
|
assigned_to = UserRelatedField(required=False)
|
||||||
|
status = ProjectRelatedField(slug_field="name")
|
||||||
|
modified_date = serializers.DateTimeField(required=False)
|
||||||
|
user_stories = EpicRelatedUserStoryExportValidator(many=True, required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = epics_models.Epic
|
||||||
|
exclude = ('id', 'project')
|
||||||
|
|
||||||
|
def custom_attributes_queryset(self, project):
|
||||||
|
if project.id not in _custom_epics_attributes_cache:
|
||||||
|
_custom_epics_attributes_cache[project.id] = list(
|
||||||
|
project.epiccustomattributes.all().values('id', 'name')
|
||||||
|
)
|
||||||
|
return _custom_epics_attributes_cache[project.id]
|
||||||
|
|
||||||
|
|
||||||
class UserStoryExportValidator(WatcheableObjectModelValidatorMixin):
|
class UserStoryExportValidator(WatcheableObjectModelValidatorMixin):
|
||||||
role_points = RolePointsExportValidator(many=True, required=False)
|
role_points = RolePointsExportValidator(many=True, required=False)
|
||||||
owner = UserRelatedField(required=False)
|
owner = UserRelatedField(required=False)
|
||||||
|
|
|
@ -42,3 +42,17 @@ def test_export_user_story_finish_date(client):
|
||||||
project_data = json.loads(output.getvalue())
|
project_data = json.loads(output.getvalue())
|
||||||
finish_date = project_data["user_stories"][0]["finish_date"]
|
finish_date = project_data["user_stories"][0]["finish_date"]
|
||||||
assert finish_date == "2014-10-22T00:00:00+0000"
|
assert finish_date == "2014-10-22T00:00:00+0000"
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_epic_with_user_stories(client):
|
||||||
|
epic = f.EpicFactory.create(subject="test epic export")
|
||||||
|
user_story = f.UserStoryFactory.create(project=epic.project)
|
||||||
|
f.RelatedUserStory.create(epic=epic, user_story=user_story)
|
||||||
|
output = io.BytesIO()
|
||||||
|
render_project(user_story.project, output)
|
||||||
|
project_data = json.loads(output.getvalue())
|
||||||
|
assert project_data["epics"][0]["subject"] == "test epic export"
|
||||||
|
assert len(project_data["epics"]) == 1
|
||||||
|
|
||||||
|
assert project_data["epics"][0]["related_user_stories"][0]["user_story"] == user_story.ref
|
||||||
|
assert len(project_data["epics"][0]["related_user_stories"]) == 1
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import io
|
||||||
|
from .. import factories as f
|
||||||
|
|
||||||
|
from taiga.base.utils import json
|
||||||
|
from taiga.export_import.services import render_project, store_project_from_dict
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_epic_with_user_stories(client):
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
project.default_points = f.PointsFactory.create(project=project)
|
||||||
|
project.default_issue_type = f.IssueTypeFactory.create(project=project)
|
||||||
|
project.default_issue_status = f.IssueStatusFactory.create(project=project)
|
||||||
|
project.default_epic_status = f.EpicStatusFactory.create(project=project)
|
||||||
|
project.default_us_status = f.UserStoryStatusFactory.create(project=project)
|
||||||
|
project.default_task_status = f.TaskStatusFactory.create(project=project)
|
||||||
|
project.default_priority = f.PriorityFactory.create(project=project)
|
||||||
|
project.default_severity = f.SeverityFactory.create(project=project)
|
||||||
|
|
||||||
|
epic = f.EpicFactory.create(subject="test epic export", project=project, status=project.default_epic_status)
|
||||||
|
user_story = f.UserStoryFactory.create(project=project, status=project.default_us_status, milestone=None)
|
||||||
|
f.RelatedUserStory.create(epic=epic, user_story=user_story, order=55)
|
||||||
|
output = io.BytesIO()
|
||||||
|
render_project(user_story.project, output)
|
||||||
|
project_data = json.loads(output.getvalue())
|
||||||
|
|
||||||
|
epic.project.delete()
|
||||||
|
|
||||||
|
project = store_project_from_dict(project_data)
|
||||||
|
assert project.epics.count() == 1
|
||||||
|
assert project.epics.first().ref == epic.ref
|
||||||
|
|
||||||
|
assert project.epics.first().user_stories.count() == 1
|
||||||
|
related_userstory = project.epics.first().relateduserstory_set.first()
|
||||||
|
assert related_userstory.user_story.ref == user_story.ref
|
||||||
|
assert related_userstory.order == 55
|
||||||
|
assert related_userstory.epic.ref == epic.ref
|
Loading…
Reference in New Issue