NODEDC_TASKMANAGER/plane-src/apps/api/plane/utils/external_contours.py

124 lines
4.7 KiB
Python

# Copyright (c) 2023-present Plane Software, Inc. and contributors
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.
import json
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import timezone
from django.utils.html import escape
from plane.app.serializers import IssueCommentSerializer
from plane.db.models import IssueComment, Notification, Project
EXTERNAL_CONTOUR_NOTIFICATION_FIELDS = {"state", "comment", "attachment", "description"}
def build_external_contour_comment_html(comment: str) -> str:
escaped_comment = escape((comment or "").strip())
escaped_comment = escaped_comment.replace("\n", "<br />")
return f"<p>{escaped_comment}</p>"
def create_external_contour_issue_comment(*, issue, actor, comment: str, origin: str):
from plane.bgtasks.issue_activities_task import issue_activity
comment_html = build_external_contour_comment_html(comment)
serializer = IssueCommentSerializer(data={"comment_html": comment_html})
serializer.is_valid(raise_exception=True)
serializer.save(project_id=issue.project_id, issue_id=issue.id, actor=actor)
issue_activity.delay(
type="comment.activity.created",
requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder),
actor_id=str(actor.id),
issue_id=str(issue.id),
project_id=str(issue.project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=origin,
)
return serializer.data
def create_external_contour_notification(*, contour_request, issue, issue_activity_data):
extra = contour_request.extra or {}
receiver_id = extra.get("requested_by_id")
source_project_id = extra.get("source_project_id")
field = issue_activity_data.get("field")
actor_id = issue_activity_data.get("actor_id") or (issue_activity_data.get("actor_detail") or {}).get("id")
if not receiver_id or not source_project_id or not field:
return None
if field not in EXTERNAL_CONTOUR_NOTIFICATION_FIELDS:
return None
if str(receiver_id) == str(actor_id):
return None
source_project = Project.objects.filter(
pk=source_project_id,
workspace_id=issue.workspace_id,
archived_at__isnull=True,
).first()
if not source_project:
return None
issue_comment = (
IssueComment.objects.filter(
id=issue_activity_data.get("issue_comment"),
issue_id=issue.id,
project_id=issue.project_id,
workspace_id=issue.workspace_id,
).first()
if issue_activity_data.get("issue_comment")
else None
)
return Notification(
workspace=source_project.workspace,
project=source_project,
sender=f"in_app:external_contours:{field}",
triggered_by_id=actor_id,
receiver_id=receiver_id,
entity_identifier=issue.id,
entity_name="issue",
title=issue_activity_data.get("comment") or "External contour updated",
data={
"issue": {
"id": str(contour_request.id),
"name": str(issue.name),
"identifier": str(issue.project.identifier),
"sequence_id": issue.sequence_id,
"state_name": issue.state.name if issue.state else None,
"state_group": issue.state.group if issue.state else None,
"project_id": str(source_project.id),
"workspace_slug": str(source_project.workspace.slug),
"external_contour_request_id": str(contour_request.id),
"target_issue_id": str(issue.id),
"target_project_id": str(issue.project_id),
"target_project_name": str(issue.project.name),
"source_project_id": str(source_project.id),
"source_project_name": str(source_project.name),
},
"issue_activity": {
"id": str(issue_activity_data.get("id")),
"verb": str(issue_activity_data.get("verb")),
"field": str(field),
"actor": str(actor_id),
"new_value": str(issue_activity_data.get("new_value")),
"old_value": str(issue_activity_data.get("old_value")),
"issue_comment": str(issue_comment.comment_stripped if issue_comment is not None else ""),
"old_identifier": (
str(issue_activity_data.get("old_identifier")) if issue_activity_data.get("old_identifier") else None
),
"new_identifier": (
str(issue_activity_data.get("new_identifier")) if issue_activity_data.get("new_identifier") else None
),
},
},
)