dev: create model for project setting

This commit is contained in:
pablohashescobar 2023-10-11 19:24:45 +05:30
parent 58ea4d6ec9
commit 03f9ca45d8
8 changed files with 264 additions and 39 deletions

View File

@ -11,6 +11,7 @@ from .workspace import (
) )
from .project import ( from .project import (
ProjectSerializer, ProjectSerializer,
ProjectSettingSerializer,
ProjectDetailSerializer, ProjectDetailSerializer,
ProjectMemberSerializer, ProjectMemberSerializer,
ProjectMemberInviteSerializer, ProjectMemberInviteSerializer,

View File

@ -10,6 +10,7 @@ from plane.api.serializers.workspace import WorkSpaceSerializer, WorkspaceLiteSe
from plane.api.serializers.user import UserLiteSerializer, UserAdminLiteSerializer from plane.api.serializers.user import UserLiteSerializer, UserAdminLiteSerializer
from plane.db.models import ( from plane.db.models import (
Project, Project,
ProjectSetting,
ProjectMember, ProjectMember,
ProjectMemberInvite, ProjectMemberInvite,
ProjectIdentifier, ProjectIdentifier,
@ -192,3 +193,14 @@ class ProjectPublicMemberSerializer(BaseSerializer):
"project", "project",
"member", "member",
] ]
class ProjectSettingSerializer(BaseSerializer):
class Meta:
model = ProjectSetting
fields = "__all__"
read_only_fields = [
"workspace",
"project",
]

View File

@ -59,6 +59,7 @@ from plane.api.views import (
## End File Assets ## End File Assets
# Projects # Projects
ProjectViewSet, ProjectViewSet,
ProjectSettingViewSet,
InviteProjectEndpoint, InviteProjectEndpoint,
ProjectMemberViewSet, ProjectMemberViewSet,
ProjectMemberEndpoint, ProjectMemberEndpoint,
@ -481,6 +482,26 @@ urlpatterns = [
), ),
name="project", name="project",
), ),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/settings/",
ProjectSettingViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="project",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/settings/",
ProjectSettingViewSet.as_view(
{
"get": "retrieve",
"patch": "partial_update",
}
),
name="project",
),
path( path(
"workspaces/<str:slug>/project-identifiers/", "workspaces/<str:slug>/project-identifiers/",
ProjectIdentifierEndpoint.as_view(), ProjectIdentifierEndpoint.as_view(),

View File

@ -1,5 +1,6 @@
from .project import ( from .project import (
ProjectViewSet, ProjectViewSet,
ProjectSettingViewSet,
ProjectMemberViewSet, ProjectMemberViewSet,
UserProjectInvitationsViewset, UserProjectInvitationsViewset,
InviteProjectEndpoint, InviteProjectEndpoint,

View File

@ -29,11 +29,11 @@ from sentry_sdk import capture_exception
from .base import BaseViewSet, BaseAPIView from .base import BaseViewSet, BaseAPIView
from plane.api.serializers import ( from plane.api.serializers import (
ProjectSerializer, ProjectSerializer,
ProjectSettingSerializer,
ProjectMemberSerializer, ProjectMemberSerializer,
ProjectDetailSerializer, ProjectDetailSerializer,
ProjectMemberInviteSerializer, ProjectMemberInviteSerializer,
ProjectFavoriteSerializer, ProjectFavoriteSerializer,
IssueLiteSerializer,
ProjectDeployBoardSerializer, ProjectDeployBoardSerializer,
ProjectMemberAdminSerializer, ProjectMemberAdminSerializer,
) )
@ -67,6 +67,7 @@ from plane.db.models import (
ModuleMember, ModuleMember,
Inbox, Inbox,
ProjectDeployBoard, ProjectDeployBoard,
ProjectSetting,
) )
from plane.bgtasks.project_invitation_task import project_invitation from plane.bgtasks.project_invitation_task import project_invitation
@ -1246,3 +1247,20 @@ class ProjectPublicCoverImagesEndpoint(BaseAPIView):
except Exception as e: except Exception as e:
capture_exception(e) capture_exception(e)
return Response([], status=status.HTTP_200_OK) return Response([], status=status.HTTP_200_OK)
class ProjectSettingViewSet(BaseViewSet):
model = ProjectSetting
permission_classes = [
ProjectBasePermission,
]
serializer_class = ProjectSettingSerializer
def get_queryset(self):
super().get_queryset().filter(
workspace__slug=self.kwargs.get("slug"),
project_id=self.kwargs.get("project_id"),
)
def perform_create(self, serializer):
serializer.save(project_id=self.kwargs.get("project_id"))

View File

@ -0,0 +1,123 @@
# Generated by Django 4.2.3 on 2023-10-11 13:34
import uuid
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
def create_project_settings(apps, schema_editor):
Project = apps.get_model("db", "Project")
ProjectSetting = apps.get_model("db", "ProjectSetting")
ProjectSetting.objects.bulk_create(
[
ProjectSetting(
project=project,
workspace_id=project.workspace_id,
archive_in=project.archive_in,
close_in=project.close_in,
cycle_view=project.cycle_view,
default_assignee=project.default_assignee,
default_state=project.default_state,
estimate=project.estimate,
inbox_view=project.inbox_view,
issue_views_view=project.issue_views_view,
module_view=project.module_view,
page_view=project.page_view,
project_lead=project.project_lead,
)
for project in Project.objects.all()
],
batch_size=1000,
)
class Migration(migrations.Migration):
dependencies = [
('db', '0045_issueactivity_epoch_workspacemember_issue_props_and_more'),
]
operations = [
migrations.CreateModel(
name='ProjectSetting',
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)),
('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)),
('archive_in', models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(12)])),
('close_in', models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(12)])),
('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')),
('default_assignee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='default_assignee', to=settings.AUTH_USER_MODEL)),
('default_state', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_state', to='db.state')),
('estimate', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projects', to='db.estimate')),
('project', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='project_settings', to='db.project')),
('project_lead', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_lead', to=settings.AUTH_USER_MODEL)),
('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_settings', to='db.workspace')),
],
options={
'verbose_name': 'Project Settings',
'verbose_name_plural': 'Project Settings',
'db_table': 'project_settings',
'ordering': ('-created_at',),
},
),
migrations.RunPython(create_project_settings),
migrations.RemoveField(
model_name='project',
name='archive_in',
),
migrations.RemoveField(
name='close_in',
),
migrations.RemoveField(
model_name='project',
name='cycle_view',
),
migrations.RemoveField(
model_name='project',
name='default_assignee',
),
migrations.RemoveField(
model_name='project',
name='default_state',
),
migrations.RemoveField(
model_name='project',
name='description_html',
),
migrations.RemoveField(
model_name='project',
name='description_text',
),
migrations.RemoveField(
model_name='project',
name='estimate',
),
migrations.RemoveField(
model_name='project',
name='inbox_view',
),
migrations.RemoveField(
model_name='project',
name='issue_views_view',
),
migrations.RemoveField(
model_name='project',
name='module_view',
),
migrations.RemoveField(
model_name='project',
name='page_view',
),
migrations.RemoveField(
model_name='project',
name='project_lead',
),
]

View File

@ -13,6 +13,7 @@ from .workspace import (
from .project import ( from .project import (
Project, Project,
ProjectSetting,
ProjectMember, ProjectMember,
ProjectBaseModel, ProjectBaseModel,
ProjectMemberInvite, ProjectMemberInvite,

View File

@ -38,7 +38,7 @@ def get_default_props():
}, },
"display_filters": { "display_filters": {
"group_by": None, "group_by": None,
"order_by": '-created_at', "order_by": "-created_at",
"type": None, "type": None,
"sub_issue": True, "sub_issue": True,
"show_empty_groups": True, "show_empty_groups": True,
@ -56,12 +56,6 @@ class Project(BaseModel):
NETWORK_CHOICES = ((0, "Secret"), (2, "Public")) NETWORK_CHOICES = ((0, "Secret"), (2, "Public"))
name = models.CharField(max_length=255, verbose_name="Project Name") name = models.CharField(max_length=255, verbose_name="Project Name")
description = models.TextField(verbose_name="Project Description", blank=True) description = models.TextField(verbose_name="Project Description", blank=True)
description_text = models.JSONField(
verbose_name="Project Description RT", blank=True, null=True
)
description_html = models.JSONField(
verbose_name="Project Description HTML", blank=True, null=True
)
network = models.PositiveSmallIntegerField(default=2, choices=NETWORK_CHOICES) network = models.PositiveSmallIntegerField(default=2, choices=NETWORK_CHOICES)
workspace = models.ForeignKey( workspace = models.ForeignKey(
"db.WorkSpace", on_delete=models.CASCADE, related_name="workspace_project" "db.WorkSpace", on_delete=models.CASCADE, related_name="workspace_project"
@ -70,40 +64,40 @@ class Project(BaseModel):
max_length=12, max_length=12,
verbose_name="Project Identifier", verbose_name="Project Identifier",
) )
default_assignee = models.ForeignKey( # default_assignee = models.ForeignKey(
settings.AUTH_USER_MODEL, # settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, # on_delete=models.CASCADE,
related_name="default_assignee", # related_name="default_assignee",
null=True, # null=True,
blank=True, # blank=True,
) # )
project_lead = models.ForeignKey( # project_lead = models.ForeignKey(
settings.AUTH_USER_MODEL, # settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, # on_delete=models.CASCADE,
related_name="project_lead", # related_name="project_lead",
null=True, # null=True,
blank=True, # blank=True,
) # )
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) # module_view = models.BooleanField(default=True)
cycle_view = models.BooleanField(default=True) # cycle_view = models.BooleanField(default=True)
issue_views_view = models.BooleanField(default=True) # issue_views_view = models.BooleanField(default=True)
page_view = models.BooleanField(default=True) # page_view = models.BooleanField(default=True)
inbox_view = models.BooleanField(default=False) # 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", on_delete=models.SET_NULL, related_name="projects", null=True # "db.Estimate", on_delete=models.SET_NULL, related_name="projects", null=True
) # )
archive_in = models.IntegerField( # archive_in = models.IntegerField(
default=0, validators=[MinValueValidator(0), MaxValueValidator(12)] # default=0, validators=[MinValueValidator(0), MaxValueValidator(12)]
) # )
close_in = models.IntegerField( # close_in = models.IntegerField(
default=0, validators=[MinValueValidator(0), MaxValueValidator(12)] # default=0, validators=[MinValueValidator(0), MaxValueValidator(12)]
) # )
default_state = models.ForeignKey( # default_state = models.ForeignKey(
"db.State", on_delete=models.SET_NULL, null=True, related_name="default_state" # "db.State", on_delete=models.SET_NULL, null=True, related_name="default_state"
) # )
def __str__(self): def __str__(self):
"""Return name of the project""" """Return name of the project"""
@ -121,6 +115,60 @@ class Project(BaseModel):
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
class ProjectSetting(BaseModel):
workspace = models.ForeignKey(
"db.Workspace", on_delete=models.CASCADE, related_name="project_settings"
)
project = models.OneToOneField(
"db.Project", on_delete=models.CASCADE, related_name="project_settings"
)
default_assignee = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="default_assignee",
null=True,
blank=True,
)
project_lead = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="project_lead",
null=True,
blank=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)
estimate = models.ForeignKey(
"db.Estimate", on_delete=models.SET_NULL, related_name="projects", null=True
)
archive_in = models.IntegerField(
default=0, validators=[MinValueValidator(0), MaxValueValidator(12)]
)
close_in = models.IntegerField(
default=0, validators=[MinValueValidator(0), MaxValueValidator(12)]
)
default_state = models.ForeignKey(
"db.State", on_delete=models.SET_NULL, null=True, related_name="default_state"
)
def save(self, *args, **kwargs):
self.workspace = self.project.workspace
super(ProjectSetting, self).save(*args, **kwargs)
def __str__(self):
"""Return name of the project"""
return f"{self.project.name} <{self.workspace.name}>"
class Meta:
verbose_name = "Project Settings"
verbose_name_plural = "Project Settings"
db_table = "project_settings"
ordering = ("-created_at",)
class ProjectBaseModel(BaseModel): class ProjectBaseModel(BaseModel):
project = models.ForeignKey( project = models.ForeignKey(
Project, on_delete=models.CASCADE, related_name="project_%(class)s" Project, on_delete=models.CASCADE, related_name="project_%(class)s"