Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: emit signal for thread, response, and comment created event #33395

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion lms/djangoapps/discussion/django_comment_client/base/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from eventtracking.processors.exceptions import EventEmissionExit
from mock import ANY, Mock, patch
from opaque_keys.edx.keys import CourseKey
from openedx_events.learning.signals import FORUM_THREAD_CREATED, FORUM_THREAD_RESPONSE_CREATED, FORUM_RESPONSE_COMMENT_CREATED

from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
Expand Down Expand Up @@ -406,7 +407,7 @@ def inner(self, default_store, block_count, mongo_calls, sql_queries, *args, **k
return inner

@ddt.data(
(ModuleStoreEnum.Type.split, 3, 8, 42),
(ModuleStoreEnum.Type.split, 3, 8, 43),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One additional for the user.profile.name

)
@ddt.unpack
@count_queries
Expand Down Expand Up @@ -1735,6 +1736,8 @@ def test_response_event(self, mock_request, mock_emit):
"""
Check to make sure an event is fired when a user responds to a thread.
"""
event_receiver = Mock()
FORUM_THREAD_RESPONSE_CREATED.connect(event_receiver)
self._set_mock_request_data(mock_request, {
"closed": False,
"commentable_id": 'test_commentable_id',
Expand All @@ -1754,12 +1757,29 @@ def test_response_event(self, mock_request, mock_emit):
assert event['discussion']['id'] == 'test_thread_id'
assert event['options']['followed'] is True

event_receiver.assert_called_once()

self.assertDictContainsSubset(
{
"signal": FORUM_THREAD_RESPONSE_CREATED,
"sender": None,
},
event_receiver.call_args.kwargs
)

self.assertIn(
"thread",
event_receiver.call_args.kwargs
)

@patch('eventtracking.tracker.emit')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_comment_event(self, mock_request, mock_emit):
"""
Ensure an event is fired when someone comments on a response.
"""
event_receiver = Mock()
FORUM_RESPONSE_COMMENT_CREATED.connect(event_receiver)
self._set_mock_request_data(mock_request, {
"closed": False,
"depth": 1,
Expand All @@ -1781,6 +1801,19 @@ def test_comment_event(self, mock_request, mock_emit):
assert event['user_course_roles'] == ['Wizard']
assert event['options']['followed'] is False

self.assertDictContainsSubset(
{
"signal": FORUM_RESPONSE_COMMENT_CREATED,
"sender": None,
},
event_receiver.call_args.kwargs
)

self.assertIn(
"thread",
event_receiver.call_args.kwargs
)

@patch('eventtracking.tracker.emit')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@ddt.data((
Expand Down Expand Up @@ -1809,6 +1842,10 @@ def test_team_events(self, view_name, event_name, view_data, view_kwargs, mock_r
team = CourseTeamFactory.create(discussion_topic_id=TEAM_COMMENTABLE_ID)
CourseTeamMembershipFactory.create(team=team, user=user)

event_receiver = Mock()
forum_event = views.TRACKING_LOG_TO_EVENT_MAPS.get(event_name)
forum_event.connect(event_receiver)

self._set_mock_request_data(mock_request, {
'closed': False,
'commentable_id': TEAM_COMMENTABLE_ID,
Expand All @@ -1825,6 +1862,19 @@ def test_team_events(self, view_name, event_name, view_data, view_kwargs, mock_r
assert name == event_name
assert event['team_id'] == team.team_id

self.assertDictContainsSubset(
{
"signal": forum_event,
"sender": None,
},
event_receiver.call_args.kwargs
)

self.assertIn(
"thread",
event_receiver.call_args.kwargs
)

@ddt.data(
('vote_for_thread', 'thread_id', 'thread'),
('undo_vote_for_thread', 'thread_id', 'thread'),
Expand Down Expand Up @@ -1863,6 +1913,10 @@ def test_thread_voted_event(self, view_name, obj_id_name, obj_type, mock_request
@patch('eventtracking.tracker.emit')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_thread_followed_event(self, view_name, mock_request, mock_emit):
event_receiver = Mock()
for signal in views.TRACKING_LOG_TO_EVENT_MAPS.values():
signal.connect(event_receiver)

self._set_mock_request_data(mock_request, {
'closed': False,
'commentable_id': 'test_commentable_id',
Expand All @@ -1887,6 +1941,11 @@ def test_thread_followed_event(self, view_name, mock_request, mock_emit):
assert event_data['user_forums_roles'] == ['Student']
assert event_data['user_course_roles'] == ['Wizard']

# In case of events that doesn't have a correspondig Open edX events signal
# we need to check that none of the openedx signals is called.
# This is tested for all the events that are not tested above.
event_receiver.assert_not_called()


class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRequestSetupMixin):

Expand Down
51 changes: 49 additions & 2 deletions lms/djangoapps/discussion/django_comment_client/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@
from django.views.decorators.http import require_GET, require_POST
from eventtracking import tracker
from opaque_keys.edx.keys import CourseKey
from openedx_events.learning.data import DiscussionThreadData, UserData, UserPersonalData
from openedx_events.learning.signals import (
FORUM_RESPONSE_COMMENT_CREATED,
FORUM_THREAD_CREATED,
FORUM_THREAD_RESPONSE_CREATED
)

import lms.djangoapps.discussion.django_comment_client.settings as cc_settings
import openedx.core.djangoapps.django_comment_common.comment_client as cc
from common.djangoapps.student.roles import GlobalStaff
from common.djangoapps.util.file import store_uploaded_file
from common.djangoapps.track import contexts
from common.djangoapps.util.file import store_uploaded_file
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.courses import get_course_overview_with_access, get_course_with_access
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect
Expand All @@ -42,7 +48,7 @@
get_user_group_ids,
is_comment_too_deep,
prepare_content,
sanitize_body,
sanitize_body
)
from openedx.core.djangoapps.django_comment_common.signals import (
comment_created,
Expand All @@ -66,6 +72,12 @@
TRACKING_MAX_FORUM_TITLE = 1000
_EVENT_NAME_TEMPLATE = 'edx.forum.{obj_type}.{action_name}'

TRACKING_LOG_TO_EVENT_MAPS = {
'edx.forum.thread.created': FORUM_THREAD_CREATED,
'edx.forum.response.created': FORUM_THREAD_RESPONSE_CREATED,
'edx.forum.comment.created': FORUM_RESPONSE_COMMENT_CREATED,
}


def track_forum_event(request, event_name, course, obj, data, id_map=None):
"""
Expand Down Expand Up @@ -97,6 +109,41 @@ def track_forum_event(request, event_name, course, obj, data, id_map=None):
with tracker.get_tracker().context(event_name, context):
tracker.emit(event_name, data)

forum_event = TRACKING_LOG_TO_EVENT_MAPS.get(event_name, None)
if forum_event is not None:
forum_event.send_event(
thread=DiscussionThreadData(
anonymous=data.get('anonymous'),
anonymous_to_peers=data.get('anonymous_to_peers'),
body=data.get('body'),
category_id=data.get('category_id'),
category_name=data.get('category_name'),
commentable_id=data.get('commentable_id'),
group_id=data.get('group_id'),
id=data.get('id'),
team_id=data.get('team_id'),
thread_type=data.get('thread_type'),
title=data.get('title'),
title_truncated=data.get('title_truncated'),
truncated=data.get('truncated'),
url=data.get('url'),
discussion=data.get('discussion'),
user_course_roles=data.get('user_course_roles'),
user_forums_roles=data.get('user_forums_roles'),
user=UserData(
pii=UserPersonalData(
username=user.username,
email=user.email,
name=user.profile.name,
),
id=user.id,
is_active=user.is_active,
),
course_id=str(course.id),
options=data.get('options'),
)
)


def track_created_event(request, event_name, course, obj, data):
"""
Expand Down
Loading