From db7d15e99c93f3344ced9a11a5bda07f8156d817 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Wed, 30 Aug 2023 12:46:10 +0530 Subject: [PATCH 1/3] chore: tracking the issues reaction and vote history --- apiserver/plane/api/views/issue.py | 116 +++++++++++++- apiserver/plane/api/views/workspace.py | 1 + .../plane/bgtasks/issue_activites_task.py | 149 ++++++++++++++++++ 3 files changed, 265 insertions(+), 1 deletion(-) diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 05434aec5aa..72dc6a78e26 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -485,7 +485,7 @@ def get(self, request, slug, project_id, issue_id): issue_activities = ( IssueActivity.objects.filter(issue_id=issue_id) .filter( - ~Q(field="comment"), + ~Q(field__in=["comment", "vote", "reaction"]), project__project_projectmember__member=self.request.user, ) .select_related("actor", "workspace", "issue", "project") @@ -1404,6 +1404,14 @@ def perform_create(self, serializer): project_id=self.kwargs.get("project_id"), actor=self.request.user, ) + issue_activity.delay( + type="issue-reaction.activity.created", + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + actor_id=str(self.request.user.id), + issue_id=str(self.kwargs.get("issue_id", None)), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=None, + ) def destroy(self, request, slug, project_id, issue_id, reaction_code): try: @@ -1414,6 +1422,19 @@ def destroy(self, request, slug, project_id, issue_id, reaction_code): reaction=reaction_code, actor=request.user, ) + issue_activity.delay( + type="issue-reaction.activity.deleted", + requested_data=None, + actor_id=str(self.request.user.id), + issue_id=str(self.kwargs.get("issue_id", None)), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=json.dumps( + { + "reaction": str(reaction_code), + "identifier": str(issue_reaction.id), + } + ), + ) issue_reaction.delete() return Response(status=status.HTTP_204_NO_CONTENT) except IssueReaction.DoesNotExist: @@ -1454,6 +1475,15 @@ def perform_create(self, serializer): comment_id=self.kwargs.get("comment_id"), project_id=self.kwargs.get("project_id"), ) + comment = IssueComment.objects.get(pk=self.kwargs.get("comment_id"),project_id=self.kwargs.get("project_id")) + issue_activity.delay( + type="comment-reaction.activity.created", + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + actor_id=str(self.request.user.id), + issue_id=str(comment.issue_id), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=None, + ) def destroy(self, request, slug, project_id, comment_id, reaction_code): try: @@ -1464,6 +1494,20 @@ def destroy(self, request, slug, project_id, comment_id, reaction_code): reaction=reaction_code, actor=request.user, ) + comment = IssueComment.objects.get(pk=self.kwargs.get("comment_id"),project_id=project_id) + issue_activity.delay( + type="comment-reaction.activity.deleted", + requested_data=None, + actor_id=str(self.request.user.id), + issue_id=str(comment.issue_id), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=json.dumps( + { + "reaction": str(reaction_code), + "identifier": str(comment_reaction.id), + } + ), + ) comment_reaction.delete() return Response(status=status.HTTP_204_NO_CONTENT) except CommentReaction.DoesNotExist: @@ -1671,6 +1715,14 @@ def create(self, request, slug, project_id, issue_id): serializer.save( project_id=project_id, issue_id=issue_id, actor=request.user ) + issue_activity.delay( + type="issue-reaction.activity.created", + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + actor_id=str(self.request.user.id), + issue_id=str(self.kwargs.get("issue_id", None)), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=None, + ) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except ProjectDeployBoard.DoesNotExist: @@ -1702,6 +1754,19 @@ def destroy(self, request, slug, project_id, issue_id, reaction_code): reaction=reaction_code, actor=request.user, ) + issue_activity.delay( + type="issue-reaction.activity.deleted", + requested_data=None, + actor_id=str(self.request.user.id), + issue_id=str(self.kwargs.get("issue_id", None)), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=json.dumps( + { + "reaction": str(reaction_code), + "identifier": str(issue_reaction.id), + } + ), + ) issue_reaction.delete() return Response(status=status.HTTP_204_NO_CONTENT) except IssueReaction.DoesNotExist: @@ -1756,8 +1821,22 @@ def create(self, request, slug, project_id, comment_id): serializer.save( project_id=project_id, comment_id=comment_id, actor=request.user ) + comment = IssueComment.objects.get(pk=self.kwargs.get("comment_id"),project_id=project_id) + issue_activity.delay( + type="comment-reaction.activity.created", + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + actor_id=str(self.request.user.id), + issue_id=str(comment.issue_id), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=None, + ) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + except IssueComment.DoesNotExist: + return Response( + {"error": "Comment does not exist"}, + status=status.HTTP_400_BAD_REQUEST, + ) except ProjectDeployBoard.DoesNotExist: return Response( {"error": "Project board does not exist"}, @@ -1788,6 +1867,20 @@ def destroy(self, request, slug, project_id, comment_id, reaction_code): reaction=reaction_code, actor=request.user, ) + comment = IssueComment.objects.get(pk=self.kwargs.get("comment_id"),project_id=project_id) + issue_activity.delay( + type="comment-reaction.activity.deleted", + requested_data=None, + actor_id=str(self.request.user.id), + issue_id=str(comment.issue_id), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=json.dumps( + { + "reaction": str(reaction_code), + "identifier": str(comment_reaction.id), + } + ), + ) comment_reaction.delete() return Response(status=status.HTTP_204_NO_CONTENT) except CommentReaction.DoesNotExist: @@ -1825,6 +1918,14 @@ def create(self, request, slug, project_id, issue_id): ) issue_vote.vote = request.data.get("vote", 1) issue_vote.save() + issue_activity.delay( + type="issue-vote.activity.created", + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + actor_id=str(self.request.user.id), + issue_id=str(self.kwargs.get("issue_id", None)), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=None, + ) serializer = IssueVoteSerializer(issue_vote) return Response(serializer.data, status=status.HTTP_201_CREATED) except Exception as e: @@ -1842,6 +1943,19 @@ def destroy(self, request, slug, project_id, issue_id): issue_id=issue_id, actor_id=request.user.id, ) + issue_activity.delay( + type="issue-vote.activity.deleted", + requested_data=None, + actor_id=str(self.request.user.id), + issue_id=str(self.kwargs.get("issue_id", None)), + project_id=str(self.kwargs.get("project_id", None)), + current_instance=json.dumps( + { + "vote": str(issue_vote.vote), + "identifier": str(issue_vote.id), + } + ), + ) issue_vote.delete() return Response(status=status.HTTP_204_NO_CONTENT) except Exception as e: diff --git a/apiserver/plane/api/views/workspace.py b/apiserver/plane/api/views/workspace.py index cfdd0dd9bf0..ef77a04b485 100644 --- a/apiserver/plane/api/views/workspace.py +++ b/apiserver/plane/api/views/workspace.py @@ -1198,6 +1198,7 @@ def get(self, request, slug, user_id): projects = request.query_params.getlist("project", []) queryset = IssueActivity.objects.filter( + ~Q(field__in=["comment", "vote", "reaction"]), workspace__slug=slug, project__project_projectmember__member=request.user, actor=user_id, diff --git a/apiserver/plane/bgtasks/issue_activites_task.py b/apiserver/plane/bgtasks/issue_activites_task.py index 1cc6c85cc9a..8705f5a149c 100644 --- a/apiserver/plane/bgtasks/issue_activites_task.py +++ b/apiserver/plane/bgtasks/issue_activites_task.py @@ -24,6 +24,8 @@ IssueSubscriber, Notification, IssueAssignee, + IssueReaction, + CommentReaction, ) from plane.api.serializers import IssueActivitySerializer @@ -1022,6 +1024,147 @@ def delete_attachment_activity( ) ) +def create_issue_reaction_activity( + requested_data, current_instance, issue_id, project, actor, issue_activities +): + requested_data = json.loads(requested_data) if requested_data is not None else None + if requested_data and requested_data.get("reaction") is not None: + issue_reaction = IssueReaction.objects.filter(reaction=requested_data.get("reaction"), project=project, actor=actor).values_list('id', flat=True).first() + if issue_reaction is not None: + issue_activities.append( + IssueActivity( + issue_id=issue_id, + actor=actor, + verb="created", + old_value=None, + new_value=requested_data.get("reaction"), + field="reaction", + project=project, + workspace=project.workspace, + comment="added the reaction", + old_identifier=None, + new_identifier=issue_reaction, + ) + ) + + +def delete_issue_reaction_activity( + requested_data, current_instance, issue_id, project, actor, issue_activities +): + current_instance = ( + json.loads(current_instance) if current_instance is not None else None + ) + if current_instance and current_instance.get("reaction") is not None: + issue_activities.append( + IssueActivity( + issue_id=issue_id, + actor=actor, + verb="deleted", + old_value=current_instance.get("reaction"), + new_value=None, + field="reaction", + project=project, + workspace=project.workspace, + comment="removed the reaction", + old_identifier=current_instance.get("identifier"), + new_identifier=None, + ) + ) + + +def create_comment_reaction_activity( + requested_data, current_instance, issue_id, project, actor, issue_activities +): + requested_data = json.loads(requested_data) if requested_data is not None else None + if requested_data and requested_data.get("reaction") is not None: + comment_reaction = CommentReaction.objects.filter(reaction=requested_data.get("reaction"), project=project, actor=actor).values_list('id', flat=True).first() + if comment_reaction is not None: + issue_activities.append( + IssueActivity( + issue_id=issue_id, + actor=actor, + verb="created", + old_value=None, + new_value=requested_data.get("reaction"), + field="reaction", + project=project, + workspace=project.workspace, + comment="added the reaction", + old_identifier=None, + new_identifier=comment_reaction, + ) + ) + + +def delete_comment_reaction_activity( + requested_data, current_instance, issue_id, project, actor, issue_activities +): + current_instance = ( + json.loads(current_instance) if current_instance is not None else None + ) + if current_instance and current_instance.get("reaction") is not None: + issue_activities.append( + IssueActivity( + issue_id=issue_id, + actor=actor, + verb="deleted", + old_value=current_instance.get("reaction"), + new_value=None, + field="reaction", + project=project, + workspace=project.workspace, + comment="removed the reaction", + old_identifier=current_instance.get("identifier"), + new_identifier=None, + ) + ) + + +def create_issue_vote_activity( + requested_data, current_instance, issue_id, project, actor, issue_activities +): + requested_data = json.loads(requested_data) if requested_data is not None else None + if requested_data and requested_data.get("vote") is not None: + issue_activities.append( + IssueActivity( + issue_id=issue_id, + actor=actor, + verb="created", + old_value=None, + new_value=requested_data.get("vote"), + field="vote", + project=project, + workspace=project.workspace, + comment="added the vote", + old_identifier=None, + new_identifier=None, + ) + ) + + +def delete_issue_vote_activity( + requested_data, current_instance, issue_id, project, actor, issue_activities +): + current_instance = ( + json.loads(current_instance) if current_instance is not None else None + ) + if current_instance and current_instance.get("vote") is not None: + issue_activities.append( + IssueActivity( + issue_id=issue_id, + actor=actor, + verb="deleted", + old_value=current_instance.get("vote"), + new_value=None, + field="vote", + project=project, + workspace=project.workspace, + comment="removed the vote", + old_identifier=current_instance.get("identifier"), + new_identifier=None, + ) + ) + # Receive message from room group @shared_task @@ -1080,6 +1223,12 @@ def issue_activity( "link.activity.deleted": delete_link_activity, "attachment.activity.created": create_attachment_activity, "attachment.activity.deleted": delete_attachment_activity, + "issue-reaction.activity.created": create_issue_reaction_activity, + "issue-reaction.activity.deleted": delete_issue_reaction_activity, + "comment-reaction.activity.created": create_comment_reaction_activity, + "comment-reaction.activity.deleted": delete_comment_reaction_activity, + "issue-vote.activity.created": create_issue_vote_activity, + "issue-vote.activity.deleted": delete_issue_vote_activity, } func = ACTIVITY_MAPPER.get(type) From 64ea6a6e9a6b1f90e06d19f51252216df6567994 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Wed, 30 Aug 2023 15:26:23 +0530 Subject: [PATCH 2/3] fix: changed the keywords for vote and reaction --- apiserver/plane/api/views/issue.py | 34 ++++++++--------- .../plane/bgtasks/issue_activites_task.py | 37 +++++++++++++------ 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 72dc6a78e26..58c2fbaa9b8 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -1405,7 +1405,7 @@ def perform_create(self, serializer): actor=self.request.user, ) issue_activity.delay( - type="issue-reaction.activity.created", + type="issue_reaction.activity.created", requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), @@ -1423,7 +1423,7 @@ def destroy(self, request, slug, project_id, issue_id, reaction_code): actor=request.user, ) issue_activity.delay( - type="issue-reaction.activity.deleted", + type="issue_reaction.activity.deleted", requested_data=None, actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), @@ -1475,12 +1475,11 @@ def perform_create(self, serializer): comment_id=self.kwargs.get("comment_id"), project_id=self.kwargs.get("project_id"), ) - comment = IssueComment.objects.get(pk=self.kwargs.get("comment_id"),project_id=self.kwargs.get("project_id")) issue_activity.delay( - type="comment-reaction.activity.created", + type="comment_reaction.activity.created", requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), - issue_id=str(comment.issue_id), + issue_id=None, project_id=str(self.kwargs.get("project_id", None)), current_instance=None, ) @@ -1494,17 +1493,17 @@ def destroy(self, request, slug, project_id, comment_id, reaction_code): reaction=reaction_code, actor=request.user, ) - comment = IssueComment.objects.get(pk=self.kwargs.get("comment_id"),project_id=project_id) issue_activity.delay( - type="comment-reaction.activity.deleted", + type="comment_reaction.activity.deleted", requested_data=None, actor_id=str(self.request.user.id), - issue_id=str(comment.issue_id), + issue_id=None, project_id=str(self.kwargs.get("project_id", None)), current_instance=json.dumps( { "reaction": str(reaction_code), "identifier": str(comment_reaction.id), + "comment_id": str(comment_id) } ), ) @@ -1716,7 +1715,7 @@ def create(self, request, slug, project_id, issue_id): project_id=project_id, issue_id=issue_id, actor=request.user ) issue_activity.delay( - type="issue-reaction.activity.created", + type="issue_reaction.activity.created", requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), @@ -1755,7 +1754,7 @@ def destroy(self, request, slug, project_id, issue_id, reaction_code): actor=request.user, ) issue_activity.delay( - type="issue-reaction.activity.deleted", + type="issue_reaction.activity.deleted", requested_data=None, actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), @@ -1821,12 +1820,11 @@ def create(self, request, slug, project_id, comment_id): serializer.save( project_id=project_id, comment_id=comment_id, actor=request.user ) - comment = IssueComment.objects.get(pk=self.kwargs.get("comment_id"),project_id=project_id) issue_activity.delay( - type="comment-reaction.activity.created", + type="comment_reaction.activity.created", requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), - issue_id=str(comment.issue_id), + issue_id=None, project_id=str(self.kwargs.get("project_id", None)), current_instance=None, ) @@ -1867,17 +1865,17 @@ def destroy(self, request, slug, project_id, comment_id, reaction_code): reaction=reaction_code, actor=request.user, ) - comment = IssueComment.objects.get(pk=self.kwargs.get("comment_id"),project_id=project_id) issue_activity.delay( - type="comment-reaction.activity.deleted", + type="comment_reaction.activity.deleted", requested_data=None, actor_id=str(self.request.user.id), - issue_id=str(comment.issue_id), + issue_id=None, project_id=str(self.kwargs.get("project_id", None)), current_instance=json.dumps( { "reaction": str(reaction_code), "identifier": str(comment_reaction.id), + "comment_id": str(comment_id) } ), ) @@ -1919,7 +1917,7 @@ def create(self, request, slug, project_id, issue_id): issue_vote.vote = request.data.get("vote", 1) issue_vote.save() issue_activity.delay( - type="issue-vote.activity.created", + type="issue_vote.activity.created", requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), @@ -1944,7 +1942,7 @@ def destroy(self, request, slug, project_id, issue_id): actor_id=request.user.id, ) issue_activity.delay( - type="issue-vote.activity.deleted", + type="issue_vote.activity.deleted", requested_data=None, actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), diff --git a/apiserver/plane/bgtasks/issue_activites_task.py b/apiserver/plane/bgtasks/issue_activites_task.py index 8705f5a149c..da5466ebb50 100644 --- a/apiserver/plane/bgtasks/issue_activites_task.py +++ b/apiserver/plane/bgtasks/issue_activites_task.py @@ -26,6 +26,7 @@ IssueAssignee, IssueReaction, CommentReaction, + IssueComment, ) from plane.api.serializers import IssueActivitySerializer @@ -631,7 +632,7 @@ def update_issue_activity( "parent": track_parent, "priority": track_priority, "state": track_state, - "description": track_description, + "description_html": track_description, "target_date": track_target_date, "start_date": track_start_date, "labels_list": track_labels, @@ -1077,11 +1078,12 @@ def create_comment_reaction_activity( ): requested_data = json.loads(requested_data) if requested_data is not None else None if requested_data and requested_data.get("reaction") is not None: - comment_reaction = CommentReaction.objects.filter(reaction=requested_data.get("reaction"), project=project, actor=actor).values_list('id', flat=True).first() - if comment_reaction is not None: + comment_reaction_id, comment_id = CommentReaction.objects.filter(reaction=requested_data.get("reaction"), project=project, actor=actor).values_list('id', 'comment__id').first() + comment = IssueComment.objects.get(pk=comment_id,project=project) + if comment is not None and comment_id is not None: issue_activities.append( IssueActivity( - issue_id=issue_id, + issue_id=comment.issue_id, actor=actor, verb="created", old_value=None, @@ -1091,7 +1093,7 @@ def create_comment_reaction_activity( workspace=project.workspace, comment="added the reaction", old_identifier=None, - new_identifier=comment_reaction, + new_identifier=comment_reaction_id, ) ) @@ -1103,6 +1105,7 @@ def delete_comment_reaction_activity( json.loads(current_instance) if current_instance is not None else None ) if current_instance and current_instance.get("reaction") is not None: + issue_id = IssueComment.objects.filter(pk=current_instance.get("comment_id"), project=project).values_list('issue_id', flat=True).first() issue_activities.append( IssueActivity( issue_id=issue_id, @@ -1188,6 +1191,12 @@ def issue_activity( "cycle.activity.deleted", "module.activity.created", "module.activity.deleted", + "issue_reaction.activity.created", + "issue_reaction.activity.deleted", + "comment_reaction.activity.created", + "comment_reaction.activity.deleted", + "issue_vote.activity.created", + "issue_vote.activity.deleted", ]: issue = Issue.objects.filter(pk=issue_id).first() @@ -1223,12 +1232,12 @@ def issue_activity( "link.activity.deleted": delete_link_activity, "attachment.activity.created": create_attachment_activity, "attachment.activity.deleted": delete_attachment_activity, - "issue-reaction.activity.created": create_issue_reaction_activity, - "issue-reaction.activity.deleted": delete_issue_reaction_activity, - "comment-reaction.activity.created": create_comment_reaction_activity, - "comment-reaction.activity.deleted": delete_comment_reaction_activity, - "issue-vote.activity.created": create_issue_vote_activity, - "issue-vote.activity.deleted": delete_issue_vote_activity, + "issue_reaction.activity.created": create_issue_reaction_activity, + "issue_reaction.activity.deleted": delete_issue_reaction_activity, + "comment_reaction.activity.created": create_comment_reaction_activity, + "comment_reaction.activity.deleted": delete_comment_reaction_activity, + "issue_vote.activity.created": create_issue_vote_activity, + "issue_vote.activity.deleted": delete_issue_vote_activity, } func = ACTIVITY_MAPPER.get(type) @@ -1268,6 +1277,12 @@ def issue_activity( "cycle.activity.deleted", "module.activity.created", "module.activity.deleted", + "issue_reaction.activity.created", + "issue_reaction.activity.deleted", + "comment_reaction.activity.created", + "comment_reaction.activity.deleted", + "issue_vote.activity.created", + "issue_vote.activity.deleted", ]: # Create Notifications bulk_notifications = [] From e81815c5ceefc049abc534795a626be6540eb573 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Wed, 30 Aug 2023 16:36:23 +0530 Subject: [PATCH 3/3] chore: added validation --- .../plane/bgtasks/issue_activites_task.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/apiserver/plane/bgtasks/issue_activites_task.py b/apiserver/plane/bgtasks/issue_activites_task.py index da5466ebb50..0cadac55323 100644 --- a/apiserver/plane/bgtasks/issue_activites_task.py +++ b/apiserver/plane/bgtasks/issue_activites_task.py @@ -1080,7 +1080,7 @@ def create_comment_reaction_activity( if requested_data and requested_data.get("reaction") is not None: comment_reaction_id, comment_id = CommentReaction.objects.filter(reaction=requested_data.get("reaction"), project=project, actor=actor).values_list('id', 'comment__id').first() comment = IssueComment.objects.get(pk=comment_id,project=project) - if comment is not None and comment_id is not None: + if comment is not None and comment_reaction_id is not None and comment_id is not None: issue_activities.append( IssueActivity( issue_id=comment.issue_id, @@ -1106,21 +1106,22 @@ def delete_comment_reaction_activity( ) if current_instance and current_instance.get("reaction") is not None: issue_id = IssueComment.objects.filter(pk=current_instance.get("comment_id"), project=project).values_list('issue_id', flat=True).first() - issue_activities.append( - IssueActivity( - issue_id=issue_id, - actor=actor, - verb="deleted", - old_value=current_instance.get("reaction"), - new_value=None, - field="reaction", - project=project, - workspace=project.workspace, - comment="removed the reaction", - old_identifier=current_instance.get("identifier"), - new_identifier=None, + if issue_id is not None: + issue_activities.append( + IssueActivity( + issue_id=issue_id, + actor=actor, + verb="deleted", + old_value=current_instance.get("reaction"), + new_value=None, + field="reaction", + project=project, + workspace=project.workspace, + comment="removed the reaction", + old_identifier=current_instance.get("identifier"), + new_identifier=None, + ) ) - ) def create_issue_vote_activity(