fix: page transaction model

This commit is contained in:
NarayanBavisetti 2023-10-11 15:13:05 +05:30
parent 1039337c45
commit 03026449ea
9 changed files with 471 additions and 127 deletions

View File

@ -70,7 +70,7 @@ from .integration import (
from .importer import ImporterSerializer from .importer import ImporterSerializer
from .page import PageSerializer, PageBlockSerializer, PageFavoriteSerializer from .page import PageSerializer, PageTransactionSerializer, SubPageSerializer, PageFavoriteSerializer
from .estimate import ( from .estimate import (
EstimateSerializer, EstimateSerializer,

View File

@ -6,28 +6,7 @@ from .base import BaseSerializer
from .issue import IssueFlatSerializer, LabelLiteSerializer from .issue import IssueFlatSerializer, LabelLiteSerializer
from .workspace import WorkspaceLiteSerializer from .workspace import WorkspaceLiteSerializer
from .project import ProjectLiteSerializer from .project import ProjectLiteSerializer
from plane.db.models import Page, PageBlock, PageFavorite, PageLabel, Label from plane.db.models import Page, PageTransaction, PageFavorite, PageLabel, Label, Issue, Module
class PageBlockSerializer(BaseSerializer):
issue_detail = IssueFlatSerializer(source="issue", read_only=True)
project_detail = ProjectLiteSerializer(source="project", read_only=True)
workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True)
class Meta:
model = PageBlock
fields = "__all__"
read_only_fields = [
"workspace",
"project",
"page",
]
class PageBlockLiteSerializer(BaseSerializer):
class Meta:
model = PageBlock
fields = "__all__"
class PageSerializer(BaseSerializer): class PageSerializer(BaseSerializer):
@ -38,7 +17,6 @@ class PageSerializer(BaseSerializer):
write_only=True, write_only=True,
required=False, required=False,
) )
blocks = PageBlockLiteSerializer(read_only=True, many=True)
project_detail = ProjectLiteSerializer(source="project", read_only=True) project_detail = ProjectLiteSerializer(source="project", read_only=True)
workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True)
@ -98,6 +76,41 @@ class PageSerializer(BaseSerializer):
return super().update(instance, validated_data) return super().update(instance, validated_data)
class SubPageSerializer(BaseSerializer):
entity_details = serializers.SerializerMethodField()
class Meta:
model = PageTransaction
fields = "__all__"
read_only_fields = [
"workspace",
"project",
"page",
]
def get_entity_details(self, obj):
entity_name = obj.entity_name
if entity_name == 'forward_link' or entity_name == 'back_link':
try:
page = Page.objects.get(pk=obj.entity_identifier)
return PageSerializer(page).data
except Page.name:
return None
return None
class PageTransactionSerializer(BaseSerializer):
class Meta:
model = PageTransaction
fields = "__all__"
read_only_fields = [
"workspace",
"project",
"page",
]
class PageFavoriteSerializer(BaseSerializer): class PageFavoriteSerializer(BaseSerializer):
page_detail = PageSerializer(source="page", read_only=True) page_detail = PageSerializer(source="page", read_only=True)

View File

@ -125,9 +125,10 @@ from plane.api.views import (
## End Modules ## End Modules
# Pages # Pages
PageViewSet, PageViewSet,
PageBlockViewSet, PageTransactionEndpoint,
SubPagesEndpoint,
PageFavoriteViewSet, PageFavoriteViewSet,
CreateIssueFromPageBlockEndpoint, CreateIssueFromBlockEndpoint,
## End Pages ## End Pages
# Api Tokens # Api Tokens
ApiTokenEndpoint, ApiTokenEndpoint,
@ -1231,25 +1232,81 @@ urlpatterns = [
name="project-pages", name="project-pages",
), ),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/page-blocks/", "workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/archive/",
PageBlockViewSet.as_view( PageViewSet.as_view(
{
"post": "archive",
}
),
name="project-page-archive",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/unarchive/",
PageViewSet.as_view(
{
"post": "unarchive",
}
),
name="project-page-unarchive"
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/archived-pages/",
PageViewSet.as_view(
{
"get": "archive_list",
}
),
name="project-pages",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/lock/",
PageViewSet.as_view(
{
"post": "lock",
}
),
name="project-pages",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/unlock/",
PageViewSet.as_view(
{
"post": "unlock",
}
)
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/transactions/",
PageTransactionEndpoint.as_view(), name="page-transactions"
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/transactions/<uuid:transaction>/",
PageTransactionEndpoint.as_view(), name="page-transactions"
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/sub-pages/",
SubPagesEndpoint.as_view(), name="sub-page"
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/",
BulkEstimatePointEndpoint.as_view(
{ {
"get": "list", "get": "list",
"post": "create", "post": "create",
} }
), ),
name="project-page-blocks", name="bulk-create-estimate-points",
), ),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/page-blocks/<uuid:pk>/", "workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/",
PageBlockViewSet.as_view( BulkEstimatePointEndpoint.as_view(
{ {
"get": "retrieve", "get": "retrieve",
"patch": "partial_update", "patch": "partial_update",
"delete": "destroy", "delete": "destroy",
} }
), ),
name="project-page-blocks", name="bulk-create-estimate-points",
), ),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/user-favorite-pages/", "workspaces/<str:slug>/projects/<uuid:project_id>/user-favorite-pages/",
@ -1272,7 +1329,7 @@ urlpatterns = [
), ),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/page-blocks/<uuid:page_block_id>/issues/", "workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:page_id>/page-blocks/<uuid:page_block_id>/issues/",
CreateIssueFromPageBlockEndpoint.as_view(), CreateIssueFromBlockEndpoint.as_view(),
name="page-block-issues", name="page-block-issues",
), ),
## End Pages ## End Pages

View File

@ -140,9 +140,10 @@ from .importer import (
from .page import ( from .page import (
PageViewSet, PageViewSet,
PageBlockViewSet,
PageFavoriteViewSet, PageFavoriteViewSet,
CreateIssueFromPageBlockEndpoint, PageTransactionEndpoint,
SubPagesEndpoint,
CreateIssueFromBlockEndpoint,
) )
from .search import GlobalSearchEndpoint, IssueSearchEndpoint from .search import GlobalSearchEndpoint, IssueSearchEndpoint

View File

@ -2,9 +2,19 @@
from datetime import timedelta, datetime, date from datetime import timedelta, datetime, date
# Django imports # Django imports
from django.db import connection
from django.db import IntegrityError from django.db import IntegrityError
from django.db.models import Exists, OuterRef, Q, Prefetch from django.db.models import Exists, OuterRef, Q, Prefetch
from django.utils import timezone from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.gzip import gzip_page
from django.db.models import (
OuterRef,
Func,
F,
Q,
Exists,
)
# Third party imports # Third party imports
from rest_framework import status from rest_framework import status
@ -16,20 +26,37 @@ from .base import BaseViewSet, BaseAPIView
from plane.api.permissions import ProjectEntityPermission from plane.api.permissions import ProjectEntityPermission
from plane.db.models import ( from plane.db.models import (
Page, Page,
PageBlock,
PageFavorite, PageFavorite,
Issue, Issue,
IssueAssignee, IssueAssignee,
IssueActivity, IssueActivity,
PageTransaction,
) )
from plane.api.serializers import ( from plane.api.serializers import (
PageSerializer, PageSerializer,
PageBlockSerializer,
PageFavoriteSerializer, PageFavoriteSerializer,
PageTransactionSerializer,
IssueLiteSerializer, IssueLiteSerializer,
SubPageSerializer,
) )
def unarchive_archive_page_and_descendants(page_id, archived_at):
# Your SQL query
sql = """
WITH RECURSIVE descendants AS (
SELECT id FROM pages WHERE id = %s
UNION ALL
SELECT pages.id FROM pages, descendants WHERE pages.parent_id = descendants.id
)
UPDATE pages SET archived_at = %s WHERE id IN (SELECT id FROM descendants);
"""
# Execute the SQL query
with connection.cursor() as cursor:
cursor.execute(sql, [page_id, archived_at])
class PageViewSet(BaseViewSet): class PageViewSet(BaseViewSet):
serializer_class = PageSerializer serializer_class = PageSerializer
model = Page model = Page
@ -53,6 +80,7 @@ class PageViewSet(BaseViewSet):
.filter(workspace__slug=self.kwargs.get("slug")) .filter(workspace__slug=self.kwargs.get("slug"))
.filter(project_id=self.kwargs.get("project_id")) .filter(project_id=self.kwargs.get("project_id"))
.filter(project__project_projectmember__member=self.request.user) .filter(project__project_projectmember__member=self.request.user)
.filter(parent__isnull=True)
.filter(Q(owned_by=self.request.user) | Q(access=0)) .filter(Q(owned_by=self.request.user) | Q(access=0))
.select_related("project") .select_related("project")
.select_related("workspace") .select_related("workspace")
@ -61,14 +89,6 @@ class PageViewSet(BaseViewSet):
.order_by(self.request.GET.get("order_by", "-created_at")) .order_by(self.request.GET.get("order_by", "-created_at"))
.prefetch_related("labels") .prefetch_related("labels")
.order_by("name", "-is_favorite") .order_by("name", "-is_favorite")
.prefetch_related(
Prefetch(
"blocks",
queryset=PageBlock.objects.select_related(
"page", "issue", "workspace", "project"
),
)
)
.distinct() .distinct()
) )
@ -99,6 +119,19 @@ class PageViewSet(BaseViewSet):
def partial_update(self, request, slug, project_id, pk): def partial_update(self, request, slug, project_id, pk):
try: try:
page = Page.objects.get(pk=pk, workspace__slug=slug, project_id=project_id) page = Page.objects.get(pk=pk, workspace__slug=slug, project_id=project_id)
if page.is_locked:
return Response(
{"error": "Page is locked"},
status=status.HTTP_400_BAD_REQUEST,
)
parent = request.data.get("parent", None)
if parent:
_ = Page.objects.get(
pk=parent, workspace__slug=slug, project_id=project_id
)
# Only update access if the page owner is the requesting user # Only update access if the page owner is the requesting user
if ( if (
page.access != request.data.get("access", page.access) page.access != request.data.get("access", page.access)
@ -110,6 +143,19 @@ class PageViewSet(BaseViewSet):
}, },
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
# # only update lock if the page owner is the requesting user
# if (
# page.is_locked != request.data.get("is_locked", page.is_locked)
# and page.owned_by_id != request.user.id
# ):
# return Response(
# {
# "error": "Lock cannot be updated since this page is owned by someone else"
# },
# status=status.HTTP_400_BAD_REQUEST,
# )
serializer = PageSerializer(page, data=request.data, partial=True) serializer = PageSerializer(page, data=request.data, partial=True)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
@ -125,18 +171,62 @@ class PageViewSet(BaseViewSet):
{"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,
) )
def lock(self, request, slug, project_id, pk):
try:
page = Page.objects.filter(
pk=pk, workspace__slug=slug, project_id=project_id
)
# only the owner can lock the page
if request.user.id != page.owned_by_id:
return Response(
{"error": "Only the page owner can lock the page"},
)
page.is_locked = True
page.save()
except Exception as e:
capture_exception(e)
return Response({"error": "Something went wrong please try again later"})
def unlock(self, request, slug, project_id, pk):
try:
page = Page.objects.get(pk=pk, workspace__slug=slug, project_id=project_id)
# only the owner can unlock the page
if request.user.id != page.owned_by_id:
return Response(
{"error": "Only the page owner can unlock the page"},
status=status.HTTP_400_BAD_REQUEST,
)
page.is_locked = False
page.save()
return Response(status=status.HTTP_204_NO_CONTENT)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def list(self, request, slug, project_id): def list(self, request, slug, project_id):
try: try:
queryset = self.get_queryset() queryset = self.get_queryset().filter(archived_at__isnull=True)
page_view = request.GET.get("page_view", False) page_view = request.GET.get("page_view", False)
if not page_view: if not page_view:
return Response({"error": "Page View parameter is required"}, status=status.HTTP_400_BAD_REQUEST) return Response(
{"error": "Page View parameter is required"},
status=status.HTTP_400_BAD_REQUEST,
)
# All Pages # All Pages
if page_view == "all": if page_view == "all":
return Response(PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK) return Response(
PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK
)
# Recent pages # Recent pages
if page_view == "recent": if page_view == "recent":
@ -144,67 +234,131 @@ class PageViewSet(BaseViewSet):
day_before = current_time - timedelta(days=1) day_before = current_time - timedelta(days=1)
todays_pages = queryset.filter(updated_at__date=date.today()) todays_pages = queryset.filter(updated_at__date=date.today())
yesterdays_pages = queryset.filter(updated_at__date=day_before) yesterdays_pages = queryset.filter(updated_at__date=day_before)
earlier_this_week = queryset.filter( updated_at__date__range=( earlier_this_week = queryset.filter(
updated_at__date__range=(
(timezone.now() - timedelta(days=7)), (timezone.now() - timedelta(days=7)),
(timezone.now() - timedelta(days=2)), (timezone.now() - timedelta(days=2)),
)) )
)
return Response( return Response(
{ {
"today": PageSerializer(todays_pages, many=True).data, "today": PageSerializer(todays_pages, many=True).data,
"yesterday": PageSerializer(yesterdays_pages, many=True).data, "yesterday": PageSerializer(yesterdays_pages, many=True).data,
"earlier_this_week": PageSerializer(earlier_this_week, many=True).data, "earlier_this_week": PageSerializer(
}, earlier_this_week, many=True
status=status.HTTP_200_OK, ).data,
) },
status=status.HTTP_200_OK,
)
# Favorite Pages # Favorite Pages
if page_view == "favorite": if page_view == "favorite":
queryset = queryset.filter(is_favorite=True) queryset = queryset.filter(is_favorite=True)
return Response(PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK) return Response(
PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK
)
# My pages # My pages
if page_view == "created_by_me": if page_view == "created_by_me":
queryset = queryset.filter(owned_by=request.user) queryset = queryset.filter(owned_by=request.user)
return Response(PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK) return Response(
PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK
)
# Created by other Pages # Created by other Pages
if page_view == "created_by_other": if page_view == "created_by_other":
queryset = queryset.filter(~Q(owned_by=request.user), access=0) queryset = queryset.filter(~Q(owned_by=request.user), access=0)
return Response(PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK) return Response(
PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK
)
return Response({"error": "No matching view found"}, status=status.HTTP_400_BAD_REQUEST) return Response(
{"error": "No matching view found"}, status=status.HTTP_400_BAD_REQUEST
)
except Exception as e: except Exception as e:
capture_exception(e) capture_exception(e)
return Response({"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST) return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
class PageBlockViewSet(BaseViewSet): def archive(self, request, slug, project_id, page_id):
serializer_class = PageBlockSerializer try:
model = PageBlock _ = Page.objects.get(
permission_classes = [ project_id=project_id,
ProjectEntityPermission, owned_by_id=request.user.id,
] workspace__slug=slug,
pk=page_id,
)
def get_queryset(self): unarchive_archive_page_and_descendants(page_id, datetime.now())
return self.filter_queryset(
super()
.get_queryset()
.filter(workspace__slug=self.kwargs.get("slug"))
.filter(project_id=self.kwargs.get("project_id"))
.filter(page_id=self.kwargs.get("page_id"))
.filter(project__project_projectmember__member=self.request.user)
.select_related("project")
.select_related("workspace")
.select_related("page")
.select_related("issue")
.order_by("sort_order")
.distinct()
)
def perform_create(self, serializer): return Response(status=status.HTTP_204_NO_CONTENT)
serializer.save( except Page.DoesNotExist:
project_id=self.kwargs.get("project_id"), return Response(
page_id=self.kwargs.get("page_id"), {"error": "Page does not exist"},
) status=status.HTTP_400_BAD_REQUEST,
)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def unarchive(self, request, slug, project_id, page_id):
try:
page = Page.objects.get(
project_id=project_id,
owned_by_id=request.user.id,
workspace__slug=slug,
pk=page_id,
)
page.parent = None
page.save()
unarchive_archive_page_and_descendants(page_id, None)
return Response(status=status.HTTP_204_NO_CONTENT)
except Page.DoesNotExist:
return Response(
{"error": "Page does not exist"},
status=status.HTTP_400_BAD_REQUEST,
)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def archive_list(self, request, slug, project_id):
try:
pages = (
Page.objects.filter(
project_id=project_id,
workspace__slug=slug,
)
.filter(archived_at__isnull=False)
.filter(parent_id__isnull=True)
)
if not pages:
return Response(
{"error": "No pages found"}, status=status.HTTP_400_BAD_REQUEST
)
return Response(
PageSerializer(pages, many=True).data, status=status.HTTP_200_OK
)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
class PageFavoriteViewSet(BaseViewSet): class PageFavoriteViewSet(BaseViewSet):
@ -219,6 +373,7 @@ class PageFavoriteViewSet(BaseViewSet):
return self.filter_queryset( return self.filter_queryset(
super() super()
.get_queryset() .get_queryset()
.filter(archived_at__isnull=True)
.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("page", "page__owned_by") .select_related("page", "page__owned_by")
@ -273,25 +428,93 @@ class PageFavoriteViewSet(BaseViewSet):
) )
class CreateIssueFromPageBlockEndpoint(BaseAPIView): class PageTransactionEndpoint(BaseAPIView):
permission_classes = [ permission_classes = [
ProjectEntityPermission, ProjectEntityPermission,
] ]
def post(self, request, slug, project_id, page_id, page_block_id): serializer_class = PageTransactionSerializer
model = PageTransaction
def post(self, request, slug, project_id, page_id):
try: try:
page_block = PageBlock.objects.get( serializer = PageTransactionSerializer(data=request.data)
pk=page_block_id, if serializer.is_valid():
serializer.save(project_id=project_id, page_id=page_id)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later", "er": str(e)},
status=status.HTTP_400_BAD_REQUEST,
)
def patch(self, request, slug, project_id, page_id, transaction):
try:
page_transaction = PageTransaction.objects.get(
workspace__slug=slug, workspace__slug=slug,
project_id=project_id, project_id=project_id,
page_id=page_id, page_id=page_id,
transaction=transaction,
)
serializer = PageTransactionSerializer(
page_transaction, 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)
except PageTransaction.DoesNotExist:
return Response(
{"error": "Transaction 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 delete(self, request, slug, project_id, page_id, transaction):
try:
transaction = PageTransaction.objects.get(
workspace__slug=slug,
project_id=project_id,
page_id=page_id,
transaction=transaction,
)
# Delete the transaction object
transaction.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
except transaction.DoesNotExist:
return Response(
{"error": "Transaction doesn't exist"}, 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,
)
class CreateIssueFromBlockEndpoint(BaseAPIView):
permission_classes = [
ProjectEntityPermission,
]
def post(self, request, slug, project_id, page_id):
try:
page = Page.objects.get(
workspace__slug=slug,
project_id=project_id,
pk=page_id,
) )
issue = Issue.objects.create( issue = Issue.objects.create(
name=page_block.name, name=request.data.get("name"),
project_id=project_id, project_id=project_id,
description=page_block.description,
description_html=page_block.description_html,
description_stripped=page_block.description_stripped,
) )
_ = IssueAssignee.objects.create( _ = IssueAssignee.objects.create(
issue=issue, assignee=request.user, project_id=project_id issue=issue, assignee=request.user, project_id=project_id
@ -301,15 +524,12 @@ class CreateIssueFromPageBlockEndpoint(BaseAPIView):
issue=issue, issue=issue,
actor=request.user, actor=request.user,
project_id=project_id, project_id=project_id,
comment=f"created the issue from {page_block.name} block", comment=f"created the issue from {page.name} block",
verb="created", verb="created",
) )
page_block.issue = issue
page_block.save()
return Response(IssueLiteSerializer(issue).data, status=status.HTTP_200_OK) return Response(IssueLiteSerializer(issue).data, status=status.HTTP_200_OK)
except PageBlock.DoesNotExist: except Page.DoesNotExist:
return Response( return Response(
{"error": "Page Block does not exist"}, status=status.HTTP_404_NOT_FOUND {"error": "Page Block does not exist"}, status=status.HTTP_404_NOT_FOUND
) )
@ -319,3 +539,33 @@ class CreateIssueFromPageBlockEndpoint(BaseAPIView):
{"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,
) )
class SubPagesEndpoint(BaseAPIView):
permission_classes = [
ProjectEntityPermission,
]
@method_decorator(gzip_page)
def get(self, request, slug, project_id, page_id):
try:
pages = (
PageTransaction.objects.filter(
page_id=page_id,
project_id=project_id,
workspace__slug=slug,
entity_name__in=["forward_link", "back_link"],
)
.filter(archived_at__isnull=True)
.select_related("project")
.select_related("workspace")
)
return Response(
SubPageSerializer(pages, many=True).data, status=status.HTTP_200_OK
)
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, Project, State, Page
from plane.bgtasks.issue_activites_task import issue_activity from plane.bgtasks.issue_activites_task import issue_activity
@ -20,6 +20,7 @@ from plane.bgtasks.issue_activites_task import issue_activity
def archive_and_close_old_issues(): def archive_and_close_old_issues():
archive_old_issues() archive_old_issues()
close_old_issues() close_old_issues()
delete_archived_pages()
def archive_old_issues(): def archive_old_issues():
@ -67,7 +68,7 @@ def archive_old_issues():
issues_to_update.append(issue) issues_to_update.append(issue)
# Bulk Update the issues and log the activity # Bulk Update the issues and log the activity
if issues_to_update: if issues_to_update:
Issue.objects.bulk_update( Issue.objects.bulk_update(
issues_to_update, ["archived_at"], batch_size=100 issues_to_update, ["archived_at"], batch_size=100
) )
@ -80,7 +81,7 @@ def archive_old_issues():
project_id=project_id, project_id=project_id,
current_instance=None, current_instance=None,
subscriber=False, subscriber=False,
epoch=int(timezone.now().timestamp()) epoch=int(timezone.now().timestamp()),
) )
for issue in issues_to_update for issue in issues_to_update
] ]
@ -142,17 +143,21 @@ def close_old_issues():
# Bulk Update the issues and log the activity # Bulk Update the issues and log the activity
if issues_to_update: if issues_to_update:
Issue.objects.bulk_update(issues_to_update, ["state"], batch_size=100) Issue.objects.bulk_update(
issues_to_update, ["state"], batch_size=100
)
[ [
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.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,
subscriber=False, subscriber=False,
epoch=int(timezone.now().timestamp()) epoch=int(timezone.now().timestamp()),
) )
for issue in issues_to_update for issue in issues_to_update
] ]
@ -162,3 +167,20 @@ def close_old_issues():
print(e) print(e)
capture_exception(e) capture_exception(e)
return return
def delete_archived_pages():
try:
pages_to_delete = Page.objects.filter(
archived_at__isnull=False,
archived_at__lte=(timezone.now() - timedelta(days=30)),
)
pages_to_delete._raw_delete(pages_to_delete.db)
return
except Exception as e:
if settings.DEBUG:
print(e)
capture_exception(e)
return

View File

@ -68,7 +68,7 @@ from .integration import (
from .importer import Importer from .importer import Importer
from .page import Page, PageBlock, PageFavorite, PageLabel from .page import Page, PageTransaction, PageFavorite, PageLabel
from .estimate import Estimate, EstimatePoint from .estimate import Estimate, EstimatePoint

View File

@ -114,22 +114,22 @@ class Issue(ProjectBaseModel):
self.state = default_state self.state = default_state
except ImportError: except ImportError:
pass pass
else: # else:
try: # try:
from plane.db.models import State, PageBlock # from plane.db.models import State, PageBlock
# Check if the current issue state and completed state id are same # # Check if the current issue state and completed state id are same
if self.state.group == "completed": # if self.state.group == "completed":
self.completed_at = timezone.now() # self.completed_at = timezone.now()
# check if there are any page blocks # # check if there are any page blocks
PageBlock.objects.filter(issue_id=self.id).filter().update( # PageBlock.objects.filter(issue_id=self.id).filter().update(
completed_at=timezone.now() # completed_at=timezone.now()
) # )
else: # else:
PageBlock.objects.filter(issue_id=self.id).filter().update( # PageBlock.objects.filter(issue_id=self.id).filter().update(
completed_at=None # completed_at=None
) # )
self.completed_at = None # self.completed_at = None
except ImportError: except ImportError:
pass pass

View File

@ -57,7 +57,7 @@ class PageTransaction(ProjectBaseModel):
("forward_link", "Forward Link"), ("forward_link", "Forward Link"),
("mention", "Mention"), ("mention", "Mention"),
) )
transaction = models.CharField(max_length=255) transaction = models.UUIDField(null=True)
page = models.ForeignKey( page = models.ForeignKey(
Page, related_name="page_relation", on_delete=models.CASCADE Page, related_name="page_relation", on_delete=models.CASCADE
) )
@ -70,6 +70,7 @@ class PageTransaction(ProjectBaseModel):
) )
class Meta: class Meta:
unique_together = ["page", "transaction"]
verbose_name = "Page Transaction" verbose_name = "Page Transaction"
verbose_name_plural = "Page Transactions" verbose_name_plural = "Page Transactions"
db_table = "page_transactions" db_table = "page_transactions"