mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: separate out project feature model
This commit is contained in:
parent
532da80375
commit
6c6aa3b0d5
@ -8,6 +8,7 @@ from plane.db.models import (
|
|||||||
WorkspaceMember,
|
WorkspaceMember,
|
||||||
State,
|
State,
|
||||||
Estimate,
|
Estimate,
|
||||||
|
ProjectFeature,
|
||||||
)
|
)
|
||||||
from .base import BaseSerializer
|
from .base import BaseSerializer
|
||||||
|
|
||||||
@ -78,6 +79,7 @@ class ProjectSerializer(BaseSerializer):
|
|||||||
project = Project.objects.create(
|
project = Project.objects.create(
|
||||||
**validated_data, workspace_id=self.context["workspace_id"]
|
**validated_data, workspace_id=self.context["workspace_id"]
|
||||||
)
|
)
|
||||||
|
|
||||||
_ = ProjectIdentifier.objects.create(
|
_ = ProjectIdentifier.objects.create(
|
||||||
name=project.identifier,
|
name=project.identifier,
|
||||||
project=project,
|
project=project,
|
||||||
|
@ -11,7 +11,6 @@ from rest_framework.serializers import ValidationError
|
|||||||
from plane.db.models import (
|
from plane.db.models import (
|
||||||
Workspace,
|
Workspace,
|
||||||
Project,
|
Project,
|
||||||
ProjectFavorite,
|
|
||||||
ProjectMember,
|
ProjectMember,
|
||||||
ProjectDeployBoard,
|
ProjectDeployBoard,
|
||||||
State,
|
State,
|
||||||
@ -19,6 +18,7 @@ from plane.db.models import (
|
|||||||
Module,
|
Module,
|
||||||
IssueProperty,
|
IssueProperty,
|
||||||
Inbox,
|
Inbox,
|
||||||
|
ProjectFeature,
|
||||||
)
|
)
|
||||||
from plane.app.permissions import ProjectBasePermission
|
from plane.app.permissions import ProjectBasePermission
|
||||||
from plane.api.serializers import ProjectSerializer
|
from plane.api.serializers import ProjectSerializer
|
||||||
@ -149,6 +149,11 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView):
|
|||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
|
|
||||||
|
# features
|
||||||
|
_ = ProjectFeature.objects.create(
|
||||||
|
project_id=serializer.data["id"]
|
||||||
|
)
|
||||||
|
|
||||||
# Add the user as Administrator to the project
|
# Add the user as Administrator to the project
|
||||||
project_member = ProjectMember.objects.create(
|
project_member = ProjectMember.objects.create(
|
||||||
project_id=serializer.data["id"],
|
project_id=serializer.data["id"],
|
||||||
|
@ -21,6 +21,7 @@ from .workspace import (
|
|||||||
)
|
)
|
||||||
from .project import (
|
from .project import (
|
||||||
ProjectSerializer,
|
ProjectSerializer,
|
||||||
|
ProjectFeatureSerializer,
|
||||||
ProjectListSerializer,
|
ProjectListSerializer,
|
||||||
ProjectDetailSerializer,
|
ProjectDetailSerializer,
|
||||||
ProjectMemberSerializer,
|
ProjectMemberSerializer,
|
||||||
|
@ -16,6 +16,7 @@ from plane.db.models import (
|
|||||||
ProjectFavorite,
|
ProjectFavorite,
|
||||||
ProjectDeployBoard,
|
ProjectDeployBoard,
|
||||||
ProjectPublicMember,
|
ProjectPublicMember,
|
||||||
|
ProjectFeature,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +48,8 @@ class ProjectSerializer(BaseSerializer):
|
|||||||
project = Project.objects.create(
|
project = Project.objects.create(
|
||||||
**validated_data, workspace_id=self.context["workspace_id"]
|
**validated_data, workspace_id=self.context["workspace_id"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Create identifiers
|
||||||
_ = ProjectIdentifier.objects.create(
|
_ = ProjectIdentifier.objects.create(
|
||||||
name=project.identifier,
|
name=project.identifier,
|
||||||
project=project,
|
project=project,
|
||||||
@ -237,3 +240,14 @@ class ProjectPublicMemberSerializer(BaseSerializer):
|
|||||||
"project",
|
"project",
|
||||||
"member",
|
"member",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectFeatureSerializer(BaseSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ProjectFeature
|
||||||
|
fields = "__all__"
|
||||||
|
read_only_fields = [
|
||||||
|
"project",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
|
@ -14,6 +14,7 @@ from plane.app.views import (
|
|||||||
ProjectPublicCoverImagesEndpoint,
|
ProjectPublicCoverImagesEndpoint,
|
||||||
ProjectDeployBoardViewSet,
|
ProjectDeployBoardViewSet,
|
||||||
UserProjectRolesEndpoint,
|
UserProjectRolesEndpoint,
|
||||||
|
ProjectFeatureEndpoint,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -175,4 +176,9 @@ urlpatterns = [
|
|||||||
),
|
),
|
||||||
name="project-deploy-board",
|
name="project-deploy-board",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"workspaces/<str:slug>/projects/<uuid:project_id>/project-features/",
|
||||||
|
ProjectFeatureEndpoint.as_view(),
|
||||||
|
name="project-feature",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -12,6 +12,7 @@ from .project import (
|
|||||||
ProjectPublicCoverImagesEndpoint,
|
ProjectPublicCoverImagesEndpoint,
|
||||||
ProjectDeployBoardViewSet,
|
ProjectDeployBoardViewSet,
|
||||||
UserProjectRolesEndpoint,
|
UserProjectRolesEndpoint,
|
||||||
|
ProjectFeatureEndpoint,
|
||||||
)
|
)
|
||||||
from .user import (
|
from .user import (
|
||||||
UserEndpoint,
|
UserEndpoint,
|
||||||
|
@ -37,6 +37,7 @@ from plane.app.serializers import (
|
|||||||
ProjectDeployBoardSerializer,
|
ProjectDeployBoardSerializer,
|
||||||
ProjectMemberAdminSerializer,
|
ProjectMemberAdminSerializer,
|
||||||
ProjectMemberRoleSerializer,
|
ProjectMemberRoleSerializer,
|
||||||
|
ProjectFeatureSerializer,
|
||||||
)
|
)
|
||||||
|
|
||||||
from plane.app.permissions import (
|
from plane.app.permissions import (
|
||||||
@ -62,6 +63,7 @@ from plane.db.models import (
|
|||||||
Inbox,
|
Inbox,
|
||||||
ProjectDeployBoard,
|
ProjectDeployBoard,
|
||||||
IssueProperty,
|
IssueProperty,
|
||||||
|
ProjectFeature,
|
||||||
)
|
)
|
||||||
|
|
||||||
from plane.bgtasks.project_invitation_task import project_invitation
|
from plane.bgtasks.project_invitation_task import project_invitation
|
||||||
@ -203,6 +205,10 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
|
|||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
|
|
||||||
|
_ = ProjectFeature.objects.create(
|
||||||
|
project_id=serializer.data["id"]
|
||||||
|
)
|
||||||
|
|
||||||
# Add the user as Administrator to the project
|
# Add the user as Administrator to the project
|
||||||
project_member = ProjectMember.objects.create(
|
project_member = ProjectMember.objects.create(
|
||||||
project_id=serializer.data["id"],
|
project_id=serializer.data["id"],
|
||||||
@ -686,9 +692,14 @@ class ProjectMemberViewSet(BaseViewSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
bulk_project_members = []
|
bulk_project_members = []
|
||||||
member_roles = {member.get("member_id"): member.get("role") for member in members}
|
member_roles = {
|
||||||
|
member.get("member_id"): member.get("role") for member in members
|
||||||
|
}
|
||||||
# Update roles in the members array based on the member_roles dictionary
|
# Update roles in the members array based on the member_roles dictionary
|
||||||
for project_member in ProjectMember.objects.filter(project_id=project_id, member_id__in=[member.get("member_id") for member in members]):
|
for project_member in ProjectMember.objects.filter(
|
||||||
|
project_id=project_id,
|
||||||
|
member_id__in=[member.get("member_id") for member in members],
|
||||||
|
):
|
||||||
project_member.role = member_roles[str(project_member.member_id)]
|
project_member.role = member_roles[str(project_member.member_id)]
|
||||||
project_member.is_active = True
|
project_member.is_active = True
|
||||||
bulk_project_members.append(project_member)
|
bulk_project_members.append(project_member)
|
||||||
@ -734,7 +745,10 @@ class ProjectMemberViewSet(BaseViewSet):
|
|||||||
bulk_issue_props, batch_size=10, ignore_conflicts=True
|
bulk_issue_props, batch_size=10, ignore_conflicts=True
|
||||||
)
|
)
|
||||||
|
|
||||||
project_members = ProjectMember.objects.filter(project_id=project_id, member_id__in=[member.get("member_id") for member in members])
|
project_members = ProjectMember.objects.filter(
|
||||||
|
project_id=project_id,
|
||||||
|
member_id__in=[member.get("member_id") for member in members],
|
||||||
|
)
|
||||||
serializer = ProjectMemberRoleSerializer(project_members, many=True)
|
serializer = ProjectMemberRoleSerializer(project_members, many=True)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@ -1138,3 +1152,29 @@ class UserProjectRolesEndpoint(BaseAPIView):
|
|||||||
for member in project_members
|
for member in project_members
|
||||||
}
|
}
|
||||||
return Response(project_members, status=status.HTTP_200_OK)
|
return Response(project_members, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectFeatureEndpoint(BaseAPIView):
|
||||||
|
permission_classes = [
|
||||||
|
ProjectBasePermission,
|
||||||
|
]
|
||||||
|
|
||||||
|
def get(self, request, slug, project_id):
|
||||||
|
project_feature = ProjectFeature.objects.get(
|
||||||
|
workspace__slug=slug, project_id=project_id
|
||||||
|
)
|
||||||
|
serializer = ProjectFeatureSerializer(project_feature)
|
||||||
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def patch(self, request, slug, project_id):
|
||||||
|
project_feature = ProjectFeature.objects.get(
|
||||||
|
workspace__slug=slug,
|
||||||
|
project_id=project_id,
|
||||||
|
)
|
||||||
|
serializer = ProjectFeatureSerializer(
|
||||||
|
project_feature, data=request.data, partial=True
|
||||||
|
)
|
||||||
|
if serializer.is_valid():
|
||||||
|
serializer.save()
|
||||||
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2024-01-29 07:11
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
def create_project_preferences(apps, schema_editor):
|
||||||
|
ProjectFeature = apps.get_model("db", "ProjectFeature")
|
||||||
|
Project = apps.get_model("db", "Project")
|
||||||
|
|
||||||
|
ProjectFeature.objects.bulk_create(
|
||||||
|
[
|
||||||
|
ProjectFeature(
|
||||||
|
project_id=project.get("id"),
|
||||||
|
workspace_id=project.get("workspace_id"),
|
||||||
|
cycles=project.get("cycle_view"),
|
||||||
|
modules=project.get("module_view"),
|
||||||
|
views=project.get("issue_views_view"),
|
||||||
|
pages=project.get("page_view"),
|
||||||
|
inbox=project.get("inbox_view"),
|
||||||
|
)
|
||||||
|
for project in Project.objects.values(
|
||||||
|
"id",
|
||||||
|
"workspace_id",
|
||||||
|
"cycle_view",
|
||||||
|
"inbox_view",
|
||||||
|
"issue_views_view",
|
||||||
|
"page_view",
|
||||||
|
"module_view",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
batch_size=100,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("db", "0057_auto_20240122_0901"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="ProjectFeature",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"created_at",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now_add=True, verbose_name="Created At"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"updated_at",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now=True, verbose_name="Last Modified At"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.UUIDField(
|
||||||
|
db_index=True,
|
||||||
|
default=uuid.uuid4,
|
||||||
|
editable=False,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
unique=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("modules", models.BooleanField(default=True)),
|
||||||
|
("cycles", models.BooleanField(default=True)),
|
||||||
|
("views", models.BooleanField(default=True)),
|
||||||
|
("pages", models.BooleanField(default=True)),
|
||||||
|
("inbox", models.BooleanField(default=False)),
|
||||||
|
(
|
||||||
|
"created_by",
|
||||||
|
models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="%(class)s_created_by",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name="Created By",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"project",
|
||||||
|
models.OneToOneField(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="features",
|
||||||
|
to="db.project",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"updated_by",
|
||||||
|
models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="%(class)s_updated_by",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name="Last Modified By",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"workspace",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="project_features",
|
||||||
|
to="db.workspace",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "Project Feature",
|
||||||
|
"verbose_name_plural": "Project Features",
|
||||||
|
"db_table": "project_features",
|
||||||
|
"ordering": ("-created_at",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RunPython(create_project_preferences),
|
||||||
|
]
|
@ -22,6 +22,7 @@ from .project import (
|
|||||||
ProjectFavorite,
|
ProjectFavorite,
|
||||||
ProjectDeployBoard,
|
ProjectDeployBoard,
|
||||||
ProjectPublicMember,
|
ProjectPublicMember,
|
||||||
|
ProjectFeature,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .issue import (
|
from .issue import (
|
||||||
|
@ -89,11 +89,6 @@ class Project(BaseModel):
|
|||||||
)
|
)
|
||||||
emoji = models.CharField(max_length=255, null=True, blank=True)
|
emoji = models.CharField(max_length=255, null=True, blank=True)
|
||||||
icon_prop = models.JSONField(null=True)
|
icon_prop = models.JSONField(null=True)
|
||||||
module_view = models.BooleanField(default=True)
|
|
||||||
cycle_view = models.BooleanField(default=True)
|
|
||||||
issue_views_view = models.BooleanField(default=True)
|
|
||||||
page_view = models.BooleanField(default=True)
|
|
||||||
inbox_view = models.BooleanField(default=False)
|
|
||||||
cover_image = models.URLField(blank=True, null=True, max_length=800)
|
cover_image = models.URLField(blank=True, null=True, max_length=800)
|
||||||
estimate = models.ForeignKey(
|
estimate = models.ForeignKey(
|
||||||
"db.Estimate",
|
"db.Estimate",
|
||||||
@ -146,6 +141,36 @@ class ProjectBaseModel(BaseModel):
|
|||||||
super(ProjectBaseModel, self).save(*args, **kwargs)
|
super(ProjectBaseModel, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectFeature(BaseModel):
|
||||||
|
workspace = models.ForeignKey(
|
||||||
|
"db.Workspace",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="project_features",
|
||||||
|
)
|
||||||
|
project = models.OneToOneField(
|
||||||
|
Project, on_delete=models.CASCADE, related_name="features"
|
||||||
|
)
|
||||||
|
modules = models.BooleanField(default=False)
|
||||||
|
cycles = models.BooleanField(default=False)
|
||||||
|
views = models.BooleanField(default=False)
|
||||||
|
pages = models.BooleanField(default=True)
|
||||||
|
inbox = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.workspace = self.project.workspace
|
||||||
|
super(ProjectFeature, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return feature of the project"""
|
||||||
|
return f"{self.project.name} <{self.id}>"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Project Feature"
|
||||||
|
verbose_name_plural = "Project Features"
|
||||||
|
db_table = "project_features"
|
||||||
|
ordering = ("-created_at",)
|
||||||
|
|
||||||
|
|
||||||
class ProjectMemberInvite(ProjectBaseModel):
|
class ProjectMemberInvite(ProjectBaseModel):
|
||||||
email = models.CharField(max_length=255)
|
email = models.CharField(max_length=255)
|
||||||
accepted = models.BooleanField(default=False)
|
accepted = models.BooleanField(default=False)
|
||||||
|
Loading…
Reference in New Issue
Block a user