mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
dev: create model for project setting
This commit is contained in:
parent
58ea4d6ec9
commit
03f9ca45d8
@ -11,6 +11,7 @@ from .workspace import (
|
|||||||
)
|
)
|
||||||
from .project import (
|
from .project import (
|
||||||
ProjectSerializer,
|
ProjectSerializer,
|
||||||
|
ProjectSettingSerializer,
|
||||||
ProjectDetailSerializer,
|
ProjectDetailSerializer,
|
||||||
ProjectMemberSerializer,
|
ProjectMemberSerializer,
|
||||||
ProjectMemberInviteSerializer,
|
ProjectMemberInviteSerializer,
|
||||||
|
@ -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",
|
||||||
|
]
|
||||||
|
@ -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(),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from .project import (
|
from .project import (
|
||||||
ProjectViewSet,
|
ProjectViewSet,
|
||||||
|
ProjectSettingViewSet,
|
||||||
ProjectMemberViewSet,
|
ProjectMemberViewSet,
|
||||||
UserProjectInvitationsViewset,
|
UserProjectInvitationsViewset,
|
||||||
InviteProjectEndpoint,
|
InviteProjectEndpoint,
|
||||||
|
@ -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"))
|
||||||
|
123
apiserver/plane/db/migrations/0046_auto_20231011_1334.py
Normal file
123
apiserver/plane/db/migrations/0046_auto_20231011_1334.py
Normal 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',
|
||||||
|
),
|
||||||
|
]
|
@ -13,6 +13,7 @@ from .workspace import (
|
|||||||
|
|
||||||
from .project import (
|
from .project import (
|
||||||
Project,
|
Project,
|
||||||
|
ProjectSetting,
|
||||||
ProjectMember,
|
ProjectMember,
|
||||||
ProjectBaseModel,
|
ProjectBaseModel,
|
||||||
ProjectMemberInvite,
|
ProjectMemberInvite,
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user