From 5908998127f78db8ed05fc5a69c717482868282e Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Fri, 13 Dec 2024 22:30:29 +0530 Subject: [PATCH] [WEB-2854] chore: trigger issue_description_version task on issue create and update (#6202) * chore: issue description version task trigger from issue create and update * chore: add default value in prop --- apiserver/plane/app/views/issue/base.py | 14 ++++ .../bgtasks/issue_description_version_task.py | 84 +++++++++++++++++++ apiserver/plane/db/models/issue.py | 10 ++- apiserver/plane/settings/common.py | 3 + 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 apiserver/plane/bgtasks/issue_description_version_task.py diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index e087a3659..01f4068ee 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -58,6 +58,7 @@ from plane.utils.timezone_converter import user_timezone_converter from plane.bgtasks.recent_visited_task import recent_visited_task from plane.utils.global_paginator import paginate from plane.bgtasks.webhook_task import model_activity +from plane.bgtasks.issue_description_version_task import issue_description_version_task class IssueListEndpoint(BaseAPIView): @@ -428,6 +429,13 @@ class IssueViewSet(BaseViewSet): slug=slug, origin=request.META.get("HTTP_ORIGIN"), ) + # updated issue description version + issue_description_version_task.delay( + updated_issue=json.dumps(request.data, cls=DjangoJSONEncoder), + issue_id=str(serializer.data["id"]), + user_id=request.user.id, + is_creating=True, + ) return Response(issue, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -649,6 +657,12 @@ class IssueViewSet(BaseViewSet): slug=slug, origin=request.META.get("HTTP_ORIGIN"), ) + # updated issue description version + issue_description_version_task.delay( + updated_issue=current_instance, + issue_id=str(serializer.data.get("id", None)), + user_id=request.user.id, + ) return Response(status=status.HTTP_204_NO_CONTENT) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/apiserver/plane/bgtasks/issue_description_version_task.py b/apiserver/plane/bgtasks/issue_description_version_task.py new file mode 100644 index 000000000..a29fb6c57 --- /dev/null +++ b/apiserver/plane/bgtasks/issue_description_version_task.py @@ -0,0 +1,84 @@ +from celery import shared_task +from django.db import transaction +from django.utils import timezone +from typing import Optional, Dict +import json + +from plane.db.models import Issue, IssueDescriptionVersion +from plane.utils.exception_logger import log_exception + + +def should_update_existing_version( + version: IssueDescriptionVersion, user_id: str, max_time_difference: int = 600 +) -> bool: + if not version: + return + + time_difference = (timezone.now() - version.last_saved_at).total_seconds() + return ( + str(version.owned_by_id) == str(user_id) + and time_difference <= max_time_difference + ) + + +def update_existing_version(version: IssueDescriptionVersion, issue) -> None: + version.description_json = issue.description + version.description_html = issue.description_html + version.description_binary = issue.description_binary + version.description_stripped = issue.description_stripped + version.last_saved_at = timezone.now() + + version.save( + update_fields=[ + "description_json", + "description_html", + "description_binary", + "description_stripped", + "last_saved_at", + ] + ) + + +@shared_task +def issue_description_version_task( + updated_issue, issue_id, user_id, is_creating=False +) -> Optional[bool]: + try: + # Parse updated issue data + current_issue: Dict = json.loads(updated_issue) if updated_issue else {} + + # Get current issue + issue = Issue.objects.get(id=issue_id) + + # Check if description has changed + if ( + current_issue.get("description_html") == issue.description_html + and not is_creating + ): + return + + with transaction.atomic(): + # Get latest version + latest_version = ( + IssueDescriptionVersion.objects.filter(issue_id=issue_id) + .order_by("-last_saved_at") + .first() + ) + + # Determine whether to update existing or create new version + if should_update_existing_version(version=latest_version, user_id=user_id): + update_existing_version(latest_version, issue) + else: + IssueDescriptionVersion.log_issue_description_version(issue, user_id) + + return + + except Issue.DoesNotExist: + # Issue no longer exists, skip processing + return + except json.JSONDecodeError as e: + log_exception(f"Invalid JSON for updated_issue: {e}") + return + except Exception as e: + log_exception(f"Error processing issue description version: {e}") + return diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index 8d1a3f320..ca7347ad7 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -804,13 +804,17 @@ class IssueDescriptionVersion(ProjectBaseModel): Log the issue description version """ cls.objects.create( - issue=issue, + workspace_id=issue.workspace_id, + project_id=issue.project_id, + created_by_id=issue.created_by_id, + updated_by_id=issue.updated_by_id, + owned_by_id=user, + last_saved_at=timezone.now(), + issue_id=issue.id, description_binary=issue.description_binary, description_html=issue.description_html, description_stripped=issue.description_stripped, description_json=issue.description, - last_saved_at=timezone.now(), - owned_by=user, ) return True except Exception as e: diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index ed42dfe19..db9a24453 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -262,6 +262,9 @@ CELERY_IMPORTS = ( "plane.license.bgtasks.tracer", # management tasks "plane.bgtasks.dummy_data_task", + # issue version tasks + "plane.bgtasks.issue_version_sync", + "plane.bgtasks.issue_description_version_sync", ) # Sentry Settings