dev: create project settings

This commit is contained in:
pablohashescobar 2023-10-12 13:51:32 +05:30
parent 03f9ca45d8
commit 1604f4585d
9 changed files with 135 additions and 38 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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