* feat: set default owner for cycle creation if not provided * Updated CycleListCreateAPIEndpoint to assign the current user as the owner when the 'owned_by' field is not included in the request data. * Enhanced the CycleCreateSerializer initialization to ensure proper ownership assignment during cycle creation. * feat: add comprehensive tests for Cycle API endpoints * Introduced a new test suite for Cycle API endpoints, covering creation, retrieval, updating, and deletion of cycles. * Implemented tests for various scenarios including successful operations, invalid data handling, and conflict resolution with external IDs. * Enhanced test coverage for listing cycles with different view filters and verifying cycle metrics annotations. * feat: enhance CycleCreateSerializer to include ownership assignment * Added 'owned_by' field to CycleCreateSerializer to specify the user who owns the cycle. * Updated CycleListCreateAPIEndpoint to remove redundant ownership assignment logic, relying on the serializer to handle default ownership. * Ensured that if 'owned_by' is not provided, it defaults to the current user during cycle creation. * fix: correct assertion syntax in CycleListCreateAPIEndpoint tests * Updated the assertion in the test for successful cycle creation to use the correct syntax for checking the response status code. * Ensured that the test accurately verifies the expected behavior of the API endpoint.
186 lines
5.9 KiB
Python
186 lines
5.9 KiB
Python
# Third party imports
|
|
import pytz
|
|
from rest_framework import serializers
|
|
|
|
# Module imports
|
|
from .base import BaseSerializer
|
|
from plane.db.models import Cycle, CycleIssue, User
|
|
from plane.utils.timezone_converter import convert_to_utc
|
|
|
|
|
|
class CycleCreateSerializer(BaseSerializer):
|
|
"""
|
|
Serializer for creating cycles with timezone handling and date validation.
|
|
|
|
Manages cycle creation including project timezone conversion, date range validation,
|
|
and UTC normalization for time-bound iteration planning and sprint management.
|
|
"""
|
|
|
|
owned_by = serializers.PrimaryKeyRelatedField(
|
|
queryset=User.objects.all(),
|
|
required=False,
|
|
allow_null=True,
|
|
help_text="User who owns the cycle. If not provided, defaults to the current user.",
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
project = self.context.get("project")
|
|
if project and project.timezone:
|
|
project_timezone = pytz.timezone(project.timezone)
|
|
self.fields["start_date"].timezone = project_timezone
|
|
self.fields["end_date"].timezone = project_timezone
|
|
|
|
class Meta:
|
|
model = Cycle
|
|
fields = [
|
|
"name",
|
|
"description",
|
|
"start_date",
|
|
"end_date",
|
|
"owned_by",
|
|
"external_source",
|
|
"external_id",
|
|
"timezone",
|
|
]
|
|
read_only_fields = [
|
|
"id",
|
|
"workspace",
|
|
"project",
|
|
"created_by",
|
|
"updated_by",
|
|
"created_at",
|
|
"updated_at",
|
|
"deleted_at",
|
|
]
|
|
|
|
def validate(self, data):
|
|
if (
|
|
data.get("start_date", None) is not None
|
|
and data.get("end_date", None) is not None
|
|
and data.get("start_date", None) > data.get("end_date", None)
|
|
):
|
|
raise serializers.ValidationError("Start date cannot exceed end date")
|
|
|
|
if data.get("start_date", None) is not None and data.get("end_date", None) is not None:
|
|
project_id = self.initial_data.get("project_id") or (
|
|
self.instance.project_id if self.instance and hasattr(self.instance, "project_id") else None
|
|
)
|
|
|
|
if not project_id:
|
|
raise serializers.ValidationError("Project ID is required")
|
|
|
|
data["start_date"] = convert_to_utc(
|
|
date=str(data.get("start_date").date()),
|
|
project_id=project_id,
|
|
is_start_date=True,
|
|
)
|
|
data["end_date"] = convert_to_utc(
|
|
date=str(data.get("end_date", None).date()),
|
|
project_id=project_id,
|
|
)
|
|
|
|
if not data.get("owned_by"):
|
|
data["owned_by"] = self.context["request"].user
|
|
|
|
return data
|
|
|
|
|
|
class CycleUpdateSerializer(CycleCreateSerializer):
|
|
"""
|
|
Serializer for updating cycles with enhanced ownership management.
|
|
|
|
Extends cycle creation with update-specific features including ownership
|
|
assignment and modification tracking for cycle lifecycle management.
|
|
"""
|
|
|
|
class Meta(CycleCreateSerializer.Meta):
|
|
model = Cycle
|
|
fields = CycleCreateSerializer.Meta.fields + [
|
|
"owned_by",
|
|
]
|
|
|
|
|
|
class CycleSerializer(BaseSerializer):
|
|
"""
|
|
Cycle serializer with comprehensive project metrics and time tracking.
|
|
|
|
Provides cycle details including work item counts by status, progress estimates,
|
|
and time-bound iteration data for project management and sprint planning.
|
|
"""
|
|
|
|
total_issues = serializers.IntegerField(read_only=True)
|
|
cancelled_issues = serializers.IntegerField(read_only=True)
|
|
completed_issues = serializers.IntegerField(read_only=True)
|
|
started_issues = serializers.IntegerField(read_only=True)
|
|
unstarted_issues = serializers.IntegerField(read_only=True)
|
|
backlog_issues = serializers.IntegerField(read_only=True)
|
|
total_estimates = serializers.FloatField(read_only=True)
|
|
completed_estimates = serializers.FloatField(read_only=True)
|
|
started_estimates = serializers.FloatField(read_only=True)
|
|
|
|
class Meta:
|
|
model = Cycle
|
|
fields = "__all__"
|
|
read_only_fields = [
|
|
"id",
|
|
"created_at",
|
|
"updated_at",
|
|
"created_by",
|
|
"updated_by",
|
|
"workspace",
|
|
"project",
|
|
"owned_by",
|
|
"deleted_at",
|
|
]
|
|
|
|
|
|
class CycleIssueSerializer(BaseSerializer):
|
|
"""
|
|
Serializer for cycle-issue relationships with sub-issue counting.
|
|
|
|
Manages the association between cycles and work items, including
|
|
hierarchical issue tracking for nested work item structures.
|
|
"""
|
|
|
|
sub_issues_count = serializers.IntegerField(read_only=True)
|
|
|
|
class Meta:
|
|
model = CycleIssue
|
|
fields = "__all__"
|
|
read_only_fields = ["workspace", "project", "cycle"]
|
|
|
|
|
|
class CycleLiteSerializer(BaseSerializer):
|
|
"""
|
|
Lightweight cycle serializer for minimal data transfer.
|
|
|
|
Provides essential cycle information without computed metrics,
|
|
optimized for list views and reference lookups.
|
|
"""
|
|
|
|
class Meta:
|
|
model = Cycle
|
|
fields = "__all__"
|
|
|
|
|
|
class CycleIssueRequestSerializer(serializers.Serializer):
|
|
"""
|
|
Serializer for bulk work item assignment to cycles.
|
|
|
|
Validates work item ID lists for batch operations including
|
|
cycle assignment and sprint planning workflows.
|
|
"""
|
|
|
|
issues = serializers.ListField(child=serializers.UUIDField(), help_text="List of issue IDs to add to the cycle")
|
|
|
|
|
|
class TransferCycleIssueRequestSerializer(serializers.Serializer):
|
|
"""
|
|
Serializer for transferring work items between cycles.
|
|
|
|
Handles work item migration between cycles including validation
|
|
and relationship updates for sprint reallocation workflows.
|
|
"""
|
|
|
|
new_cycle_id = serializers.UUIDField(help_text="ID of the target cycle to transfer issues to")
|