mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
dev: create project settings
This commit is contained in:
parent
03f9ca45d8
commit
1604f4585d
@ -13,6 +13,7 @@ from .project import (
|
|||||||
ProjectSerializer,
|
ProjectSerializer,
|
||||||
ProjectSettingSerializer,
|
ProjectSettingSerializer,
|
||||||
ProjectDetailSerializer,
|
ProjectDetailSerializer,
|
||||||
|
ProjectSettingDetailSerializer,
|
||||||
ProjectMemberSerializer,
|
ProjectMemberSerializer,
|
||||||
ProjectMemberInviteSerializer,
|
ProjectMemberInviteSerializer,
|
||||||
ProjectIdentifierSerializer,
|
ProjectIdentifierSerializer,
|
||||||
@ -21,7 +22,7 @@ from .project import (
|
|||||||
ProjectMemberLiteSerializer,
|
ProjectMemberLiteSerializer,
|
||||||
ProjectDeployBoardSerializer,
|
ProjectDeployBoardSerializer,
|
||||||
ProjectMemberAdminSerializer,
|
ProjectMemberAdminSerializer,
|
||||||
ProjectPublicMemberSerializer
|
ProjectPublicMemberSerializer,
|
||||||
)
|
)
|
||||||
from .state import StateSerializer, StateLiteSerializer
|
from .state import StateSerializer, StateLiteSerializer
|
||||||
from .view import GlobalViewSerializer, IssueViewSerializer, IssueViewFavoriteSerializer
|
from .view import GlobalViewSerializer, IssueViewSerializer, IssueViewFavoriteSerializer
|
||||||
|
@ -97,8 +97,6 @@ class ProjectLiteSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class ProjectDetailSerializer(BaseSerializer):
|
class ProjectDetailSerializer(BaseSerializer):
|
||||||
workspace = WorkSpaceSerializer(read_only=True)
|
workspace = WorkSpaceSerializer(read_only=True)
|
||||||
default_assignee = UserLiteSerializer(read_only=True)
|
|
||||||
project_lead = UserLiteSerializer(read_only=True)
|
|
||||||
is_favorite = serializers.BooleanField(read_only=True)
|
is_favorite = serializers.BooleanField(read_only=True)
|
||||||
total_members = serializers.IntegerField(read_only=True)
|
total_members = serializers.IntegerField(read_only=True)
|
||||||
total_cycles = serializers.IntegerField(read_only=True)
|
total_cycles = serializers.IntegerField(read_only=True)
|
||||||
@ -179,12 +177,12 @@ class ProjectDeployBoardSerializer(BaseSerializer):
|
|||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
"workspace",
|
"workspace",
|
||||||
"project", "anchor",
|
"project",
|
||||||
|
"anchor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ProjectPublicMemberSerializer(BaseSerializer):
|
class ProjectPublicMemberSerializer(BaseSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProjectPublicMember
|
model = ProjectPublicMember
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
@ -196,6 +194,18 @@ class ProjectPublicMemberSerializer(BaseSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class ProjectSettingSerializer(BaseSerializer):
|
class ProjectSettingSerializer(BaseSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = ProjectSetting
|
||||||
|
fields = "__all__"
|
||||||
|
read_only_fields = [
|
||||||
|
"workspace",
|
||||||
|
"project",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectSettingDetailSerializer(BaseSerializer):
|
||||||
|
default_assignee = UserLiteSerializer(read_only=True)
|
||||||
|
project_lead = UserLiteSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProjectSetting
|
model = ProjectSetting
|
||||||
|
@ -9,7 +9,7 @@ from sentry_sdk import capture_exception
|
|||||||
# Module imports
|
# Module imports
|
||||||
from .base import BaseViewSet, BaseAPIView
|
from .base import BaseViewSet, BaseAPIView
|
||||||
from plane.api.permissions import ProjectEntityPermission
|
from plane.api.permissions import ProjectEntityPermission
|
||||||
from plane.db.models import Project, Estimate, EstimatePoint
|
from plane.db.models import ProjectSetting, Estimate, EstimatePoint
|
||||||
from plane.api.serializers import (
|
from plane.api.serializers import (
|
||||||
EstimateSerializer,
|
EstimateSerializer,
|
||||||
EstimatePointSerializer,
|
EstimatePointSerializer,
|
||||||
@ -24,10 +24,10 @@ class ProjectEstimatePointEndpoint(BaseAPIView):
|
|||||||
|
|
||||||
def get(self, request, slug, project_id):
|
def get(self, request, slug, project_id):
|
||||||
try:
|
try:
|
||||||
project = Project.objects.get(workspace__slug=slug, pk=project_id)
|
project_setting = ProjectSetting.objects.get(workspace__slug=slug, pk=project_id)
|
||||||
if project.estimate_id is not None:
|
if project_setting.estimate_id is not None:
|
||||||
estimate_points = EstimatePoint.objects.filter(
|
estimate_points = EstimatePoint.objects.filter(
|
||||||
estimate_id=project.estimate_id,
|
estimate_id=project_setting.estimate_id,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
)
|
)
|
||||||
|
@ -63,6 +63,7 @@ from plane.api.permissions import (
|
|||||||
)
|
)
|
||||||
from plane.db.models import (
|
from plane.db.models import (
|
||||||
Project,
|
Project,
|
||||||
|
ProjectSetting,
|
||||||
Issue,
|
Issue,
|
||||||
IssueActivity,
|
IssueActivity,
|
||||||
IssueComment,
|
IssueComment,
|
||||||
@ -293,13 +294,14 @@ class IssueViewSet(BaseViewSet):
|
|||||||
def create(self, request, slug, project_id):
|
def create(self, request, slug, project_id):
|
||||||
try:
|
try:
|
||||||
project = Project.objects.get(pk=project_id)
|
project = Project.objects.get(pk=project_id)
|
||||||
|
project_setting = ProjectSetting.objects.get(workspace__slug=slug, project_id=project_id)
|
||||||
|
|
||||||
serializer = IssueCreateSerializer(
|
serializer = IssueCreateSerializer(
|
||||||
data=request.data,
|
data=request.data,
|
||||||
context={
|
context={
|
||||||
"project_id": project_id,
|
"project_id": project_id,
|
||||||
"workspace_id": project.workspace_id,
|
"workspace_id": project.workspace_id,
|
||||||
"default_assignee_id": project.default_assignee_id,
|
"default_assignee_id": project_setting.default_assignee_id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2565,13 +2567,13 @@ class IssueDraftViewSet(BaseViewSet):
|
|||||||
def create(self, request, slug, project_id):
|
def create(self, request, slug, project_id):
|
||||||
try:
|
try:
|
||||||
project = Project.objects.get(pk=project_id)
|
project = Project.objects.get(pk=project_id)
|
||||||
|
project_setting = ProjectSetting.objects.get(workspace__slug=slug, project_id=project_id)
|
||||||
serializer = IssueCreateSerializer(
|
serializer = IssueCreateSerializer(
|
||||||
data=request.data,
|
data=request.data,
|
||||||
context={
|
context={
|
||||||
"project_id": project_id,
|
"project_id": project_id,
|
||||||
"workspace_id": project.workspace_id,
|
"workspace_id": project.workspace_id,
|
||||||
"default_assignee_id": project.default_assignee_id,
|
"default_assignee_id": project_setting.default_assignee_id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ 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,
|
||||||
|
ProjectSettingDetailSerializer,
|
||||||
ProjectSettingSerializer,
|
ProjectSettingSerializer,
|
||||||
ProjectMemberSerializer,
|
ProjectMemberSerializer,
|
||||||
ProjectDetailSerializer,
|
ProjectDetailSerializer,
|
||||||
@ -99,7 +100,7 @@ class ProjectViewSet(BaseViewSet):
|
|||||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||||
.filter(Q(project_projectmember__member=self.request.user) | Q(network=2))
|
.filter(Q(project_projectmember__member=self.request.user) | Q(network=2))
|
||||||
.select_related(
|
.select_related(
|
||||||
"workspace", "workspace__owner", "default_assignee", "project_lead"
|
"workspace", "workspace__owner",
|
||||||
)
|
)
|
||||||
.annotate(is_favorite=Exists(subquery))
|
.annotate(is_favorite=Exists(subquery))
|
||||||
.annotate(
|
.annotate(
|
||||||
@ -211,20 +212,29 @@ class ProjectViewSet(BaseViewSet):
|
|||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
|
|
||||||
|
project_lead = request.data.get("project_lead", None)
|
||||||
|
|
||||||
|
# Create Project Setting
|
||||||
|
_ = ProjectSetting.objects.create(
|
||||||
|
project_id=serializer.data["id"],
|
||||||
|
project_lead_id=request.data.get("project_lead", None)
|
||||||
|
)
|
||||||
|
|
||||||
# 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"], member=request.user, role=20
|
project_id=serializer.data["id"], member=request.user, role=20
|
||||||
)
|
)
|
||||||
|
|
||||||
if serializer.data["project_lead"] is not None and str(
|
if project_lead is not None and str(
|
||||||
serializer.data["project_lead"]
|
project_lead
|
||||||
) != str(request.user.id):
|
) != str(request.user.id):
|
||||||
ProjectMember.objects.create(
|
ProjectMember.objects.create(
|
||||||
project_id=serializer.data["id"],
|
project_id=serializer.data["id"],
|
||||||
member_id=serializer.data["project_lead"],
|
member_id=project_lead,
|
||||||
role=20,
|
role=20,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Default states
|
# Default states
|
||||||
states = [
|
states = [
|
||||||
{
|
{
|
||||||
@ -308,7 +318,7 @@ class ProjectViewSet(BaseViewSet):
|
|||||||
status=status.HTTP_410_GONE,
|
status=status.HTTP_410_GONE,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capture_exception(e)
|
print(e)
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Something went wrong please try again later"},
|
{"error": "Something went wrong please try again later"},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
@ -974,7 +984,7 @@ class ProjectFavoritesViewSet(BaseViewSet):
|
|||||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||||
.filter(user=self.request.user)
|
.filter(user=self.request.user)
|
||||||
.select_related(
|
.select_related(
|
||||||
"project", "project__project_lead", "project__default_assignee"
|
"project",
|
||||||
)
|
)
|
||||||
.select_related("workspace", "workspace__owner")
|
.select_related("workspace", "workspace__owner")
|
||||||
)
|
)
|
||||||
@ -1254,13 +1264,62 @@ class ProjectSettingViewSet(BaseViewSet):
|
|||||||
permission_classes = [
|
permission_classes = [
|
||||||
ProjectBasePermission,
|
ProjectBasePermission,
|
||||||
]
|
]
|
||||||
serializer_class = ProjectSettingSerializer
|
|
||||||
|
def get_serializer_class(self, *args, **kwargs):
|
||||||
|
if self.action in ["create", "partial_update"]:
|
||||||
|
return ProjectSettingSerializer
|
||||||
|
return ProjectSettingDetailSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
super().get_queryset().filter(
|
return (
|
||||||
workspace__slug=self.kwargs.get("slug"),
|
super()
|
||||||
project_id=self.kwargs.get("project_id"),
|
.get_queryset()
|
||||||
|
.filter(
|
||||||
|
workspace__slug=self.kwargs.get("slug"),
|
||||||
|
project_id=self.kwargs.get("project_id"),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
serializer.save(project_id=self.kwargs.get("project_id"))
|
serializer.save(project_id=self.kwargs.get("project_id"))
|
||||||
|
|
||||||
|
def list(self, request, slug, project_id):
|
||||||
|
try:
|
||||||
|
project_setting = self.get_queryset().first()
|
||||||
|
|
||||||
|
if project_setting is not None:
|
||||||
|
serializer = ProjectSettingDetailSerializer(project_setting)
|
||||||
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
return Response(
|
||||||
|
{"error": "Project setting does not exists"},
|
||||||
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
capture_exception(e)
|
||||||
|
return Response(
|
||||||
|
{"error": "Something went wrong please try again later"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
|
||||||
|
def partial_update(self, request, slug, project_id):
|
||||||
|
try:
|
||||||
|
project_setting = self.get_queryset().first()
|
||||||
|
|
||||||
|
# Check if it is None
|
||||||
|
if project_setting is not None:
|
||||||
|
serializer = ProjectSettingSerializer(
|
||||||
|
project_setting, data=request.data, partial=True
|
||||||
|
)
|
||||||
|
if serializer.is_valid():
|
||||||
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
return Response(
|
||||||
|
{"error": "Project setting does not exists"},
|
||||||
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
capture_exception(e)
|
||||||
|
return Response(
|
||||||
|
{"error": "Something went wrong please try again later"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
@ -12,7 +12,7 @@ from celery import shared_task
|
|||||||
from sentry_sdk import capture_exception
|
from sentry_sdk import capture_exception
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import Issue, Project, State
|
from plane.db.models import Issue, ProjectSetting, State
|
||||||
from plane.bgtasks.issue_activites_task import issue_activity
|
from plane.bgtasks.issue_activites_task import issue_activity
|
||||||
|
|
||||||
|
|
||||||
@ -25,11 +25,11 @@ def archive_and_close_old_issues():
|
|||||||
def archive_old_issues():
|
def archive_old_issues():
|
||||||
try:
|
try:
|
||||||
# Get all the projects whose archive_in is greater than 0
|
# Get all the projects whose archive_in is greater than 0
|
||||||
projects = Project.objects.filter(archive_in__gt=0)
|
project_settings = ProjectSetting.objects.filter(archive_in__gt=0)
|
||||||
|
|
||||||
for project in projects:
|
for project_setting in project_settings:
|
||||||
project_id = project.id
|
project_id = project_setting.project_id
|
||||||
archive_in = project.archive_in
|
archive_in = project_setting.archive_in
|
||||||
|
|
||||||
# Get all the issues whose updated_at in less that the archive_in month
|
# Get all the issues whose updated_at in less that the archive_in month
|
||||||
issues = Issue.issue_objects.filter(
|
issues = Issue.issue_objects.filter(
|
||||||
@ -75,7 +75,7 @@ def archive_old_issues():
|
|||||||
issue_activity.delay(
|
issue_activity.delay(
|
||||||
type="issue.activity.updated",
|
type="issue.activity.updated",
|
||||||
requested_data=json.dumps({"archived_at": str(archive_at)}),
|
requested_data=json.dumps({"archived_at": str(archive_at)}),
|
||||||
actor_id=str(project.created_by_id),
|
actor_id=str(project_setting.created_by_id),
|
||||||
issue_id=issue.id,
|
issue_id=issue.id,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
current_instance=None,
|
current_instance=None,
|
||||||
@ -95,13 +95,13 @@ def archive_old_issues():
|
|||||||
def close_old_issues():
|
def close_old_issues():
|
||||||
try:
|
try:
|
||||||
# Get all the projects whose close_in is greater than 0
|
# Get all the projects whose close_in is greater than 0
|
||||||
projects = Project.objects.filter(close_in__gt=0).select_related(
|
project_settings = ProjectSetting.objects.filter(close_in__gt=0).select_related(
|
||||||
"default_state"
|
"close_state"
|
||||||
)
|
)
|
||||||
|
|
||||||
for project in projects:
|
for project_setting in project_settings:
|
||||||
project_id = project.id
|
project_id = project_setting.project_id
|
||||||
close_in = project.close_in
|
close_in = project_setting.close_in
|
||||||
|
|
||||||
# Get all the issues whose updated_at in less that the close_in month
|
# Get all the issues whose updated_at in less that the close_in month
|
||||||
issues = Issue.issue_objects.filter(
|
issues = Issue.issue_objects.filter(
|
||||||
@ -130,10 +130,10 @@ def close_old_issues():
|
|||||||
|
|
||||||
# Check if Issues
|
# Check if Issues
|
||||||
if issues:
|
if issues:
|
||||||
if project.default_state is None:
|
if project_setting.close_state is None:
|
||||||
close_state = State.objects.filter(group="cancelled").first()
|
close_state = State.objects.filter(group="cancelled").first()
|
||||||
else:
|
else:
|
||||||
close_state = project.default_state
|
close_state = project_setting.close_state
|
||||||
|
|
||||||
issues_to_update = []
|
issues_to_update = []
|
||||||
for issue in issues:
|
for issue in issues:
|
||||||
@ -147,7 +147,7 @@ def close_old_issues():
|
|||||||
issue_activity.delay(
|
issue_activity.delay(
|
||||||
type="issue.activity.updated",
|
type="issue.activity.updated",
|
||||||
requested_data=json.dumps({"closed_to": str(issue.state_id)}),
|
requested_data=json.dumps({"closed_to": str(issue.state_id)}),
|
||||||
actor_id=str(project.created_by_id),
|
actor_id=str(project_setting.created_by_id),
|
||||||
issue_id=issue.id,
|
issue_id=issue.id,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
current_instance=None,
|
current_instance=None,
|
||||||
|
@ -74,6 +74,7 @@ class Migration(migrations.Migration):
|
|||||||
name='archive_in',
|
name='archive_in',
|
||||||
),
|
),
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
|
model_name='project',
|
||||||
name='close_in',
|
name='close_in',
|
||||||
),
|
),
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 4.2.3 on 2023-10-12 07:13
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('db', '0046_auto_20231011_1334'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='projectsetting',
|
||||||
|
name='default_state',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='projectsetting',
|
||||||
|
name='close_state',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='close_state', to='db.state'),
|
||||||
|
),
|
||||||
|
]
|
@ -150,8 +150,8 @@ class ProjectSetting(BaseModel):
|
|||||||
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(
|
close_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="close_state"
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
Loading…
Reference in New Issue
Block a user