chore: separate out project feature model

This commit is contained in:
pablohashescobar 2024-01-29 14:13:42 +05:30
parent 532da80375
commit 6c6aa3b0d5
10 changed files with 224 additions and 9 deletions

View File

@ -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,

View File

@ -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"],

View File

@ -21,6 +21,7 @@ from .workspace import (
) )
from .project import ( from .project import (
ProjectSerializer, ProjectSerializer,
ProjectFeatureSerializer,
ProjectListSerializer, ProjectListSerializer,
ProjectDetailSerializer, ProjectDetailSerializer,
ProjectMemberSerializer, ProjectMemberSerializer,

View File

@ -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",
]

View File

@ -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",
),
] ]

View File

@ -12,6 +12,7 @@ from .project import (
ProjectPublicCoverImagesEndpoint, ProjectPublicCoverImagesEndpoint,
ProjectDeployBoardViewSet, ProjectDeployBoardViewSet,
UserProjectRolesEndpoint, UserProjectRolesEndpoint,
ProjectFeatureEndpoint,
) )
from .user import ( from .user import (
UserEndpoint, UserEndpoint,

View File

@ -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)

View File

@ -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),
]

View File

@ -22,6 +22,7 @@ from .project import (
ProjectFavorite, ProjectFavorite,
ProjectDeployBoard, ProjectDeployBoard,
ProjectPublicMember, ProjectPublicMember,
ProjectFeature,
) )
from .issue import ( from .issue import (

View File

@ -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)