From 03026449eafb7c86fe32fcf5a654a24a169793da Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Wed, 11 Oct 2023 15:13:05 +0530 Subject: [PATCH] fix: page transaction model --- apiserver/plane/api/serializers/__init__.py | 2 +- apiserver/plane/api/serializers/page.py | 59 +-- apiserver/plane/api/urls.py | 75 +++- apiserver/plane/api/views/__init__.py | 5 +- apiserver/plane/api/views/page.py | 388 ++++++++++++++---- .../plane/bgtasks/issue_automation_task.py | 34 +- apiserver/plane/db/models/__init__.py | 2 +- apiserver/plane/db/models/issue.py | 30 +- apiserver/plane/db/models/page.py | 3 +- 9 files changed, 471 insertions(+), 127 deletions(-) diff --git a/apiserver/plane/api/serializers/__init__.py b/apiserver/plane/api/serializers/__init__.py index dbf7ca049..72806fd28 100644 --- a/apiserver/plane/api/serializers/__init__.py +++ b/apiserver/plane/api/serializers/__init__.py @@ -70,7 +70,7 @@ from .integration import ( from .importer import ImporterSerializer -from .page import PageSerializer, PageBlockSerializer, PageFavoriteSerializer +from .page import PageSerializer, PageTransactionSerializer, SubPageSerializer, PageFavoriteSerializer from .estimate import ( EstimateSerializer, diff --git a/apiserver/plane/api/serializers/page.py b/apiserver/plane/api/serializers/page.py index 94f7836de..5fd5d1e2d 100644 --- a/apiserver/plane/api/serializers/page.py +++ b/apiserver/plane/api/serializers/page.py @@ -6,28 +6,7 @@ from .base import BaseSerializer from .issue import IssueFlatSerializer, LabelLiteSerializer from .workspace import WorkspaceLiteSerializer from .project import ProjectLiteSerializer -from plane.db.models import Page, PageBlock, PageFavorite, PageLabel, Label - - -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__" +from plane.db.models import Page, PageTransaction, PageFavorite, PageLabel, Label, Issue, Module class PageSerializer(BaseSerializer): @@ -38,7 +17,6 @@ class PageSerializer(BaseSerializer): write_only=True, required=False, ) - blocks = PageBlockLiteSerializer(read_only=True, many=True) project_detail = ProjectLiteSerializer(source="project", read_only=True) workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) @@ -98,6 +76,41 @@ class PageSerializer(BaseSerializer): 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): page_detail = PageSerializer(source="page", read_only=True) diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index 2213c0d9d..a235b456f 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -125,9 +125,10 @@ from plane.api.views import ( ## End Modules # Pages PageViewSet, - PageBlockViewSet, + PageTransactionEndpoint, + SubPagesEndpoint, PageFavoriteViewSet, - CreateIssueFromPageBlockEndpoint, + CreateIssueFromBlockEndpoint, ## End Pages # Api Tokens ApiTokenEndpoint, @@ -1231,25 +1232,81 @@ urlpatterns = [ name="project-pages", ), path( - "workspaces//projects//pages//page-blocks/", - PageBlockViewSet.as_view( + "workspaces//projects//pages//archive/", + PageViewSet.as_view( + { + "post": "archive", + } + ), + name="project-page-archive", + ), + path( + "workspaces//projects//pages//unarchive/", + PageViewSet.as_view( + { + "post": "unarchive", + } + ), + name="project-page-unarchive" + ), + path( + "workspaces//projects//archived-pages/", + PageViewSet.as_view( + { + "get": "archive_list", + } + ), + name="project-pages", + ), + path( + "workspaces//projects//pages//lock/", + PageViewSet.as_view( + { + "post": "lock", + } + ), + name="project-pages", + ), + path( + "workspaces//projects//pages//unlock/", + PageViewSet.as_view( + { + "post": "unlock", + } + ) + ), + path( + "workspaces//projects//pages//transactions/", + PageTransactionEndpoint.as_view(), name="page-transactions" + ), + path( + "workspaces//projects//pages//transactions//", + PageTransactionEndpoint.as_view(), name="page-transactions" + ), + path( + "workspaces//projects//pages//sub-pages/", + SubPagesEndpoint.as_view(), name="sub-page" + ), + path( + "workspaces//projects//estimates/", + BulkEstimatePointEndpoint.as_view( { "get": "list", "post": "create", } ), - name="project-page-blocks", + name="bulk-create-estimate-points", ), path( - "workspaces//projects//pages//page-blocks//", - PageBlockViewSet.as_view( + "workspaces//projects//estimates//", + BulkEstimatePointEndpoint.as_view( { "get": "retrieve", "patch": "partial_update", "delete": "destroy", } ), - name="project-page-blocks", + name="bulk-create-estimate-points", ), path( "workspaces//projects//user-favorite-pages/", @@ -1272,7 +1329,7 @@ urlpatterns = [ ), path( "workspaces//projects//pages//page-blocks//issues/", - CreateIssueFromPageBlockEndpoint.as_view(), + CreateIssueFromBlockEndpoint.as_view(), name="page-block-issues", ), ## End Pages diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index f7ad735c1..7be8dd1cb 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -140,9 +140,10 @@ from .importer import ( from .page import ( PageViewSet, - PageBlockViewSet, PageFavoriteViewSet, - CreateIssueFromPageBlockEndpoint, + PageTransactionEndpoint, + SubPagesEndpoint, + CreateIssueFromBlockEndpoint, ) from .search import GlobalSearchEndpoint, IssueSearchEndpoint diff --git a/apiserver/plane/api/views/page.py b/apiserver/plane/api/views/page.py index d9fad9eaa..9a20e312f 100644 --- a/apiserver/plane/api/views/page.py +++ b/apiserver/plane/api/views/page.py @@ -2,9 +2,19 @@ from datetime import timedelta, datetime, date # Django imports +from django.db import connection from django.db import IntegrityError from django.db.models import Exists, OuterRef, Q, Prefetch 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 from rest_framework import status @@ -16,20 +26,37 @@ from .base import BaseViewSet, BaseAPIView from plane.api.permissions import ProjectEntityPermission from plane.db.models import ( Page, - PageBlock, PageFavorite, Issue, IssueAssignee, IssueActivity, + PageTransaction, ) from plane.api.serializers import ( PageSerializer, - PageBlockSerializer, PageFavoriteSerializer, + PageTransactionSerializer, 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): serializer_class = PageSerializer model = Page @@ -53,6 +80,7 @@ class PageViewSet(BaseViewSet): .filter(workspace__slug=self.kwargs.get("slug")) .filter(project_id=self.kwargs.get("project_id")) .filter(project__project_projectmember__member=self.request.user) + .filter(parent__isnull=True) .filter(Q(owned_by=self.request.user) | Q(access=0)) .select_related("project") .select_related("workspace") @@ -61,14 +89,6 @@ class PageViewSet(BaseViewSet): .order_by(self.request.GET.get("order_by", "-created_at")) .prefetch_related("labels") .order_by("name", "-is_favorite") - .prefetch_related( - Prefetch( - "blocks", - queryset=PageBlock.objects.select_related( - "page", "issue", "workspace", "project" - ), - ) - ) .distinct() ) @@ -99,6 +119,19 @@ class PageViewSet(BaseViewSet): def partial_update(self, request, slug, project_id, pk): try: 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 if ( page.access != request.data.get("access", page.access) @@ -110,6 +143,19 @@ class PageViewSet(BaseViewSet): }, 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) if serializer.is_valid(): serializer.save() @@ -125,18 +171,62 @@ class PageViewSet(BaseViewSet): {"error": "Something went wrong please try again later"}, 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): try: - queryset = self.get_queryset() + queryset = self.get_queryset().filter(archived_at__isnull=True) page_view = request.GET.get("page_view", False) 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 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 if page_view == "recent": @@ -144,67 +234,131 @@ class PageViewSet(BaseViewSet): day_before = current_time - timedelta(days=1) todays_pages = queryset.filter(updated_at__date=date.today()) 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=2)), - )) + ) + ) return Response( - { - "today": PageSerializer(todays_pages, many=True).data, - "yesterday": PageSerializer(yesterdays_pages, many=True).data, - "earlier_this_week": PageSerializer(earlier_this_week, many=True).data, - }, - status=status.HTTP_200_OK, - ) + { + "today": PageSerializer(todays_pages, many=True).data, + "yesterday": PageSerializer(yesterdays_pages, many=True).data, + "earlier_this_week": PageSerializer( + earlier_this_week, many=True + ).data, + }, + status=status.HTTP_200_OK, + ) # Favorite Pages if page_view == "favorite": 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 if page_view == "created_by_me": 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 if page_view == "created_by_other": - queryset = queryset.filter(~Q(owned_by=request.user), access=0) - return Response(PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK) + queryset = queryset.filter(~Q(owned_by=request.user), access=0) + 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: 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): - serializer_class = PageBlockSerializer - model = PageBlock - permission_classes = [ - ProjectEntityPermission, - ] + def archive(self, request, slug, project_id, page_id): + try: + _ = Page.objects.get( + project_id=project_id, + owned_by_id=request.user.id, + workspace__slug=slug, + pk=page_id, + ) - def get_queryset(self): - 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() - ) + unarchive_archive_page_and_descendants(page_id, datetime.now()) - def perform_create(self, serializer): - serializer.save( - project_id=self.kwargs.get("project_id"), - page_id=self.kwargs.get("page_id"), - ) + 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 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): @@ -219,6 +373,7 @@ class PageFavoriteViewSet(BaseViewSet): return self.filter_queryset( super() .get_queryset() + .filter(archived_at__isnull=True) .filter(workspace__slug=self.kwargs.get("slug")) .filter(user=self.request.user) .select_related("page", "page__owned_by") @@ -273,25 +428,93 @@ class PageFavoriteViewSet(BaseViewSet): ) -class CreateIssueFromPageBlockEndpoint(BaseAPIView): +class PageTransactionEndpoint(BaseAPIView): permission_classes = [ 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: - page_block = PageBlock.objects.get( - pk=page_block_id, + serializer = PageTransactionSerializer(data=request.data) + 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, project_id=project_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( - name=page_block.name, + name=request.data.get("name"), project_id=project_id, - description=page_block.description, - description_html=page_block.description_html, - description_stripped=page_block.description_stripped, ) _ = IssueAssignee.objects.create( issue=issue, assignee=request.user, project_id=project_id @@ -301,15 +524,12 @@ class CreateIssueFromPageBlockEndpoint(BaseAPIView): issue=issue, actor=request.user, 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", ) - page_block.issue = issue - page_block.save() - return Response(IssueLiteSerializer(issue).data, status=status.HTTP_200_OK) - except PageBlock.DoesNotExist: + except Page.DoesNotExist: return Response( {"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"}, 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, + ) diff --git a/apiserver/plane/bgtasks/issue_automation_task.py b/apiserver/plane/bgtasks/issue_automation_task.py index 68c64403a..656329cec 100644 --- a/apiserver/plane/bgtasks/issue_automation_task.py +++ b/apiserver/plane/bgtasks/issue_automation_task.py @@ -12,7 +12,7 @@ from celery import shared_task from sentry_sdk import capture_exception # 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 @@ -20,6 +20,7 @@ from plane.bgtasks.issue_activites_task import issue_activity def archive_and_close_old_issues(): archive_old_issues() close_old_issues() + delete_archived_pages() def archive_old_issues(): @@ -67,7 +68,7 @@ def archive_old_issues(): issues_to_update.append(issue) # Bulk Update the issues and log the activity - if issues_to_update: + if issues_to_update: Issue.objects.bulk_update( issues_to_update, ["archived_at"], batch_size=100 ) @@ -80,7 +81,7 @@ def archive_old_issues(): project_id=project_id, current_instance=None, subscriber=False, - epoch=int(timezone.now().timestamp()) + epoch=int(timezone.now().timestamp()), ) for issue in issues_to_update ] @@ -142,17 +143,21 @@ def close_old_issues(): # Bulk Update the issues and log the activity 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( 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), issue_id=issue.id, project_id=project_id, current_instance=None, subscriber=False, - epoch=int(timezone.now().timestamp()) + epoch=int(timezone.now().timestamp()), ) for issue in issues_to_update ] @@ -162,3 +167,20 @@ def close_old_issues(): print(e) capture_exception(e) 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 + diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index 9496b5906..e18f5a6e9 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -68,7 +68,7 @@ from .integration import ( from .importer import Importer -from .page import Page, PageBlock, PageFavorite, PageLabel +from .page import Page, PageTransaction, PageFavorite, PageLabel from .estimate import Estimate, EstimatePoint diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index 3ba054d49..ba46ae9f7 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -114,22 +114,22 @@ class Issue(ProjectBaseModel): self.state = default_state except ImportError: pass - else: - try: - from plane.db.models import State, PageBlock + # else: + # try: + # from plane.db.models import State, PageBlock - # Check if the current issue state and completed state id are same - if self.state.group == "completed": - self.completed_at = timezone.now() - # check if there are any page blocks - PageBlock.objects.filter(issue_id=self.id).filter().update( - completed_at=timezone.now() - ) - else: - PageBlock.objects.filter(issue_id=self.id).filter().update( - completed_at=None - ) - self.completed_at = None + # # Check if the current issue state and completed state id are same + # if self.state.group == "completed": + # self.completed_at = timezone.now() + # # check if there are any page blocks + # PageBlock.objects.filter(issue_id=self.id).filter().update( + # completed_at=timezone.now() + # ) + # else: + # PageBlock.objects.filter(issue_id=self.id).filter().update( + # completed_at=None + # ) + # self.completed_at = None except ImportError: pass diff --git a/apiserver/plane/db/models/page.py b/apiserver/plane/db/models/page.py index 8efd864e4..51d377a54 100644 --- a/apiserver/plane/db/models/page.py +++ b/apiserver/plane/db/models/page.py @@ -57,7 +57,7 @@ class PageTransaction(ProjectBaseModel): ("forward_link", "Forward Link"), ("mention", "Mention"), ) - transaction = models.CharField(max_length=255) + transaction = models.UUIDField(null=True) page = models.ForeignKey( Page, related_name="page_relation", on_delete=models.CASCADE ) @@ -70,6 +70,7 @@ class PageTransaction(ProjectBaseModel): ) class Meta: + unique_together = ["page", "transaction"] verbose_name = "Page Transaction" verbose_name_plural = "Page Transactions" db_table = "page_transactions"