base
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# See the LICENSE file for details.
|
||||
|
||||
from .issue import IssueExportSerializer
|
||||
|
||||
__all__ = [
|
||||
# Export Serializers
|
||||
"IssueExportSerializer",
|
||||
]
|
||||
@@ -0,0 +1,145 @@
|
||||
# Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# See the LICENSE file for details.
|
||||
|
||||
# Third party imports
|
||||
from rest_framework import serializers
|
||||
|
||||
# Module imports
|
||||
from plane.app.serializers import IssueSerializer
|
||||
|
||||
|
||||
class IssueExportSerializer(IssueSerializer):
|
||||
"""
|
||||
Export-optimized serializer that extends IssueSerializer with human-readable fields.
|
||||
|
||||
Converts UUIDs to readable values for CSV/JSON export.
|
||||
"""
|
||||
|
||||
identifier = serializers.SerializerMethodField()
|
||||
project_name = serializers.CharField(source='project.name', read_only=True, default="")
|
||||
project_identifier = serializers.CharField(source='project.identifier', read_only=True, default="")
|
||||
state_name = serializers.CharField(source='state.name', read_only=True, default="")
|
||||
created_by_name = serializers.CharField(source='created_by.full_name', read_only=True, default="")
|
||||
|
||||
assignees = serializers.SerializerMethodField()
|
||||
parent = serializers.SerializerMethodField()
|
||||
labels = serializers.SerializerMethodField()
|
||||
cycles = serializers.SerializerMethodField()
|
||||
modules = serializers.SerializerMethodField()
|
||||
comments = serializers.SerializerMethodField()
|
||||
estimate = serializers.SerializerMethodField()
|
||||
links = serializers.SerializerMethodField()
|
||||
relations = serializers.SerializerMethodField()
|
||||
subscribers = serializers.SerializerMethodField()
|
||||
|
||||
class Meta(IssueSerializer.Meta):
|
||||
fields = [
|
||||
"project_name",
|
||||
"project_identifier",
|
||||
"parent",
|
||||
"identifier",
|
||||
"sequence_id",
|
||||
"name",
|
||||
"state_name",
|
||||
"priority",
|
||||
"assignees",
|
||||
"subscribers",
|
||||
"created_by_name",
|
||||
"start_date",
|
||||
"target_date",
|
||||
"completed_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"archived_at",
|
||||
"estimate",
|
||||
"labels",
|
||||
"cycles",
|
||||
"modules",
|
||||
"links",
|
||||
"relations",
|
||||
"comments",
|
||||
"sub_issues_count",
|
||||
"link_count",
|
||||
"attachment_count",
|
||||
"is_draft",
|
||||
]
|
||||
|
||||
def get_identifier(self, obj):
|
||||
return f"{obj.project.identifier}-{obj.sequence_id}"
|
||||
|
||||
def get_assignees(self, obj):
|
||||
return [u.full_name for u in obj.assignees.all() if u.is_active]
|
||||
|
||||
def get_subscribers(self, obj):
|
||||
"""Return list of subscriber names."""
|
||||
return [sub.subscriber.full_name for sub in obj.issue_subscribers.all() if sub.subscriber]
|
||||
|
||||
def get_parent(self, obj):
|
||||
if not obj.parent:
|
||||
return ""
|
||||
return f"{obj.parent.project.identifier}-{obj.parent.sequence_id}"
|
||||
|
||||
def get_labels(self, obj):
|
||||
return [
|
||||
il.label.name
|
||||
for il in obj.label_issue.all()
|
||||
if il.deleted_at is None
|
||||
]
|
||||
|
||||
def get_cycles(self, obj):
|
||||
return [ic.cycle.name for ic in obj.issue_cycle.all()]
|
||||
|
||||
def get_modules(self, obj):
|
||||
return [im.module.name for im in obj.issue_module.all()]
|
||||
|
||||
def get_estimate(self, obj):
|
||||
"""Return estimate point value."""
|
||||
if obj.estimate_point:
|
||||
return obj.estimate_point.value if hasattr(obj.estimate_point, 'value') else str(obj.estimate_point)
|
||||
return ""
|
||||
|
||||
def get_links(self, obj):
|
||||
"""Return list of issue links with titles."""
|
||||
return [
|
||||
{
|
||||
"url": link.url,
|
||||
"title": link.title if link.title else link.url,
|
||||
}
|
||||
for link in obj.issue_link.all()
|
||||
]
|
||||
|
||||
def get_relations(self, obj):
|
||||
"""Return list of related issues."""
|
||||
relations = []
|
||||
|
||||
# Outgoing relations (this issue relates to others)
|
||||
for rel in obj.issue_relation.all():
|
||||
if rel.related_issue:
|
||||
relations.append({
|
||||
"type": rel.relation_type if hasattr(rel, 'relation_type') else "related",
|
||||
"issue": f"{rel.related_issue.project.identifier}-{rel.related_issue.sequence_id}",
|
||||
"direction": "outgoing"
|
||||
})
|
||||
|
||||
# Incoming relations (other issues relate to this one)
|
||||
for rel in obj.issue_related.all():
|
||||
if rel.issue:
|
||||
relations.append({
|
||||
"type": rel.relation_type if hasattr(rel, 'relation_type') else "related",
|
||||
"issue": f"{rel.issue.project.identifier}-{rel.issue.sequence_id}",
|
||||
"direction": "incoming"
|
||||
})
|
||||
|
||||
return relations
|
||||
|
||||
def get_comments(self, obj):
|
||||
"""Return list of comments with author and timestamp."""
|
||||
return [
|
||||
{
|
||||
"comment": comment.comment_stripped if hasattr(comment, 'comment_stripped') else comment.comment_html,
|
||||
"created_by": comment.actor.full_name if comment.actor else "",
|
||||
"created_at": comment.created_at.strftime("%Y-%m-%d %H:%M:%S") if comment.created_at else "",
|
||||
}
|
||||
for comment in obj.issue_comments.all()
|
||||
]
|
||||
Reference in New Issue
Block a user