mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: tracking the history of issue reactions and votes. (#2020)
* chore: tracking the issues reaction and vote history * fix: changed the keywords for vote and reaction * chore: added validation
This commit is contained in:
parent
f5a076e9a9
commit
6c6b81bea7
@ -486,7 +486,7 @@ class IssueActivityEndpoint(BaseAPIView):
|
|||||||
issue_activities = (
|
issue_activities = (
|
||||||
IssueActivity.objects.filter(issue_id=issue_id)
|
IssueActivity.objects.filter(issue_id=issue_id)
|
||||||
.filter(
|
.filter(
|
||||||
~Q(field="comment"),
|
~Q(field__in=["comment", "vote", "reaction"]),
|
||||||
project__project_projectmember__member=self.request.user,
|
project__project_projectmember__member=self.request.user,
|
||||||
)
|
)
|
||||||
.select_related("actor", "workspace", "issue", "project")
|
.select_related("actor", "workspace", "issue", "project")
|
||||||
@ -1405,6 +1405,14 @@ class IssueReactionViewSet(BaseViewSet):
|
|||||||
project_id=self.kwargs.get("project_id"),
|
project_id=self.kwargs.get("project_id"),
|
||||||
actor=self.request.user,
|
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):
|
def destroy(self, request, slug, project_id, issue_id, reaction_code):
|
||||||
try:
|
try:
|
||||||
@ -1415,6 +1423,19 @@ class IssueReactionViewSet(BaseViewSet):
|
|||||||
reaction=reaction_code,
|
reaction=reaction_code,
|
||||||
actor=request.user,
|
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()
|
issue_reaction.delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
except IssueReaction.DoesNotExist:
|
except IssueReaction.DoesNotExist:
|
||||||
@ -1455,6 +1476,14 @@ class CommentReactionViewSet(BaseViewSet):
|
|||||||
comment_id=self.kwargs.get("comment_id"),
|
comment_id=self.kwargs.get("comment_id"),
|
||||||
project_id=self.kwargs.get("project_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=None,
|
||||||
|
project_id=str(self.kwargs.get("project_id", None)),
|
||||||
|
current_instance=None,
|
||||||
|
)
|
||||||
|
|
||||||
def destroy(self, request, slug, project_id, comment_id, reaction_code):
|
def destroy(self, request, slug, project_id, comment_id, reaction_code):
|
||||||
try:
|
try:
|
||||||
@ -1465,6 +1494,20 @@ class CommentReactionViewSet(BaseViewSet):
|
|||||||
reaction=reaction_code,
|
reaction=reaction_code,
|
||||||
actor=request.user,
|
actor=request.user,
|
||||||
)
|
)
|
||||||
|
issue_activity.delay(
|
||||||
|
type="comment_reaction.activity.deleted",
|
||||||
|
requested_data=None,
|
||||||
|
actor_id=str(self.request.user.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)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
comment_reaction.delete()
|
comment_reaction.delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
except CommentReaction.DoesNotExist:
|
except CommentReaction.DoesNotExist:
|
||||||
@ -1691,6 +1734,14 @@ class IssueReactionPublicViewSet(BaseViewSet):
|
|||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
member=request.user,
|
member=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.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
except ProjectDeployBoard.DoesNotExist:
|
except ProjectDeployBoard.DoesNotExist:
|
||||||
@ -1722,6 +1773,19 @@ class IssueReactionPublicViewSet(BaseViewSet):
|
|||||||
reaction=reaction_code,
|
reaction=reaction_code,
|
||||||
actor=request.user,
|
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()
|
issue_reaction.delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
except IssueReaction.DoesNotExist:
|
except IssueReaction.DoesNotExist:
|
||||||
@ -1784,8 +1848,21 @@ class CommentReactionPublicViewSet(BaseViewSet):
|
|||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
member=request.user,
|
member=request.user,
|
||||||
)
|
)
|
||||||
|
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=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.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
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:
|
except ProjectDeployBoard.DoesNotExist:
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Project board does not exist"},
|
{"error": "Project board does not exist"},
|
||||||
@ -1816,6 +1893,20 @@ class CommentReactionPublicViewSet(BaseViewSet):
|
|||||||
reaction=reaction_code,
|
reaction=reaction_code,
|
||||||
actor=request.user,
|
actor=request.user,
|
||||||
)
|
)
|
||||||
|
issue_activity.delay(
|
||||||
|
type="comment_reaction.activity.deleted",
|
||||||
|
requested_data=None,
|
||||||
|
actor_id=str(self.request.user.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)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
comment_reaction.delete()
|
comment_reaction.delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
except CommentReaction.DoesNotExist:
|
except CommentReaction.DoesNotExist:
|
||||||
@ -1861,6 +1952,14 @@ class IssueVotePublicViewSet(BaseViewSet):
|
|||||||
)
|
)
|
||||||
issue_vote.vote = request.data.get("vote", 1)
|
issue_vote.vote = request.data.get("vote", 1)
|
||||||
issue_vote.save()
|
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)
|
serializer = IssueVoteSerializer(issue_vote)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -1878,6 +1977,19 @@ class IssueVotePublicViewSet(BaseViewSet):
|
|||||||
issue_id=issue_id,
|
issue_id=issue_id,
|
||||||
actor_id=request.user.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()
|
issue_vote.delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -1197,6 +1197,7 @@ class WorkspaceUserActivityEndpoint(BaseAPIView):
|
|||||||
projects = request.query_params.getlist("project", [])
|
projects = request.query_params.getlist("project", [])
|
||||||
|
|
||||||
queryset = IssueActivity.objects.filter(
|
queryset = IssueActivity.objects.filter(
|
||||||
|
~Q(field__in=["comment", "vote", "reaction"]),
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
project__project_projectmember__member=request.user,
|
project__project_projectmember__member=request.user,
|
||||||
actor=user_id,
|
actor=user_id,
|
||||||
|
@ -24,6 +24,9 @@ from plane.db.models import (
|
|||||||
IssueSubscriber,
|
IssueSubscriber,
|
||||||
Notification,
|
Notification,
|
||||||
IssueAssignee,
|
IssueAssignee,
|
||||||
|
IssueReaction,
|
||||||
|
CommentReaction,
|
||||||
|
IssueComment,
|
||||||
)
|
)
|
||||||
from plane.api.serializers import IssueActivitySerializer
|
from plane.api.serializers import IssueActivitySerializer
|
||||||
|
|
||||||
@ -629,7 +632,7 @@ def update_issue_activity(
|
|||||||
"parent": track_parent,
|
"parent": track_parent,
|
||||||
"priority": track_priority,
|
"priority": track_priority,
|
||||||
"state": track_state,
|
"state": track_state,
|
||||||
"description": track_description,
|
"description_html": track_description,
|
||||||
"target_date": track_target_date,
|
"target_date": track_target_date,
|
||||||
"start_date": track_start_date,
|
"start_date": track_start_date,
|
||||||
"labels_list": track_labels,
|
"labels_list": track_labels,
|
||||||
@ -1022,6 +1025,150 @@ 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_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_reaction_id is not None and comment_id is not None:
|
||||||
|
issue_activities.append(
|
||||||
|
IssueActivity(
|
||||||
|
issue_id=comment.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_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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_id = IssueComment.objects.filter(pk=current_instance.get("comment_id"), project=project).values_list('issue_id', flat=True).first()
|
||||||
|
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(
|
||||||
|
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
|
# Receive message from room group
|
||||||
@shared_task
|
@shared_task
|
||||||
@ -1045,6 +1192,12 @@ def issue_activity(
|
|||||||
"cycle.activity.deleted",
|
"cycle.activity.deleted",
|
||||||
"module.activity.created",
|
"module.activity.created",
|
||||||
"module.activity.deleted",
|
"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()
|
issue = Issue.objects.filter(pk=issue_id).first()
|
||||||
|
|
||||||
@ -1080,6 +1233,12 @@ def issue_activity(
|
|||||||
"link.activity.deleted": delete_link_activity,
|
"link.activity.deleted": delete_link_activity,
|
||||||
"attachment.activity.created": create_attachment_activity,
|
"attachment.activity.created": create_attachment_activity,
|
||||||
"attachment.activity.deleted": delete_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)
|
func = ACTIVITY_MAPPER.get(type)
|
||||||
@ -1119,6 +1278,12 @@ def issue_activity(
|
|||||||
"cycle.activity.deleted",
|
"cycle.activity.deleted",
|
||||||
"module.activity.created",
|
"module.activity.created",
|
||||||
"module.activity.deleted",
|
"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
|
# Create Notifications
|
||||||
bulk_notifications = []
|
bulk_notifications = []
|
||||||
|
Loading…
Reference in New Issue
Block a user