chore: rebase
This commit is contained in:
@@ -235,6 +235,22 @@ class DiscussionNotificationSender:
|
||||
}
|
||||
return {}
|
||||
|
||||
def send_response_endorsed_on_thread_notification(self):
|
||||
"""
|
||||
Sends a notification to the author of the thread
|
||||
response on his thread has been endorsed
|
||||
"""
|
||||
context = {
|
||||
"username": self.creator.username,
|
||||
}
|
||||
self._send_notification([self.thread.user_id], "response_endorsed_on_thread", context)
|
||||
|
||||
def send_response_endorsed_notification(self):
|
||||
"""
|
||||
Sends a notification to the author of the response
|
||||
"""
|
||||
self._send_notification([self.creator.id], "response_endorsed")
|
||||
|
||||
def send_new_thread_created_notification(self):
|
||||
"""
|
||||
Send notification based on notification_type
|
||||
|
||||
@@ -47,3 +47,22 @@ def send_response_notifications(thread_id, course_key_str, user_id, parent_id=No
|
||||
notification_sender.send_new_response_notification()
|
||||
notification_sender.send_new_comment_on_response_notification()
|
||||
notification_sender.send_response_on_followed_post_notification()
|
||||
|
||||
|
||||
@shared_task
|
||||
@set_code_owner_attribute
|
||||
def send_response_endorsed_notifications(thread_id, course_key_str, comment_author_id):
|
||||
"""
|
||||
Send notifications when a response is marked answered/ endorsed
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_key_str)
|
||||
if not ENABLE_NOTIFICATIONS.is_enabled(course_key):
|
||||
return
|
||||
thread = Thread(id=thread_id).retrieve()
|
||||
comment_author = User.objects.get(id=comment_author_id)
|
||||
course = get_course_with_access(comment_author, 'load', course_key, check_if_enrolled=True)
|
||||
notification_sender = DiscussionNotificationSender(thread, course, comment_author)
|
||||
#sends notification to author of thread
|
||||
notification_sender.send_response_endorsed_on_thread_notification()
|
||||
#sends notification to author of response
|
||||
notification_sender.send_response_endorsed_notification()
|
||||
|
||||
@@ -13,7 +13,10 @@ from openedx_events.learning.signals import USER_NOTIFICATION_REQUESTED, COURSE_
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import StaffFactory, UserFactory
|
||||
from lms.djangoapps.discussion.django_comment_client.tests.factories import RoleFactory
|
||||
from lms.djangoapps.discussion.rest_api.tasks import send_response_notifications, send_thread_created_notification
|
||||
from lms.djangoapps.discussion.rest_api.tasks import (
|
||||
send_response_notifications,
|
||||
send_thread_created_notification,
|
||||
send_response_endorsed_notifications)
|
||||
from lms.djangoapps.discussion.rest_api.tests.utils import ThreadMock, make_minimal_cs_thread
|
||||
from openedx.core.djangoapps.course_groups.models import CohortMembership, CourseCohortsSettings
|
||||
from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory
|
||||
@@ -49,6 +52,7 @@ class TestNewThreadCreatedNotification(DiscussionAPIViewTestMixin, ModuleStoreTe
|
||||
"""
|
||||
Test cases related to new_discussion_post and new_question_post notification types
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Setup test case
|
||||
@@ -478,6 +482,7 @@ class TestSendCommentNotification(DiscussionAPIViewTestMixin, ModuleStoreTestCas
|
||||
"""
|
||||
Test case to send new_comment notification
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
httpretty.reset()
|
||||
@@ -527,3 +532,92 @@ class TestSendCommentNotification(DiscussionAPIViewTestMixin, ModuleStoreTestCas
|
||||
handler.assert_called_once()
|
||||
context = handler.call_args[1]['notification_data'].context
|
||||
self.assertEqual(context['author_name'], 'their')
|
||||
|
||||
|
||||
@override_waffle_flag(ENABLE_NOTIFICATIONS, active=True)
|
||||
class TestResponseEndorsedNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
"""
|
||||
Test case to send response endorsed notifications
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
httpretty.reset()
|
||||
httpretty.enable()
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
self.user_1 = UserFactory.create()
|
||||
CourseEnrollment.enroll(self.user_1, self.course.id)
|
||||
self.user_2 = UserFactory.create()
|
||||
CourseEnrollment.enroll(self.user_2, self.course.id)
|
||||
|
||||
def test_basic(self):
|
||||
"""
|
||||
Left empty intentionally. This test case is inherited from DiscussionAPIViewTestMixin
|
||||
"""
|
||||
|
||||
def test_not_authenticated(self):
|
||||
"""
|
||||
Left empty intentionally. This test case is inherited from DiscussionAPIViewTestMixin
|
||||
"""
|
||||
|
||||
def test_response_endorsed_notifications(self):
|
||||
"""
|
||||
Tests nresponse endorsed notifications
|
||||
"""
|
||||
|
||||
thread = ThreadMock(thread_id=1, creator=self.user_1, title='test thread')
|
||||
response = ThreadMock(thread_id=2, creator=self.user_2, title='test response')
|
||||
self.register_get_thread_response({
|
||||
'id': thread.id,
|
||||
'course_id': str(self.course.id),
|
||||
'topic_id': 'abc',
|
||||
"user_id": thread.user_id,
|
||||
"username": thread.username,
|
||||
"thread_type": 'discussion',
|
||||
"title": thread.title,
|
||||
})
|
||||
self.register_get_comment_response({
|
||||
'id': response.id,
|
||||
'thread_id': thread.id,
|
||||
'user_id': response.user_id
|
||||
})
|
||||
handler = mock.Mock()
|
||||
USER_NOTIFICATION_REQUESTED.connect(handler)
|
||||
send_response_endorsed_notifications(thread.id, str(self.course.id), self.user_2.id)
|
||||
self.assertEqual(handler.call_count, 2)
|
||||
|
||||
#Test response endorsed on thread notification
|
||||
notification_data = handler.call_args_list[0][1]['notification_data']
|
||||
# Target only the thread author
|
||||
self.assertEqual([int(user_id) for user_id in notification_data.user_ids], [int(thread.user_id)])
|
||||
self.assertEqual(notification_data.notification_type, 'response_endorsed_on_thread')
|
||||
|
||||
expected_context = {
|
||||
'replier_name': response.username,
|
||||
'post_title': 'test thread',
|
||||
'course_name': self.course.display_name,
|
||||
'sender_id': int(response.user_id),
|
||||
'username': response.username,
|
||||
}
|
||||
self.assertDictEqual(notification_data.context, expected_context)
|
||||
self.assertEqual(notification_data.content_url, _get_mfe_url(self.course.id, thread.id))
|
||||
self.assertEqual(notification_data.app_name, 'discussion')
|
||||
self.assertEqual('response_endorsed_on_thread', notification_data.notification_type)
|
||||
|
||||
#Test response endorsed notification
|
||||
notification_data = handler.call_args_list[1][1]['notification_data']
|
||||
# Target only the response author
|
||||
self.assertEqual([int(user_id) for user_id in notification_data.user_ids], [int(response.user_id)])
|
||||
self.assertEqual(notification_data.notification_type, 'response_endorsed')
|
||||
|
||||
expected_context = {
|
||||
'replier_name': response.username,
|
||||
'post_title': 'test thread',
|
||||
'course_name': self.course.display_name,
|
||||
'sender_id': int(response.user_id),
|
||||
}
|
||||
self.assertDictEqual(notification_data.context, expected_context)
|
||||
self.assertEqual(notification_data.content_url, _get_mfe_url(self.course.id, thread.id))
|
||||
self.assertEqual(notification_data.app_name, 'discussion')
|
||||
self.assertEqual('response_endorsed', notification_data.notification_type)
|
||||
|
||||
@@ -15,7 +15,11 @@ from lms.djangoapps.discussion.toggles import ENABLE_REPORTED_CONTENT_NOTIFICATI
|
||||
from xmodule.modulestore.django import SignalHandler, modulestore
|
||||
|
||||
from lms.djangoapps.discussion import tasks
|
||||
from lms.djangoapps.discussion.rest_api.tasks import send_response_notifications, send_thread_created_notification
|
||||
from lms.djangoapps.discussion.rest_api.tasks import (
|
||||
send_response_notifications,
|
||||
send_thread_created_notification,
|
||||
send_response_endorsed_notifications
|
||||
)
|
||||
from openedx.core.djangoapps.django_comment_common import signals
|
||||
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
|
||||
from openedx.core.djangoapps.theming.helpers import get_current_site
|
||||
@@ -178,3 +182,17 @@ def create_comment_created_notification(*args, **kwargs):
|
||||
parent_id = comment.attributes['parent_id']
|
||||
course_key_str = comment.attributes['course_id']
|
||||
send_response_notifications.apply_async(args=[thread_id, course_key_str, user.id, parent_id])
|
||||
|
||||
|
||||
@receiver(signals.comment_endorsed)
|
||||
def create_response_endorsed_on_thread_notification(*args, **kwargs):
|
||||
"""
|
||||
Creates a notification for thread author when response on thread is endorsed
|
||||
and another notification for response author when response is endorsed
|
||||
"""
|
||||
comment = kwargs['post']
|
||||
comment_author_id = comment.attributes['user_id']
|
||||
thread_id = comment.attributes['thread_id']
|
||||
course_key_str = comment.attributes['course_id']
|
||||
|
||||
send_response_endorsed_notifications.apply_async(args=[thread_id, course_key_str, comment_author_id])
|
||||
|
||||
@@ -320,8 +320,11 @@ class TeamSignalsTest(EventTestMixin, SharedModuleStoreTestCase):
|
||||
user = getattr(self, user)
|
||||
with patch('lms.djangoapps.discussion.rest_api.tasks.send_response_notifications.apply_async'):
|
||||
with patch('lms.djangoapps.discussion.rest_api.tasks.send_thread_created_notification.apply_async'):
|
||||
signal = self.SIGNALS[signal_name]
|
||||
signal.send(sender=None, user=user, post=self.mock_comment())
|
||||
with patch(
|
||||
'lms.djangoapps.discussion.rest_api.tasks.send_response_endorsed_notifications.apply_async'
|
||||
):
|
||||
signal = self.SIGNALS[signal_name]
|
||||
signal.send(sender=None, user=user, post=self.mock_comment())
|
||||
|
||||
@ddt.data('thread_voted', 'comment_voted')
|
||||
def test_vote_others_post(self, signal_name):
|
||||
@@ -339,5 +342,8 @@ class TeamSignalsTest(EventTestMixin, SharedModuleStoreTestCase):
|
||||
with self.assert_last_activity_updated(False):
|
||||
with patch('lms.djangoapps.discussion.rest_api.tasks.send_response_notifications.apply_async'):
|
||||
with patch('lms.djangoapps.discussion.rest_api.tasks.send_thread_created_notification.apply_async'):
|
||||
signal = self.SIGNALS[signal_name]
|
||||
signal.send(sender=None, user=self.user, post=self.mock_comment(context='course'))
|
||||
with patch(
|
||||
'lms.djangoapps.discussion.rest_api.tasks.send_response_endorsed_notifications.apply_async'
|
||||
):
|
||||
signal = self.SIGNALS[signal_name]
|
||||
signal.send(sender=None, user=self.user, post=self.mock_comment(context='course'))
|
||||
|
||||
@@ -132,6 +132,34 @@ COURSE_NOTIFICATION_TYPES = {
|
||||
},
|
||||
'email_template': '',
|
||||
},
|
||||
'response_endorsed_on_thread': {
|
||||
'notification_app': 'discussion',
|
||||
'name': 'response_endorsed_on_thread',
|
||||
'is_core': True,
|
||||
'info': '',
|
||||
'non_editable': [],
|
||||
'content_template': _('<{p}><{strong}>{username}</{strong}> response has been endorsed in your post '
|
||||
'<{strong}>{post_title}</{strong}></{p}>'),
|
||||
'content_context': {
|
||||
'post_title': 'Post title',
|
||||
'username': 'Response author name',
|
||||
},
|
||||
'email_template': '',
|
||||
'filters': [FILTER_AUDIT_EXPIRED_USERS_WITH_NO_ROLE]
|
||||
},
|
||||
'response_endorsed': {
|
||||
'notification_app': 'discussion',
|
||||
'name': 'response_endorsed',
|
||||
'is_core': True,
|
||||
'info': '',
|
||||
'non_editable': [],
|
||||
'content_template': _('<{p}><Your response has been endorsed <{strong}>{post_title}</{strong}></{p}>'),
|
||||
'content_context': {
|
||||
'post_title': 'Post title',
|
||||
},
|
||||
'email_template': '',
|
||||
'filters': [FILTER_AUDIT_EXPIRED_USERS_WITH_NO_ROLE]
|
||||
}
|
||||
}
|
||||
|
||||
COURSE_NOTIFICATION_APPS = {
|
||||
|
||||
@@ -21,7 +21,7 @@ log = logging.getLogger(__name__)
|
||||
NOTIFICATION_CHANNELS = ['web', 'push', 'email']
|
||||
|
||||
# Update this version when there is a change to any course specific notification type or app.
|
||||
COURSE_NOTIFICATION_CONFIG_VERSION = 5
|
||||
COURSE_NOTIFICATION_CONFIG_VERSION = 6
|
||||
|
||||
|
||||
def get_course_notification_preference_config():
|
||||
|
||||
@@ -236,7 +236,9 @@ class UserNotificationPreferenceAPITest(ModuleStoreTestCase):
|
||||
'new_comment',
|
||||
'new_response',
|
||||
'response_on_followed_post',
|
||||
'comment_on_followed_post'
|
||||
'comment_on_followed_post',
|
||||
'response_endorsed_on_thread',
|
||||
'response_endorsed'
|
||||
],
|
||||
'notification_types': {
|
||||
'core': {
|
||||
|
||||
Reference in New Issue
Block a user