From 85a8b39735d261ec97f9ce2825bd580cb9a01226 Mon Sep 17 00:00:00 2001 From: SaadYousaf Date: Tue, 13 Sep 2022 15:17:17 +0500 Subject: [PATCH] feat: add event for content deletion for discussions --- .../django_comment_client/base/views.py | 36 ++++++++++++++++ lms/djangoapps/discussion/rest_api/api.py | 4 ++ .../discussion/rest_api/tests/test_api.py | 42 ++++++++++++++++++- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/discussion/django_comment_client/base/views.py b/lms/djangoapps/discussion/django_comment_client/base/views.py index f64a5d7312..f0c79f1fad 100644 --- a/lms/djangoapps/discussion/django_comment_client/base/views.py +++ b/lms/djangoapps/discussion/django_comment_client/base/views.py @@ -191,6 +191,42 @@ def track_thread_lock_unlock_event(request, course, thread, close_reason_code, l track_forum_event(request, event_name, course, thread, event_data) +def track_thread_deleted_event(request, course, thread): + """ + Send analytics event for a deleted thread. + """ + event_name = _EVENT_NAME_TEMPLATE.format(obj_type='thread', action_name='deleted') + own_content = str(request.user.id) == thread['user_id'] + event_data = { + 'body': thread.body[:TRACKING_MAX_FORUM_BODY], + 'content_type': 'Post', + 'own_content': own_content, + 'commentable_id': thread.get('commentable_id', ''), + } + if hasattr(thread, 'username'): + event_data['target_username'] = thread.get('username', '') + add_truncated_title_to_event_data(event_data, thread.get('title', '')) + track_forum_event(request, event_name, course, thread, event_data) + + +def track_comment_deleted_event(request, course, comment): + """ + Send analytics event for a deleted response or comment. + """ + obj_type = 'comment' if comment.get('parent_id') else 'response' + event_name = _EVENT_NAME_TEMPLATE.format(obj_type=obj_type, action_name='deleted') + own_content = str(request.user.id) == comment["user_id"] + event_data = { + 'body': comment.body[:TRACKING_MAX_FORUM_BODY], + 'commentable_id': comment.get('commentable_id', ''), + 'content_type': obj_type.capitalize(), + 'own_content': own_content, + } + if hasattr(comment, 'username'): + event_data['target_username'] = comment.get('username', '') + track_forum_event(request, event_name, course, comment, event_data) + + def permitted(func): """ View decorator to verify the user is authorized to access this endpoint. diff --git a/lms/djangoapps/discussion/rest_api/api.py b/lms/djangoapps/discussion/rest_api/api.py index f1cb7c98e3..6e638eaac9 100644 --- a/lms/djangoapps/discussion/rest_api/api.py +++ b/lms/djangoapps/discussion/rest_api/api.py @@ -73,7 +73,9 @@ from ..config.waffle import ENABLE_LEARNERS_STATS from ..django_comment_client.base.views import ( track_comment_created_event, + track_comment_deleted_event, track_thread_created_event, + track_thread_deleted_event, track_thread_viewed_event, track_voted_event, ) @@ -1604,6 +1606,7 @@ def delete_thread(request, thread_id): if can_delete(cc_thread, context): cc_thread.delete() thread_deleted.send(sender=None, user=request.user, post=cc_thread) + track_thread_deleted_event(request, context["course"], cc_thread) else: raise PermissionDenied @@ -1628,6 +1631,7 @@ def delete_comment(request, comment_id): if can_delete(cc_comment, context): cc_comment.delete() comment_deleted.send(sender=None, user=request.user, post=cc_comment) + track_comment_deleted_event(request, context["course"], cc_comment) else: raise PermissionDenied diff --git a/lms/djangoapps/discussion/rest_api/tests/test_api.py b/lms/djangoapps/discussion/rest_api/tests/test_api.py index 0ef986151f..72c4a3f922 100644 --- a/lms/djangoapps/discussion/rest_api/tests/test_api.py +++ b/lms/djangoapps/discussion/rest_api/tests/test_api.py @@ -3437,13 +3437,33 @@ class DeleteThreadTest( self.register_get_thread_response(cs_data) self.register_delete_thread_response(cs_data["id"]) - def test_basic(self): + @mock.patch("eventtracking.tracker.emit") + def test_basic(self, mock_emit): self.register_thread() with self.assert_signal_sent(api, 'thread_deleted', sender=None, user=self.user, exclude_args=('post',)): assert delete_thread(self.request, self.thread_id) is None assert urlparse(httpretty.last_request().path).path == f"/api/v1/threads/{self.thread_id}" # lint-amnesty, pylint: disable=no-member assert httpretty.last_request().method == 'DELETE' + expected_event_name = 'edx.forum.thread.deleted' + expected_event_data = { + 'body': 'dummy', + 'content_type': 'Post', + 'own_content': True, + 'commentable_id': 'dummy', + 'target_username': 'dummy', + 'title_truncated': False, + 'title': 'dummy', + 'id': 'test_thread', + 'url': '', + 'user_forums_roles': ['Student'], + 'user_course_roles': [] + } + + actual_event_name, actual_event_data = mock_emit.call_args[0] + self.assertEqual(actual_event_name, expected_event_name) + self.assertEqual(actual_event_data, expected_event_data) + def test_thread_id_not_found(self): self.register_get_thread_error_response("missing_thread", 404) with pytest.raises(ThreadNotFoundError): @@ -3579,13 +3599,31 @@ class DeleteCommentTest( self.register_get_comment_response(cs_comment_data) self.register_delete_comment_response(self.comment_id) - def test_basic(self): + @mock.patch("eventtracking.tracker.emit") + def test_basic(self, mock_emit): self.register_comment_and_thread() with self.assert_signal_sent(api, 'comment_deleted', sender=None, user=self.user, exclude_args=('post',)): assert delete_comment(self.request, self.comment_id) is None assert urlparse(httpretty.last_request().path).path == f"/api/v1/comments/{self.comment_id}" # lint-amnesty, pylint: disable=no-member assert httpretty.last_request().method == 'DELETE' + expected_event_name = 'edx.forum.response.deleted' + expected_event_data = { + 'body': 'dummy', + 'content_type': 'Response', + 'own_content': True, + 'commentable_id': 'dummy', + 'target_username': self.user.username, + 'id': 'test_comment', + 'url': '', + 'user_forums_roles': ['Student'], + 'user_course_roles': [] + } + + actual_event_name, actual_event_data = mock_emit.call_args[0] + self.assertEqual(actual_event_name, expected_event_name) + self.assertEqual(actual_event_data, expected_event_data) + def test_comment_id_not_found(self): self.register_get_comment_error_response("missing_comment", 404) with pytest.raises(CommentNotFoundError):