NODEDC_TASKMANAGER/plane-src/apps/api/plane/api/serializers/module.py

286 lines
9.1 KiB
Python

# 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 .base import BaseSerializer
from plane.db.models import (
User,
Module,
ModuleLink,
ModuleMember,
ModuleIssue,
ProjectMember,
Project,
)
class ModuleCreateSerializer(BaseSerializer):
"""
Serializer for creating modules with member validation and date checking.
Handles module creation including member assignment validation, date range
verification, and duplicate name prevention for feature-based
project organization setup.
"""
members = serializers.ListField(
child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()),
write_only=True,
required=False,
)
class Meta:
model = Module
fields = [
"name",
"description",
"start_date",
"target_date",
"status",
"lead",
"members",
"external_source",
"external_id",
]
read_only_fields = [
"id",
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
"deleted_at",
]
def validate(self, data):
project_id = self.context.get("project_id")
if not project_id:
raise serializers.ValidationError("Project ID is required")
project = Project.objects.get(id=project_id)
if not project:
raise serializers.ValidationError("Project not found")
if not project.module_view:
raise serializers.ValidationError("Modules are not enabled for this project")
if (
data.get("start_date", None) is not None
and data.get("target_date", None) is not None
and data.get("start_date", None) > data.get("target_date", None)
):
raise serializers.ValidationError("Start date cannot exceed target date")
if data.get("members", []):
data["members"] = ProjectMember.objects.filter(
project_id=self.context.get("project_id"), member_id__in=data["members"]
).values_list("member_id", flat=True)
return data
def create(self, validated_data):
members = validated_data.pop("members", None)
project_id = self.context["project_id"]
workspace_id = self.context["workspace_id"]
module_name = validated_data.get("name")
if module_name:
# Lookup for the module name in the module table for that project
module = Module.objects.filter(name=module_name, project_id=project_id).first()
if module:
raise serializers.ValidationError(
{
"id": str(module.id),
"code": "MODULE_NAME_ALREADY_EXISTS",
"error": "Module with this name already exists",
"message": "Module with this name already exists",
}
)
module = Module.objects.create(**validated_data, project_id=project_id)
if members is not None:
ModuleMember.objects.bulk_create(
[
ModuleMember(
module=module,
member_id=str(member),
project_id=project_id,
workspace_id=workspace_id,
created_by=module.created_by,
updated_by=module.updated_by,
)
for member in members
],
batch_size=10,
ignore_conflicts=True,
)
return module
class ModuleUpdateSerializer(ModuleCreateSerializer):
"""
Serializer for updating modules with enhanced validation and member management.
Extends module creation with update-specific validations including
member reassignment, name conflict checking,
and relationship management for module modifications.
"""
class Meta(ModuleCreateSerializer.Meta):
model = Module
fields = ModuleCreateSerializer.Meta.fields + [
"members",
]
read_only_fields = ModuleCreateSerializer.Meta.read_only_fields
def update(self, instance, validated_data):
members = validated_data.pop("members", None)
module_name = validated_data.get("name")
if module_name:
# Lookup for the module name in the module table for that project
if Module.objects.filter(name=module_name, project=instance.project).exclude(id=instance.id).exists():
raise serializers.ValidationError({"error": "Module with this name already exists"})
if members is not None:
ModuleMember.objects.filter(module=instance).delete()
ModuleMember.objects.bulk_create(
[
ModuleMember(
module=instance,
member_id=str(member),
project=instance.project,
workspace=instance.project.workspace,
created_by=instance.created_by,
updated_by=instance.updated_by,
)
for member in members
],
batch_size=10,
ignore_conflicts=True,
)
return super().update(instance, validated_data)
class ModuleSerializer(BaseSerializer):
"""
Comprehensive module serializer with work item metrics and member management.
Provides complete module data including work item counts by status, member
relationships, and progress tracking for feature-based project organization.
"""
members = serializers.ListField(
child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()),
write_only=True,
required=False,
)
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)
class Meta:
model = Module
fields = "__all__"
read_only_fields = [
"id",
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
"deleted_at",
]
def to_representation(self, instance):
data = super().to_representation(instance)
data["members"] = [str(member.id) for member in instance.members.all()]
return data
class ModuleIssueSerializer(BaseSerializer):
"""
Serializer for module-work item relationships with sub-item counting.
Manages the association between modules and work items, including
hierarchical issue tracking for nested work item structures.
"""
sub_issues_count = serializers.IntegerField(read_only=True)
class Meta:
model = ModuleIssue
fields = "__all__"
read_only_fields = [
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
"module",
]
class ModuleLinkSerializer(BaseSerializer):
"""
Serializer for module external links with URL validation.
Handles external resource associations with modules including
URL validation and duplicate prevention for reference management.
"""
class Meta:
model = ModuleLink
fields = "__all__"
read_only_fields = [
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
"module",
]
# Validation if url already exists
def create(self, validated_data):
if ModuleLink.objects.filter(url=validated_data.get("url"), module_id=validated_data.get("module_id")).exists():
raise serializers.ValidationError({"error": "URL already exists for this Issue"})
return ModuleLink.objects.create(**validated_data)
class ModuleLiteSerializer(BaseSerializer):
"""
Lightweight module serializer for minimal data transfer.
Provides essential module information without computed metrics,
optimized for list views and reference lookups.
"""
class Meta:
model = Module
fields = "__all__"
class ModuleIssueRequestSerializer(serializers.Serializer):
"""
Serializer for bulk work item assignment to modules.
Validates work item ID lists for batch operations including
module assignment and work item organization workflows.
"""
issues = serializers.ListField(
child=serializers.UUIDField(),
help_text="List of issue IDs to add to the module",
)