feat: reapply forum v2 changes (#36002)
* feat: Reapply "Integrate Forum V2 into edx-platform"
This reverts commit 818aa343a2.
* feat: make it possible to globally disable forum v2 with setting
We introduce a setting that allows us to bypass any course waffle flag
check. The advantage of such a setting is that we don't need to find the
course ID: in some cases, we might not have access to the course ID, and
we need to look for it... in forum v2.
See discussion here: https://github.com/openedx/forum/issues/137
* chore: bump openedx-forum to 0.1.5
This should fix an issue with index creation on edX.org.
This commit is contained in:
@@ -82,6 +82,7 @@ class MockRequestSetupMixin:
|
||||
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
class CreateThreadGroupIdTestCase(
|
||||
MockRequestSetupMixin,
|
||||
CohortedTestCase,
|
||||
@@ -90,7 +91,21 @@ class CreateThreadGroupIdTestCase(
|
||||
):
|
||||
cs_endpoint = "/threads"
|
||||
|
||||
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def call_view(self, mock_is_forum_v2_enabled, mock_request, commentable_id, user, group_id, pass_group_id=True):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {})
|
||||
request_data = {"body": "body", "title": "title", "thread_type": "discussion"}
|
||||
if pass_group_id:
|
||||
@@ -105,8 +120,9 @@ class CreateThreadGroupIdTestCase(
|
||||
commentable_id=commentable_id
|
||||
)
|
||||
|
||||
def test_group_info_in_response(self, mock_request):
|
||||
def test_group_info_in_response(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.student,
|
||||
@@ -116,6 +132,7 @@ class CreateThreadGroupIdTestCase(
|
||||
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
@disable_signal(views, 'thread_edited')
|
||||
@disable_signal(views, 'thread_voted')
|
||||
@disable_signal(views, 'thread_deleted')
|
||||
@@ -127,11 +144,18 @@ class ThreadActionGroupIdTestCase(
|
||||
def call_view(
|
||||
self,
|
||||
view_name,
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
user=None,
|
||||
post_params=None,
|
||||
view_args=None
|
||||
):
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(
|
||||
mock_request,
|
||||
{
|
||||
@@ -154,53 +178,58 @@ class ThreadActionGroupIdTestCase(
|
||||
**(view_args or {})
|
||||
)
|
||||
|
||||
def test_update(self, mock_request):
|
||||
def test_update(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
"update_thread",
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
post_params={"body": "body", "title": "title"}
|
||||
)
|
||||
self._assert_json_response_contains_group_info(response)
|
||||
|
||||
def test_delete(self, mock_request):
|
||||
response = self.call_view("delete_thread", mock_request)
|
||||
def test_delete(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view("delete_thread", mock_is_forum_v2_enabled, mock_request)
|
||||
self._assert_json_response_contains_group_info(response)
|
||||
|
||||
def test_vote(self, mock_request):
|
||||
def test_vote(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
"vote_for_thread",
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
view_args={"value": "up"}
|
||||
)
|
||||
self._assert_json_response_contains_group_info(response)
|
||||
response = self.call_view("undo_vote_for_thread", mock_request)
|
||||
response = self.call_view("undo_vote_for_thread", mock_is_forum_v2_enabled, mock_request)
|
||||
self._assert_json_response_contains_group_info(response)
|
||||
|
||||
def test_flag(self, mock_request):
|
||||
def test_flag(self, mock_is_forum_v2_enabled, mock_request):
|
||||
with mock.patch('openedx.core.djangoapps.django_comment_common.signals.thread_flagged.send') as signal_mock:
|
||||
response = self.call_view("flag_abuse_for_thread", mock_request)
|
||||
response = self.call_view("flag_abuse_for_thread", mock_is_forum_v2_enabled, mock_request)
|
||||
self._assert_json_response_contains_group_info(response)
|
||||
self.assertEqual(signal_mock.call_count, 1)
|
||||
response = self.call_view("un_flag_abuse_for_thread", mock_request)
|
||||
response = self.call_view("un_flag_abuse_for_thread", mock_is_forum_v2_enabled, mock_request)
|
||||
self._assert_json_response_contains_group_info(response)
|
||||
|
||||
def test_pin(self, mock_request):
|
||||
def test_pin(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
"pin_thread",
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
user=self.moderator
|
||||
)
|
||||
self._assert_json_response_contains_group_info(response)
|
||||
response = self.call_view(
|
||||
"un_pin_thread",
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
user=self.moderator
|
||||
)
|
||||
self._assert_json_response_contains_group_info(response)
|
||||
|
||||
def test_openclose(self, mock_request):
|
||||
def test_openclose(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
"openclose_thread",
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
user=self.moderator
|
||||
)
|
||||
@@ -280,10 +309,11 @@ class ViewsTestCaseMixin:
|
||||
data["depth"] = 0
|
||||
self._set_mock_request_data(mock_request, data)
|
||||
|
||||
def create_thread_helper(self, mock_request, extra_request_data=None, extra_response_data=None):
|
||||
def create_thread_helper(self, mock_is_forum_v2_enabled, mock_request, extra_request_data=None, extra_response_data=None):
|
||||
"""
|
||||
Issues a request to create a thread and verifies the result.
|
||||
"""
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"thread_type": "discussion",
|
||||
"title": "Hello",
|
||||
@@ -350,10 +380,11 @@ class ViewsTestCaseMixin:
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def update_thread_helper(self, mock_request):
|
||||
def update_thread_helper(self, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Issues a request to update a thread and verifies the result.
|
||||
"""
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._setup_mock_request(mock_request)
|
||||
# Mock out saving in order to test that content is correctly
|
||||
# updated. Otherwise, the call to thread.save() receives the
|
||||
@@ -376,6 +407,7 @@ class ViewsTestCaseMixin:
|
||||
|
||||
@ddt.ddt
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
@disable_signal(views, 'thread_created')
|
||||
@disable_signal(views, 'thread_edited')
|
||||
class ViewsQueryCountTestCase(
|
||||
@@ -393,6 +425,11 @@ class ViewsQueryCountTestCase(
|
||||
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def count_queries(func): # pylint: disable=no-self-argument
|
||||
"""
|
||||
@@ -414,22 +451,23 @@ class ViewsQueryCountTestCase(
|
||||
)
|
||||
@ddt.unpack
|
||||
@count_queries
|
||||
def test_create_thread(self, mock_request):
|
||||
self.create_thread_helper(mock_request)
|
||||
def test_create_thread(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.create_thread_helper(mock_is_forum_v2_enabled, mock_request)
|
||||
|
||||
@ddt.data(
|
||||
(ModuleStoreEnum.Type.split, 3, 6, 41),
|
||||
)
|
||||
@ddt.unpack
|
||||
@count_queries
|
||||
def test_update_thread(self, mock_request):
|
||||
self.update_thread_helper(mock_request)
|
||||
def test_update_thread(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.update_thread_helper(mock_is_forum_v2_enabled, mock_request)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@disable_signal(views, 'comment_flagged')
|
||||
@disable_signal(views, 'thread_flagged')
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
class ViewsTestCase(
|
||||
ForumsEnableMixin,
|
||||
UrlResetMixin,
|
||||
@@ -464,7 +502,16 @@ class ViewsTestCase(
|
||||
# so we need to call super.setUp() which reloads urls.py (because
|
||||
# of the UrlResetMixin)
|
||||
super().setUp()
|
||||
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
# Patch the comment client user save method so it does not try
|
||||
# to create a new cc user when creating a django user
|
||||
with patch('common.djangoapps.student.models.user.cc.User.save'):
|
||||
@@ -497,11 +544,11 @@ class ViewsTestCase(
|
||||
with self.assert_signal_sent(views, signal, sender=None, user=user, exclude_args=('post',)):
|
||||
yield
|
||||
|
||||
def test_create_thread(self, mock_request):
|
||||
def test_create_thread(self, mock_is_forum_v2_enabled, mock_request):
|
||||
with self.assert_discussion_signals('thread_created'):
|
||||
self.create_thread_helper(mock_request)
|
||||
self.create_thread_helper(mock_is_forum_v2_enabled, mock_request)
|
||||
|
||||
def test_create_thread_standalone(self, mock_request):
|
||||
def test_create_thread_standalone(self, mock_is_forum_v2_enabled, mock_request):
|
||||
team = CourseTeamFactory.create(
|
||||
name="A Team",
|
||||
course_id=self.course_id,
|
||||
@@ -513,15 +560,15 @@ class ViewsTestCase(
|
||||
team.add_user(self.student)
|
||||
|
||||
# create_thread_helper verifies that extra data are passed through to the comments service
|
||||
self.create_thread_helper(mock_request, extra_response_data={'context': ThreadContext.STANDALONE})
|
||||
self.create_thread_helper(mock_is_forum_v2_enabled, mock_request, extra_response_data={'context': ThreadContext.STANDALONE})
|
||||
|
||||
@ddt.data(
|
||||
('follow_thread', 'thread_followed'),
|
||||
('unfollow_thread', 'thread_unfollowed'),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_follow_unfollow_thread_signals(self, view_name, signal, mock_request):
|
||||
self.create_thread_helper(mock_request)
|
||||
def test_follow_unfollow_thread_signals(self, view_name, signal, mock_is_forum_v2_enabled, mock_request):
|
||||
self.create_thread_helper(mock_is_forum_v2_enabled, mock_request)
|
||||
|
||||
with self.assert_discussion_signals(signal):
|
||||
response = self.client.post(
|
||||
@@ -532,7 +579,8 @@ class ViewsTestCase(
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_delete_thread(self, mock_request):
|
||||
def test_delete_thread(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"user_id": str(self.student.id),
|
||||
"closed": False,
|
||||
@@ -551,7 +599,8 @@ class ViewsTestCase(
|
||||
assert response.status_code == 200
|
||||
assert mock_request.called
|
||||
|
||||
def test_delete_comment(self, mock_request):
|
||||
def test_delete_comment(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"user_id": str(self.student.id),
|
||||
"closed": False,
|
||||
@@ -573,12 +622,13 @@ class ViewsTestCase(
|
||||
assert args[0] == 'delete'
|
||||
assert args[1].endswith(f"/{test_comment_id}")
|
||||
|
||||
def _test_request_error(self, view_name, view_kwargs, data, mock_request):
|
||||
def _test_request_error(self, view_name, view_kwargs, data, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Submit a request against the given view with the given data and ensure
|
||||
that the result is a 400 error and that no data was posted using
|
||||
mock_request
|
||||
"""
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._setup_mock_request(mock_request, include_depth=(view_name == "create_sub_comment"))
|
||||
|
||||
response = self.client.post(reverse(view_name, kwargs=view_kwargs), data=data)
|
||||
@@ -586,87 +636,97 @@ class ViewsTestCase(
|
||||
for call in mock_request.call_args_list:
|
||||
assert call[0][0].lower() == 'get'
|
||||
|
||||
def test_create_thread_no_title(self, mock_request):
|
||||
def test_create_thread_no_title(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"create_thread",
|
||||
{"commentable_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": "foo"},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_create_thread_empty_title(self, mock_request):
|
||||
def test_create_thread_empty_title(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"create_thread",
|
||||
{"commentable_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": "foo", "title": " "},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_create_thread_no_body(self, mock_request):
|
||||
def test_create_thread_no_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"create_thread",
|
||||
{"commentable_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"title": "foo"},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_create_thread_empty_body(self, mock_request):
|
||||
def test_create_thread_empty_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"create_thread",
|
||||
{"commentable_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": " ", "title": "foo"},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_update_thread_no_title(self, mock_request):
|
||||
def test_update_thread_no_title(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"update_thread",
|
||||
{"thread_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": "foo"},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_update_thread_empty_title(self, mock_request):
|
||||
def test_update_thread_empty_title(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"update_thread",
|
||||
{"thread_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": "foo", "title": " "},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_update_thread_no_body(self, mock_request):
|
||||
def test_update_thread_no_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"update_thread",
|
||||
{"thread_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"title": "foo"},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_update_thread_empty_body(self, mock_request):
|
||||
def test_update_thread_empty_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"update_thread",
|
||||
{"thread_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": " ", "title": "foo"},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_update_thread_course_topic(self, mock_request):
|
||||
def test_update_thread_course_topic(self, mock_is_forum_v2_enabled, mock_request):
|
||||
with self.assert_discussion_signals('thread_edited'):
|
||||
self.update_thread_helper(mock_request)
|
||||
self.update_thread_helper(mock_is_forum_v2_enabled, mock_request)
|
||||
|
||||
@patch(
|
||||
'lms.djangoapps.discussion.django_comment_client.utils.get_discussion_categories_ids',
|
||||
return_value=["test_commentable"],
|
||||
)
|
||||
def test_update_thread_wrong_commentable_id(self, mock_get_discussion_id_map, mock_request):
|
||||
def test_update_thread_wrong_commentable_id(self, mock_get_discussion_id_map, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"update_thread",
|
||||
{"thread_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": "foo", "title": "foo", "commentable_id": "wrong_commentable"},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_create_comment(self, mock_request):
|
||||
def test_create_comment(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._setup_mock_request(mock_request)
|
||||
with self.assert_discussion_signals('comment_created'):
|
||||
response = self.client.post(
|
||||
@@ -678,55 +738,62 @@ class ViewsTestCase(
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_create_comment_no_body(self, mock_request):
|
||||
def test_create_comment_no_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"create_comment",
|
||||
{"thread_id": "dummy", "course_id": str(self.course_id)},
|
||||
{},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_create_comment_empty_body(self, mock_request):
|
||||
def test_create_comment_empty_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"create_comment",
|
||||
{"thread_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": " "},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_create_sub_comment_no_body(self, mock_request):
|
||||
def test_create_sub_comment_no_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"create_sub_comment",
|
||||
{"comment_id": "dummy", "course_id": str(self.course_id)},
|
||||
{},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_create_sub_comment_empty_body(self, mock_request):
|
||||
def test_create_sub_comment_empty_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"create_sub_comment",
|
||||
{"comment_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": " "},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_update_comment_no_body(self, mock_request):
|
||||
def test_update_comment_no_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"update_comment",
|
||||
{"comment_id": "dummy", "course_id": str(self.course_id)},
|
||||
{},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_update_comment_empty_body(self, mock_request):
|
||||
def test_update_comment_empty_body(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._test_request_error(
|
||||
"update_comment",
|
||||
{"comment_id": "dummy", "course_id": str(self.course_id)},
|
||||
{"body": " "},
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request
|
||||
)
|
||||
|
||||
def test_update_comment_basic(self, mock_request):
|
||||
def test_update_comment_basic(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._setup_mock_request(mock_request)
|
||||
comment_id = "test_comment_id"
|
||||
updated_body = "updated body"
|
||||
@@ -748,13 +815,14 @@ class ViewsTestCase(
|
||||
data={"body": updated_body}
|
||||
)
|
||||
|
||||
def test_flag_thread_open(self, mock_request):
|
||||
self.flag_thread(mock_request, False)
|
||||
def test_flag_thread_open(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.flag_thread(mock_is_forum_v2_enabled, mock_request, False)
|
||||
|
||||
def test_flag_thread_close(self, mock_request):
|
||||
self.flag_thread(mock_request, True)
|
||||
def test_flag_thread_close(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.flag_thread(mock_is_forum_v2_enabled, mock_request, True)
|
||||
|
||||
def flag_thread(self, mock_request, is_closed):
|
||||
def flag_thread(self, mock_is_forum_v2_enabled, mock_request, is_closed):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"title": "Hello",
|
||||
"body": "this is a post",
|
||||
@@ -826,13 +894,14 @@ class ViewsTestCase(
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_un_flag_thread_open(self, mock_request):
|
||||
self.un_flag_thread(mock_request, False)
|
||||
def test_un_flag_thread_open(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.un_flag_thread(mock_is_forum_v2_enabled, mock_request, False)
|
||||
|
||||
def test_un_flag_thread_close(self, mock_request):
|
||||
self.un_flag_thread(mock_request, True)
|
||||
def test_un_flag_thread_close(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.un_flag_thread(mock_is_forum_v2_enabled, mock_request, True)
|
||||
|
||||
def un_flag_thread(self, mock_request, is_closed):
|
||||
def un_flag_thread(self, mock_is_forum_v2_enabled, mock_request, is_closed):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"title": "Hello",
|
||||
"body": "this is a post",
|
||||
@@ -905,13 +974,14 @@ class ViewsTestCase(
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_flag_comment_open(self, mock_request):
|
||||
self.flag_comment(mock_request, False)
|
||||
def test_flag_comment_open(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.flag_comment(mock_is_forum_v2_enabled, mock_request, False)
|
||||
|
||||
def test_flag_comment_close(self, mock_request):
|
||||
self.flag_comment(mock_request, True)
|
||||
def test_flag_comment_close(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.flag_comment(mock_is_forum_v2_enabled, mock_request, True)
|
||||
|
||||
def flag_comment(self, mock_request, is_closed):
|
||||
def flag_comment(self, mock_is_forum_v2_enabled, mock_request, is_closed):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"body": "this is a comment",
|
||||
"course_id": "MITx/999/Robot_Super_Course",
|
||||
@@ -976,13 +1046,14 @@ class ViewsTestCase(
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_un_flag_comment_open(self, mock_request):
|
||||
self.un_flag_comment(mock_request, False)
|
||||
def test_un_flag_comment_open(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.un_flag_comment(mock_is_forum_v2_enabled, mock_request, False)
|
||||
|
||||
def test_un_flag_comment_close(self, mock_request):
|
||||
self.un_flag_comment(mock_request, True)
|
||||
def test_un_flag_comment_close(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.un_flag_comment(mock_is_forum_v2_enabled, mock_request, True)
|
||||
|
||||
def un_flag_comment(self, mock_request, is_closed):
|
||||
def un_flag_comment(self, mock_is_forum_v2_enabled, mock_request, is_closed):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"body": "this is a comment",
|
||||
"course_id": "MITx/999/Robot_Super_Course",
|
||||
@@ -1054,7 +1125,8 @@ class ViewsTestCase(
|
||||
('downvote_comment', 'comment_id', 'comment_voted')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_voting(self, view_name, item_id, signal, mock_request):
|
||||
def test_voting(self, view_name, item_id, signal, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._setup_mock_request(mock_request)
|
||||
with self.assert_discussion_signals(signal):
|
||||
response = self.client.post(
|
||||
@@ -1065,7 +1137,8 @@ class ViewsTestCase(
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_endorse_comment(self, mock_request):
|
||||
def test_endorse_comment(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._setup_mock_request(mock_request)
|
||||
self.client.login(username=self.moderator.username, password=self.password)
|
||||
with self.assert_discussion_signals('comment_endorsed', user=self.moderator):
|
||||
@@ -1079,6 +1152,7 @@ class ViewsTestCase(
|
||||
|
||||
|
||||
@patch("openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request", autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
@disable_signal(views, 'comment_endorsed')
|
||||
class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCase, MockRequestSetupMixin):
|
||||
|
||||
@@ -1106,8 +1180,19 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
|
||||
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_pin_thread_as_student(self, mock_request):
|
||||
def test_pin_thread_as_student(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {})
|
||||
self.client.login(username=self.student.username, password=self.password)
|
||||
response = self.client.post(
|
||||
@@ -1115,7 +1200,8 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_pin_thread_as_moderator(self, mock_request):
|
||||
def test_pin_thread_as_moderator(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {})
|
||||
self.client.login(username=self.moderator.username, password=self.password)
|
||||
response = self.client.post(
|
||||
@@ -1123,7 +1209,8 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_un_pin_thread_as_student(self, mock_request):
|
||||
def test_un_pin_thread_as_student(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {})
|
||||
self.client.login(username=self.student.username, password=self.password)
|
||||
response = self.client.post(
|
||||
@@ -1131,7 +1218,8 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_un_pin_thread_as_moderator(self, mock_request):
|
||||
def test_un_pin_thread_as_moderator(self, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {})
|
||||
self.client.login(username=self.moderator.username, password=self.password)
|
||||
response = self.client.post(
|
||||
@@ -1139,7 +1227,7 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def _set_mock_request_thread_and_comment(self, mock_request, thread_data, comment_data):
|
||||
def _set_mock_request_thread_and_comment(self, mock_is_forum_v2_enabled, mock_request, thread_data, comment_data):
|
||||
def handle_request(*args, **kwargs):
|
||||
url = args[1]
|
||||
if "/threads/" in url:
|
||||
@@ -1148,10 +1236,12 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
|
||||
return self._create_response_mock(comment_data)
|
||||
else:
|
||||
raise ArgumentError("Bad url to mock request")
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
mock_request.side_effect = handle_request
|
||||
|
||||
def test_endorse_response_as_staff(self, mock_request):
|
||||
def test_endorse_response_as_staff(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._set_mock_request_thread_and_comment(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
{"type": "thread", "thread_type": "question", "user_id": str(self.student.id), "commentable_id": "course"},
|
||||
{"type": "comment", "thread_id": "dummy"}
|
||||
@@ -1162,8 +1252,9 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_endorse_response_as_student(self, mock_request):
|
||||
def test_endorse_response_as_student(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._set_mock_request_thread_and_comment(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
{"type": "thread", "thread_type": "question",
|
||||
"user_id": str(self.moderator.id), "commentable_id": "course"},
|
||||
@@ -1175,8 +1266,9 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_endorse_response_as_student_question_author(self, mock_request):
|
||||
def test_endorse_response_as_student_question_author(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self._set_mock_request_thread_and_comment(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
{"type": "thread", "thread_type": "question", "user_id": str(self.student.id), "commentable_id": "course"},
|
||||
{"type": "comment", "thread_id": "dummy"}
|
||||
@@ -1209,10 +1301,12 @@ class CreateThreadUnicodeTestCase(
|
||||
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_request,):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request,):
|
||||
"""
|
||||
Test to make sure unicode data in a thread doesn't break it.
|
||||
"""
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {})
|
||||
request = RequestFactory().post("dummy_url", {"thread_type": "discussion", "body": text, "title": text})
|
||||
request.user = self.student
|
||||
@@ -1235,6 +1329,13 @@ class UpdateThreadUnicodeTestCase(
|
||||
UnicodeTestMixin,
|
||||
MockRequestSetupMixin
|
||||
):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -1255,7 +1356,9 @@ class UpdateThreadUnicodeTestCase(
|
||||
return_value=["test_commentable"],
|
||||
)
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_request, mock_get_discussion_id_map):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request, mock_get_discussion_id_map):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"user_id": str(self.student.id),
|
||||
"closed": False,
|
||||
@@ -1280,6 +1383,13 @@ class CreateCommentUnicodeTestCase(
|
||||
UnicodeTestMixin,
|
||||
MockRequestSetupMixin
|
||||
):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -1296,7 +1406,9 @@ class CreateCommentUnicodeTestCase(
|
||||
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_request):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
commentable_id = "non_team_dummy_id"
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"closed": False,
|
||||
@@ -1327,6 +1439,13 @@ class UpdateCommentUnicodeTestCase(
|
||||
UnicodeTestMixin,
|
||||
MockRequestSetupMixin
|
||||
):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -1343,7 +1462,9 @@ class UpdateCommentUnicodeTestCase(
|
||||
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_request):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"user_id": str(self.student.id),
|
||||
"closed": False,
|
||||
@@ -1359,6 +1480,7 @@ class UpdateCommentUnicodeTestCase(
|
||||
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
class CommentActionTestCase(
|
||||
MockRequestSetupMixin,
|
||||
CohortedTestCase,
|
||||
@@ -1367,11 +1489,18 @@ class CommentActionTestCase(
|
||||
def call_view(
|
||||
self,
|
||||
view_name,
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
user=None,
|
||||
post_params=None,
|
||||
view_args=None
|
||||
):
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self._set_mock_request_data(
|
||||
mock_request,
|
||||
{
|
||||
@@ -1394,9 +1523,9 @@ class CommentActionTestCase(
|
||||
**(view_args or {})
|
||||
)
|
||||
|
||||
def test_flag(self, mock_request):
|
||||
def test_flag(self, mock_is_forum_v2_enabled, mock_request):
|
||||
with mock.patch('openedx.core.djangoapps.django_comment_common.signals.comment_flagged.send') as signal_mock:
|
||||
self.call_view("flag_abuse_for_comment", mock_request)
|
||||
self.call_view("flag_abuse_for_comment", mock_is_forum_v2_enabled, mock_request)
|
||||
self.assertEqual(signal_mock.call_count, 1)
|
||||
|
||||
|
||||
@@ -1410,6 +1539,14 @@ class CreateSubCommentUnicodeTestCase(
|
||||
"""
|
||||
Make sure comments under a response can handle unicode.
|
||||
"""
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# pylint: disable=super-method-not-called
|
||||
@@ -1425,10 +1562,12 @@ class CreateSubCommentUnicodeTestCase(
|
||||
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_request):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Create a comment with unicode in it.
|
||||
"""
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"closed": False,
|
||||
"depth": 1,
|
||||
@@ -1453,6 +1592,7 @@ class CreateSubCommentUnicodeTestCase(
|
||||
|
||||
@ddt.ddt
|
||||
@patch("openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request", autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
@disable_signal(views, 'thread_voted')
|
||||
@disable_signal(views, 'thread_edited')
|
||||
@disable_signal(views, 'comment_created')
|
||||
@@ -1562,13 +1702,24 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
|
||||
users=[cls.group_moderator, cls.cohorted]
|
||||
)
|
||||
|
||||
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def _setup_mock(self, user, mock_request, data):
|
||||
def _setup_mock(self, user, mock_is_forum_v2_enabled, mock_request, data):
|
||||
user = getattr(self, user)
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, data)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.client.login(username=user.username, password=self.password)
|
||||
|
||||
@ddt.data(
|
||||
@@ -1593,7 +1744,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
|
||||
('group_moderator', 'cohorted', 'course_commentable_id', 401, CourseDiscussionSettings.NONE)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_update_thread(self, user, thread_author, commentable_id, status_code, division_scheme, mock_request):
|
||||
def test_update_thread(self, user, thread_author, commentable_id, status_code, division_scheme, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Verify that update_thread is limited to thread authors and privileged users (team membership does not matter).
|
||||
"""
|
||||
@@ -1603,7 +1754,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
|
||||
thread_author = getattr(self, thread_author)
|
||||
|
||||
self._setup_mock(
|
||||
user, mock_request, # user is the person making the request.
|
||||
user, mock_is_forum_v2_enabled, mock_request, # user is the person making the request.
|
||||
{
|
||||
"user_id": str(thread_author.id),
|
||||
"closed": False, "commentable_id": commentable_id,
|
||||
@@ -1643,12 +1794,12 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
|
||||
('group_moderator', 'cohorted', 'team_commentable_id', 401, CourseDiscussionSettings.NONE)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_delete_comment(self, user, comment_author, commentable_id, status_code, division_scheme, mock_request):
|
||||
def test_delete_comment(self, user, comment_author, commentable_id, status_code, division_scheme, mock_is_forum_v2_enabled, mock_request):
|
||||
commentable_id = getattr(self, commentable_id)
|
||||
comment_author = getattr(self, comment_author)
|
||||
self.change_divided_discussion_settings(division_scheme)
|
||||
|
||||
self._setup_mock(user, mock_request, {
|
||||
self._setup_mock(user, mock_is_forum_v2_enabled, mock_request, {
|
||||
"closed": False,
|
||||
"commentable_id": commentable_id,
|
||||
"user_id": str(comment_author.id),
|
||||
@@ -1671,12 +1822,12 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
|
||||
|
||||
@ddt.data(*ddt_permissions_args)
|
||||
@ddt.unpack
|
||||
def test_create_comment(self, user, commentable_id, status_code, mock_request):
|
||||
def test_create_comment(self, user, commentable_id, status_code, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Verify that create_comment is limited to members of the team or users with 'edit_content' permission.
|
||||
"""
|
||||
commentable_id = getattr(self, commentable_id)
|
||||
self._setup_mock(user, mock_request, {"closed": False, "commentable_id": commentable_id})
|
||||
self._setup_mock(user, mock_is_forum_v2_enabled, mock_request, {"closed": False, "commentable_id": commentable_id})
|
||||
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
@@ -1692,13 +1843,13 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
|
||||
|
||||
@ddt.data(*ddt_permissions_args)
|
||||
@ddt.unpack
|
||||
def test_create_sub_comment(self, user, commentable_id, status_code, mock_request):
|
||||
def test_create_sub_comment(self, user, commentable_id, status_code, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Verify that create_subcomment is limited to members of the team or users with 'edit_content' permission.
|
||||
"""
|
||||
commentable_id = getattr(self, commentable_id)
|
||||
self._setup_mock(
|
||||
user, mock_request,
|
||||
user, mock_is_forum_v2_enabled, mock_request,
|
||||
{"closed": False, "commentable_id": commentable_id, "thread_id": "dummy_thread"},
|
||||
)
|
||||
response = self.client.post(
|
||||
@@ -1715,14 +1866,14 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
|
||||
|
||||
@ddt.data(*ddt_permissions_args)
|
||||
@ddt.unpack
|
||||
def test_comment_actions(self, user, commentable_id, status_code, mock_request):
|
||||
def test_comment_actions(self, user, commentable_id, status_code, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Verify that voting and flagging of comments is limited to members of the team or users with
|
||||
'edit_content' permission.
|
||||
"""
|
||||
commentable_id = getattr(self, commentable_id)
|
||||
self._setup_mock(
|
||||
user, mock_request,
|
||||
user, mock_is_forum_v2_enabled, mock_request,
|
||||
{
|
||||
"closed": False,
|
||||
"commentable_id": commentable_id,
|
||||
@@ -1742,14 +1893,14 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
|
||||
|
||||
@ddt.data(*ddt_permissions_args)
|
||||
@ddt.unpack
|
||||
def test_threads_actions(self, user, commentable_id, status_code, mock_request):
|
||||
def test_threads_actions(self, user, commentable_id, status_code, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Verify that voting, flagging, and following of threads is limited to members of the team or users with
|
||||
'edit_content' permission.
|
||||
"""
|
||||
commentable_id = getattr(self, commentable_id)
|
||||
self._setup_mock(
|
||||
user, mock_request,
|
||||
user, mock_is_forum_v2_enabled, mock_request,
|
||||
{"closed": False, "commentable_id": commentable_id, "body": "dummy body", "course_id": str(self.course.id)}
|
||||
)
|
||||
for action in ["upvote_thread", "downvote_thread", "un_flag_abuse_for_thread", "flag_abuse_for_thread",
|
||||
@@ -1772,6 +1923,19 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
|
||||
"""
|
||||
Forum actions are expected to launch analytics events. Test these here.
|
||||
"""
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# pylint: disable=super-method-not-called
|
||||
@@ -1791,12 +1955,14 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
|
||||
|
||||
@patch('eventtracking.tracker.emit')
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def test_response_event(self, mock_request, mock_emit):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def test_response_event(self, mock_is_forum_v2_enabled, 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)
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"closed": False,
|
||||
"commentable_id": 'test_commentable_id',
|
||||
@@ -1833,12 +1999,14 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
|
||||
|
||||
@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):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def test_comment_event(self, mock_is_forum_v2_enabled, 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)
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"closed": False,
|
||||
"depth": 1,
|
||||
@@ -1875,6 +2043,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
|
||||
|
||||
@patch('eventtracking.tracker.emit')
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
@ddt.data((
|
||||
'create_thread',
|
||||
'edx.forum.thread.created', {
|
||||
@@ -1896,7 +2065,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
|
||||
{'comment_id': 'dummy_comment_id'}
|
||||
))
|
||||
@ddt.unpack
|
||||
def test_team_events(self, view_name, event_name, view_data, view_kwargs, mock_request, mock_emit):
|
||||
def test_team_events(self, view_name, event_name, view_data, view_kwargs, mock_is_forum_v2_enabled, mock_request, mock_emit):
|
||||
user = self.student
|
||||
team = CourseTeamFactory.create(discussion_topic_id=TEAM_COMMENTABLE_ID)
|
||||
CourseTeamMembershipFactory.create(team=team, user=user)
|
||||
@@ -1905,6 +2074,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
|
||||
forum_event = views.TRACKING_LOG_TO_EVENT_MAPS.get(event_name)
|
||||
forum_event.connect(event_receiver)
|
||||
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
'closed': False,
|
||||
'commentable_id': TEAM_COMMENTABLE_ID,
|
||||
@@ -1943,9 +2113,11 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
|
||||
@ddt.unpack
|
||||
@patch('eventtracking.tracker.emit')
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def test_thread_voted_event(self, view_name, obj_id_name, obj_type, mock_request, mock_emit):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def test_thread_voted_event(self, view_name, obj_id_name, obj_type, mock_is_forum_v2_enabled, mock_request, mock_emit):
|
||||
undo = view_name.startswith('undo')
|
||||
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
'closed': False,
|
||||
'commentable_id': 'test_commentable_id',
|
||||
@@ -1971,11 +2143,13 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
|
||||
@ddt.data('follow_thread', 'unfollow_thread',)
|
||||
@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):
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def test_thread_followed_event(self, view_name, mock_is_forum_v2_enabled, mock_request, mock_emit):
|
||||
event_receiver = Mock()
|
||||
for signal in views.TRACKING_LOG_TO_EVENT_MAPS.values():
|
||||
signal.connect(event_receiver)
|
||||
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
'closed': False,
|
||||
'commentable_id': 'test_commentable_id',
|
||||
@@ -2025,10 +2199,11 @@ class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRe
|
||||
cls.other_user = UserFactory.create(username="other")
|
||||
CourseEnrollmentFactory(user=cls.other_user, course_id=cls.course.id)
|
||||
|
||||
def set_post_counts(self, mock_request, threads_count=1, comments_count=1):
|
||||
def set_post_counts(self, mock_is_forum_v2_enabled, mock_request, threads_count=1, comments_count=1):
|
||||
"""
|
||||
sets up a mock response from the comments service for getting post counts for our other_user
|
||||
"""
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
self._set_mock_request_data(mock_request, {
|
||||
"threads_count": threads_count,
|
||||
"comments_count": comments_count,
|
||||
@@ -2042,15 +2217,17 @@ class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRe
|
||||
return views.users(request, course_id=str(course_id))
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def test_finds_exact_match(self, mock_request):
|
||||
self.set_post_counts(mock_request)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def test_finds_exact_match(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.set_post_counts(mock_is_forum_v2_enabled, mock_request)
|
||||
response = self.make_request(username="other")
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.content.decode('utf-8'))['users'] == [{'id': self.other_user.id, 'username': self.other_user.username}]
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def test_finds_no_match(self, mock_request):
|
||||
self.set_post_counts(mock_request)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def test_finds_no_match(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.set_post_counts(mock_is_forum_v2_enabled, mock_request)
|
||||
response = self.make_request(username="othor")
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.content.decode('utf-8'))['users'] == []
|
||||
@@ -2086,8 +2263,9 @@ class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRe
|
||||
assert 'users' not in content
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
def test_requires_matched_user_has_forum_content(self, mock_request):
|
||||
self.set_post_counts(mock_request, 0, 0)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
def test_requires_matched_user_has_forum_content(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.set_post_counts(mock_is_forum_v2_enabled, mock_request, 0, 0)
|
||||
response = self.make_request(username="other")
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.content.decode('utf-8'))['users'] == []
|
||||
|
||||
@@ -562,7 +562,6 @@ def create_thread(request, course_id, commentable_id):
|
||||
params['context'] = ThreadContext.STANDALONE
|
||||
else:
|
||||
params['context'] = ThreadContext.COURSE
|
||||
|
||||
thread = cc.Thread(**params)
|
||||
|
||||
# Divide the thread if required
|
||||
|
||||
@@ -60,51 +60,76 @@ class CohortedTopicGroupIdTestMixin(GroupIdAssertionMixin):
|
||||
Provides test cases to verify that views pass the correct `group_id` to
|
||||
the comments service when requesting content in cohorted discussions.
|
||||
"""
|
||||
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
|
||||
def call_view(self, mock_is_forum_v2_enabled, mock_request, commentable_id, user, group_id, pass_group_id=True):
|
||||
"""
|
||||
Call the view for the implementing test class, constructing a request
|
||||
from the parameters.
|
||||
"""
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
|
||||
def test_cohorted_topic_student_without_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "cohorted_topic", self.student, '', pass_group_id=False)
|
||||
def test_cohorted_topic_student_without_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.student, '', pass_group_id=False)
|
||||
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
|
||||
|
||||
def test_cohorted_topic_student_none_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "cohorted_topic", self.student, "")
|
||||
def test_cohorted_topic_student_none_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.student, "")
|
||||
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
|
||||
|
||||
def test_cohorted_topic_student_with_own_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "cohorted_topic", self.student, self.student_cohort.id)
|
||||
def test_cohorted_topic_student_with_own_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.student, self.student_cohort.id)
|
||||
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
|
||||
|
||||
def test_cohorted_topic_student_with_other_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "cohorted_topic", self.student, self.moderator_cohort.id)
|
||||
def test_cohorted_topic_student_with_other_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.student,
|
||||
self.moderator_cohort.id
|
||||
)
|
||||
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
|
||||
|
||||
def test_cohorted_topic_moderator_without_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "cohorted_topic", self.moderator, '', pass_group_id=False)
|
||||
def test_cohorted_topic_moderator_without_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.moderator,
|
||||
'',
|
||||
pass_group_id=False
|
||||
)
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_cohorted_topic_moderator_none_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "cohorted_topic", self.moderator, "")
|
||||
def test_cohorted_topic_moderator_none_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.moderator, "")
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_cohorted_topic_moderator_with_own_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "cohorted_topic", self.moderator, self.moderator_cohort.id)
|
||||
def test_cohorted_topic_moderator_with_own_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.moderator,
|
||||
self.moderator_cohort.id
|
||||
)
|
||||
self._assert_comments_service_called_with_group_id(mock_request, self.moderator_cohort.id)
|
||||
|
||||
def test_cohorted_topic_moderator_with_other_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "cohorted_topic", self.moderator, self.student_cohort.id)
|
||||
def test_cohorted_topic_moderator_with_other_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.moderator,
|
||||
self.student_cohort.id
|
||||
)
|
||||
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
|
||||
|
||||
def test_cohorted_topic_moderator_with_invalid_group_id(self, mock_request):
|
||||
def test_cohorted_topic_moderator_with_invalid_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
invalid_id = self.student_cohort.id + self.moderator_cohort.id
|
||||
response = self.call_view(mock_request, "cohorted_topic", self.moderator, invalid_id) # lint-amnesty, pylint: disable=assignment-from-no-return
|
||||
response = self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.moderator, invalid_id) # lint-amnesty, pylint: disable=assignment-from-no-return
|
||||
assert response.status_code == 500
|
||||
|
||||
def test_cohorted_topic_enrollment_track_invalid_group_id(self, mock_request):
|
||||
def test_cohorted_topic_enrollment_track_invalid_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.AUDIT)
|
||||
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.VERIFIED)
|
||||
discussion_settings = CourseDiscussionSettings.get(self.course.id)
|
||||
@@ -115,7 +140,7 @@ class CohortedTopicGroupIdTestMixin(GroupIdAssertionMixin):
|
||||
})
|
||||
|
||||
invalid_id = -1000
|
||||
response = self.call_view(mock_request, "cohorted_topic", self.moderator, invalid_id) # lint-amnesty, pylint: disable=assignment-from-no-return
|
||||
response = self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.moderator, invalid_id) # lint-amnesty, pylint: disable=assignment-from-no-return
|
||||
assert response.status_code == 500
|
||||
|
||||
|
||||
@@ -124,57 +149,95 @@ class NonCohortedTopicGroupIdTestMixin(GroupIdAssertionMixin):
|
||||
Provides test cases to verify that views pass the correct `group_id` to
|
||||
the comments service when requesting content in non-cohorted discussions.
|
||||
"""
|
||||
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
|
||||
def call_view(self, mock_is_forum_v2_enabled, mock_request, commentable_id, user, group_id, pass_group_id=True):
|
||||
"""
|
||||
Call the view for the implementing test class, constructing a request
|
||||
from the parameters.
|
||||
"""
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
|
||||
def test_non_cohorted_topic_student_without_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.student, '', pass_group_id=False)
|
||||
def test_non_cohorted_topic_student_without_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"non_cohorted_topic",
|
||||
self.student,
|
||||
'',
|
||||
pass_group_id=False
|
||||
)
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_non_cohorted_topic_student_none_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.student, '')
|
||||
def test_non_cohorted_topic_student_none_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(mock_is_forum_v2_enabled, mock_request, "non_cohorted_topic", self.student, '')
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_non_cohorted_topic_student_with_own_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.student, self.student_cohort.id)
|
||||
def test_non_cohorted_topic_student_with_own_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"non_cohorted_topic",
|
||||
self.student,
|
||||
self.student_cohort.id
|
||||
)
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_non_cohorted_topic_student_with_other_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.student, self.moderator_cohort.id)
|
||||
def test_non_cohorted_topic_student_with_other_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"non_cohorted_topic",
|
||||
self.student,
|
||||
self.moderator_cohort.id
|
||||
)
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_non_cohorted_topic_moderator_without_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.moderator, '', pass_group_id=False)
|
||||
def test_non_cohorted_topic_moderator_without_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"non_cohorted_topic",
|
||||
self.moderator,
|
||||
"",
|
||||
pass_group_id=False,
|
||||
)
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_non_cohorted_topic_moderator_none_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.moderator, '')
|
||||
def test_non_cohorted_topic_moderator_none_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(mock_is_forum_v2_enabled, mock_request, "non_cohorted_topic", self.moderator, '')
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_non_cohorted_topic_moderator_with_own_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.moderator, self.moderator_cohort.id)
|
||||
def test_non_cohorted_topic_moderator_with_own_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"non_cohorted_topic",
|
||||
self.moderator,
|
||||
self.moderator_cohort.id,
|
||||
)
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_non_cohorted_topic_moderator_with_other_group_id(self, mock_request):
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.moderator, self.student_cohort.id)
|
||||
def test_non_cohorted_topic_moderator_with_other_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"non_cohorted_topic",
|
||||
self.moderator,
|
||||
self.student_cohort.id,
|
||||
)
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_non_cohorted_topic_moderator_with_invalid_group_id(self, mock_request):
|
||||
def test_non_cohorted_topic_moderator_with_invalid_group_id(self, mock_is_forum_v2_enabled, mock_request):
|
||||
invalid_id = self.student_cohort.id + self.moderator_cohort.id
|
||||
self.call_view(mock_request, "non_cohorted_topic", self.moderator, invalid_id)
|
||||
self.call_view(mock_is_forum_v2_enabled, mock_request, "non_cohorted_topic", self.moderator, invalid_id)
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
def test_team_discussion_id_not_cohorted(self, mock_request):
|
||||
def test_team_discussion_id_not_cohorted(self, mock_is_forum_v2_enabled, mock_request):
|
||||
team = CourseTeamFactory(
|
||||
course_id=self.course.id,
|
||||
topic_id='topic-id'
|
||||
)
|
||||
|
||||
team.add_user(self.student)
|
||||
self.call_view(mock_request, team.discussion_topic_id, self.student, '')
|
||||
self.call_view(mock_is_forum_v2_enabled, mock_request, team.discussion_topic_id, self.student, '')
|
||||
|
||||
self._assert_comments_service_called_without_group_id(mock_request)
|
||||
|
||||
@@ -199,7 +199,7 @@ def _get_course(course_key: CourseKey, user: User, check_tab: bool = True) -> Co
|
||||
return course
|
||||
|
||||
|
||||
def _get_thread_and_context(request, thread_id, retrieve_kwargs=None):
|
||||
def _get_thread_and_context(request, thread_id, retrieve_kwargs=None, course_id=None):
|
||||
"""
|
||||
Retrieve the given thread and build a serializer context for it, returning
|
||||
both. This function also enforces access control for the thread (checking
|
||||
@@ -213,7 +213,7 @@ def _get_thread_and_context(request, thread_id, retrieve_kwargs=None):
|
||||
retrieve_kwargs["with_responses"] = False
|
||||
if "mark_as_read" not in retrieve_kwargs:
|
||||
retrieve_kwargs["mark_as_read"] = False
|
||||
cc_thread = Thread(id=thread_id).retrieve(**retrieve_kwargs)
|
||||
cc_thread = Thread(id=thread_id).retrieve(course_id=course_id, **retrieve_kwargs)
|
||||
course_key = CourseKey.from_string(cc_thread["course_id"])
|
||||
course = _get_course(course_key, request.user)
|
||||
context = get_context(course, request, cc_thread)
|
||||
@@ -1645,7 +1645,8 @@ def get_thread(request, thread_id, requested_fields=None, course_id=None):
|
||||
retrieve_kwargs={
|
||||
"with_responses": True,
|
||||
"user_id": str(request.user.id),
|
||||
}
|
||||
},
|
||||
course_id=course_id,
|
||||
)
|
||||
if course_id and course_id != cc_thread.course_id:
|
||||
raise ThreadNotFoundError("Thread not found.")
|
||||
|
||||
@@ -202,7 +202,7 @@ class DiscussionNotificationSender:
|
||||
|
||||
while has_more_subscribers:
|
||||
|
||||
subscribers = Subscription.fetch(self.thread.id, query_params={'page': page})
|
||||
subscribers = Subscription.fetch(self.thread.id, self.course.id, query_params={'page': page})
|
||||
if page <= subscribers.num_pages:
|
||||
for subscriber in subscribers.collection:
|
||||
# Check if the subscriber is not the thread creator or response creator
|
||||
|
||||
@@ -68,7 +68,7 @@ def get_context(course, request, thread=None):
|
||||
moderator_user_ids = get_moderator_users_list(course.id)
|
||||
ta_user_ids = get_course_ta_users_list(course.id)
|
||||
requester = request.user
|
||||
cc_requester = CommentClientUser.from_django_user(requester).retrieve()
|
||||
cc_requester = CommentClientUser.from_django_user(requester).retrieve(course_id=course.id)
|
||||
cc_requester["course_id"] = course.id
|
||||
course_discussion_settings = CourseDiscussionSettings.get(course.id)
|
||||
is_global_staff = GlobalStaff().has_user(requester)
|
||||
|
||||
@@ -1248,6 +1248,22 @@ class GetCommentListTest(ForumsEnableMixin, CommentsServiceMockMixin, SharedModu
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.maxDiff = None # pylint: disable=invalid-name
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -1872,6 +1888,12 @@ class CreateThreadTest(
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
self.request = RequestFactory().get("/test_path")
|
||||
@@ -2198,6 +2220,22 @@ class CreateCommentTest(
|
||||
self.course = CourseFactory.create()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
self.request = RequestFactory().get("/test_path")
|
||||
@@ -2589,6 +2627,17 @@ class UpdateThreadTest(
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -3153,6 +3202,22 @@ class UpdateCommentTest(
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
self.request = RequestFactory().get("/test_path")
|
||||
@@ -3670,6 +3735,22 @@ class DeleteThreadTest(
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
self.request = RequestFactory().get("/test_path")
|
||||
@@ -3823,6 +3904,22 @@ class DeleteCommentTest(
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
self.request = RequestFactory().get("/test_path")
|
||||
@@ -3991,6 +4088,17 @@ class RetrieveThreadTest(
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
self.request = RequestFactory().get("/test_path")
|
||||
|
||||
@@ -54,6 +54,12 @@ class SerializerTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetM
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.maxDiff = None # pylint: disable=invalid-name
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -571,6 +577,12 @@ class ThreadSerializerDeserializationTest(
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
self.request = RequestFactory().get("/dummy")
|
||||
@@ -802,6 +814,22 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user = UserFactory.create()
|
||||
self.register_get_user_response(self.user)
|
||||
self.request = RequestFactory().get("/dummy")
|
||||
|
||||
@@ -58,10 +58,27 @@ class TestNewThreadCreatedNotification(DiscussionAPIViewTestMixin, ModuleStoreTe
|
||||
Setup test case
|
||||
"""
|
||||
super().setUp()
|
||||
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
# Creating a course
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread",
|
||||
return_value=self.course.id
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment",
|
||||
return_value=self.course.id
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
# Creating relative discussion and cohort settings
|
||||
CourseCohortsSettings.objects.create(course_id=str(self.course.id))
|
||||
CourseDiscussionSettings.objects.create(course_id=str(self.course.id), _divided_discussions='[]')
|
||||
@@ -250,8 +267,26 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
|
||||
super().setUp()
|
||||
httpretty.reset()
|
||||
httpretty.enable()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread",
|
||||
return_value=self.course.id
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment",
|
||||
return_value=self.course.id
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user_1 = UserFactory.create()
|
||||
CourseEnrollment.enroll(self.user_1, self.course.id)
|
||||
self.user_2 = UserFactory.create()
|
||||
@@ -536,8 +571,26 @@ class TestSendCommentNotification(DiscussionAPIViewTestMixin, ModuleStoreTestCas
|
||||
super().setUp()
|
||||
httpretty.reset()
|
||||
httpretty.enable()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread",
|
||||
return_value=self.course.id
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment",
|
||||
return_value=self.course.id
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user_1 = UserFactory.create()
|
||||
CourseEnrollment.enroll(self.user_1, self.course.id)
|
||||
self.user_2 = UserFactory.create()
|
||||
@@ -603,8 +656,26 @@ class TestResponseEndorsedNotifications(DiscussionAPIViewTestMixin, ModuleStoreT
|
||||
super().setUp()
|
||||
httpretty.reset()
|
||||
httpretty.enable()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
self.course = CourseFactory.create()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread",
|
||||
return_value=self.course.id
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment",
|
||||
return_value=self.course.id
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.user_1 = UserFactory.create()
|
||||
CourseEnrollment.enroll(self.user_1, self.course.id)
|
||||
self.user_2 = UserFactory.create()
|
||||
|
||||
@@ -171,6 +171,12 @@ class UploadFileViewTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMi
|
||||
self.user = UserFactory.create(password=self.TEST_PASSWORD)
|
||||
self.course = CourseFactory.create(org='a', course='b', run='c', start=datetime.now(UTC))
|
||||
self.url = reverse("upload_file", kwargs={"course_id": str(self.course.id)})
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def user_login(self):
|
||||
"""
|
||||
@@ -301,6 +307,7 @@ class UploadFileViewTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMi
|
||||
|
||||
@ddt.ddt
|
||||
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_FORUM_V2": False})
|
||||
class CommentViewSetListByUserTest(
|
||||
ForumsEnableMixin,
|
||||
CommentsServiceMockMixin,
|
||||
@@ -319,6 +326,12 @@ class CommentViewSetListByUserTest(
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
self.user = UserFactory.create(password=self.TEST_PASSWORD)
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -500,6 +513,12 @@ class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.url = reverse("discussion_course", kwargs={"course_id": str(self.course.id)})
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_404(self):
|
||||
response = self.client.get(
|
||||
@@ -561,6 +580,12 @@ class RetireViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
self.superuser_client = APIClient()
|
||||
self.retired_username = get_retired_username_by_username(self.user.username)
|
||||
self.url = reverse("retire_discussion_user")
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def assert_response_correct(self, response, expected_status, expected_content):
|
||||
"""
|
||||
@@ -631,6 +656,12 @@ class ReplaceUsernamesViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
self.worker_client = APIClient()
|
||||
self.new_username = "test_username_replacement"
|
||||
self.url = reverse("replace_discussion_username")
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def assert_response_correct(self, response, expected_status, expected_content):
|
||||
"""
|
||||
@@ -733,6 +764,12 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, CommentsServiceMockMixin,
|
||||
"courseware-3": {"discussion": 7, "question": 2},
|
||||
}
|
||||
self.register_get_course_commentable_counts_response(self.course.id, self.thread_counts_map)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def create_course(self, blocks_count, module_store, topics):
|
||||
"""
|
||||
@@ -988,6 +1025,12 @@ class CourseTopicsViewV3Test(DiscussionAPIViewTestMixin, CommentsServiceMockMixi
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.url = reverse("course_topics_v3", kwargs={"course_id": str(self.course.id)})
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_basic(self):
|
||||
response = self.client.get(self.url)
|
||||
@@ -1024,6 +1067,12 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
|
||||
super().setUp()
|
||||
self.author = UserFactory.create()
|
||||
self.url = reverse("thread-list")
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def create_source_thread(self, overrides=None):
|
||||
"""
|
||||
@@ -1365,6 +1414,12 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.url = reverse("thread-list")
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_basic(self):
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -1437,6 +1492,17 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
|
||||
self.unsupported_media_type = JSONParser.media_type
|
||||
super().setUp()
|
||||
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_basic(self):
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -1581,6 +1647,17 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
super().setUp()
|
||||
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
|
||||
self.thread_id = "test_thread"
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_basic(self):
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -1681,6 +1758,12 @@ class LearnerThreadViewAPITest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
|
||||
]
|
||||
self.url = reverse("discussion_learner_threads", kwargs={'course_id': str(self.course.id)})
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def update_thread(self, thread):
|
||||
"""
|
||||
@@ -1923,6 +2006,17 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
|
||||
self.url = reverse("comment-list")
|
||||
self.thread_id = "test_thread"
|
||||
self.storage = get_profile_image_storage()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def create_source_comment(self, overrides=None):
|
||||
"""
|
||||
@@ -2377,6 +2471,22 @@ class CommentViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
super().setUp()
|
||||
self.url = reverse("comment-detail", kwargs={"comment_id": "test_comment"})
|
||||
self.comment_id = "test_comment"
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_basic(self):
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -2416,6 +2526,23 @@ class CommentViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.url = reverse("comment-list")
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_basic(self):
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -2518,6 +2645,22 @@ class CommentViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTes
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.reset)
|
||||
self.addCleanup(httpretty.disable)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.register_get_user_response(self.user)
|
||||
self.url = reverse("comment-detail", kwargs={"comment_id": "test_comment"})
|
||||
|
||||
@@ -2640,6 +2783,22 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase,
|
||||
super().setUp()
|
||||
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
|
||||
self.thread_id = "test_thread"
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_basic(self):
|
||||
self.register_get_user_response(self.user)
|
||||
@@ -2693,6 +2852,22 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
|
||||
self.url = reverse("comment-detail", kwargs={"comment_id": "test_comment"})
|
||||
self.thread_id = "test_thread"
|
||||
self.comment_id = "test_comment"
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def make_comment_data(self, comment_id, parent_id=None, children=[]): # pylint: disable=W0102
|
||||
"""
|
||||
@@ -2838,6 +3013,12 @@ class CourseDiscussionSettingsAPIViewTest(APITestCase, UrlResetMixin, ModuleStor
|
||||
self.path = reverse('discussion_course_settings', kwargs={'course_id': str(self.course.id)})
|
||||
self.password = self.TEST_PASSWORD
|
||||
self.user = UserFactory(username='staff', password=self.password, is_staff=True)
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def _get_oauth_headers(self, user):
|
||||
"""Return the OAuth headers for testing OAuth authentication"""
|
||||
@@ -3127,6 +3308,12 @@ class CourseDiscussionRolesAPIViewTest(APITestCase, UrlResetMixin, ModuleStoreTe
|
||||
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.course = CourseFactory.create(
|
||||
org="x",
|
||||
course="y",
|
||||
@@ -3318,6 +3505,12 @@ class CourseActivityStatsTest(ForumsEnableMixin, UrlResetMixin, CommentsServiceM
|
||||
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.course = CourseFactory.create()
|
||||
self.course_key = str(self.course.id)
|
||||
seed_permissions_roles(self.course.id)
|
||||
|
||||
@@ -232,6 +232,22 @@ class TaskTestCase(ModuleStoreTestCase): # lint-amnesty, pylint: disable=missin
|
||||
thread_permalink = '/courses/discussion/dummy_discussion_id'
|
||||
self.permalink_patcher = mock.patch('lms.djangoapps.discussion.tasks.permalink', return_value=thread_permalink)
|
||||
self.mock_permalink = self.permalink_patcher.start()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
|
||||
@@ -4,6 +4,7 @@ Tests the forum notification views.
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from unittest import mock
|
||||
from unittest.mock import ANY, Mock, call, patch
|
||||
|
||||
import ddt
|
||||
@@ -109,9 +110,20 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase): # lint-amnest
|
||||
config = ForumsConfig.current()
|
||||
config.enabled = True
|
||||
config.save()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
@patch('common.djangoapps.student.models.user.cc.User.from_django_user')
|
||||
@patch('common.djangoapps.student.models.user.cc.User.active_threads')
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.user.User.active_threads')
|
||||
def test_user_profile_exception(self, mock_threads, mock_from_django_user):
|
||||
|
||||
# Mock the code that makes the HTTP requests to the cs_comment_service app
|
||||
@@ -323,6 +335,17 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
self.course = CourseFactory.create(discussion_topics={'dummy discussion': {'id': 'dummy_discussion_id'}})
|
||||
self.student = UserFactory.create()
|
||||
@@ -513,6 +536,20 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
|
||||
Ensures the number of modulestore queries and number of sql queries are
|
||||
independent of the number of responses retrieved for a given discussion thread.
|
||||
"""
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
@ddt.data(
|
||||
# split mongo: 3 queries, regardless of thread response size.
|
||||
(False, 1, 2, 2, 21, 8),
|
||||
@@ -582,6 +619,20 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
|
||||
@patch('requests.request', autospec=True)
|
||||
class SingleCohortedThreadTestCase(CohortedTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def _create_mock_cohorted_thread(self, mock_request): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
mock_text = "dummy content"
|
||||
mock_thread_id = "test_thread_id"
|
||||
@@ -644,6 +695,20 @@ class SingleCohortedThreadTestCase(CohortedTestCase): # lint-amnesty, pylint: d
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
class SingleThreadAccessTestCase(CohortedTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def call_view(self, mock_request, commentable_id, user, group_id, thread_group_id=None, pass_group_id=True): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
thread_id = "test_thread_id"
|
||||
mock_request.side_effect = make_mock_request_impl(
|
||||
@@ -746,6 +811,20 @@ class SingleThreadAccessTestCase(CohortedTestCase): # lint-amnesty, pylint: dis
|
||||
class SingleThreadGroupIdTestCase(CohortedTestCase, GroupIdAssertionMixin): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
cs_endpoint = "/threads/dummy_thread_id"
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True, is_ajax=False): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
mock_request.side_effect = make_mock_request_impl(
|
||||
course=self.course, text="dummy context", group_id=self.student_cohort.id
|
||||
@@ -881,6 +960,22 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
|
||||
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def assert_can_access(self, user, discussion_id, thread_id, should_have_access):
|
||||
"""
|
||||
@@ -1046,6 +1141,7 @@ class InlineDiscussionContextTestCase(ForumsEnableMixin, ModuleStoreTestCase):
|
||||
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
CohortedTestCase,
|
||||
CohortedTopicGroupIdTestMixin,
|
||||
@@ -1056,8 +1152,22 @@ class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.cohorted_commentable_id = 'cohorted_topic'
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
|
||||
def call_view(
|
||||
self,
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
commentable_id,
|
||||
user,
|
||||
group_id,
|
||||
pass_group_id=True
|
||||
): # pylint: disable=arguments-differ
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
kwargs = {'commentable_id': self.cohorted_commentable_id}
|
||||
if group_id:
|
||||
# avoid causing a server error when the LMS chokes attempting
|
||||
@@ -1084,8 +1194,9 @@ class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-
|
||||
commentable_id
|
||||
)
|
||||
|
||||
def test_group_info_in_ajax_response(self, mock_request):
|
||||
def test_group_info_in_ajax_response(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
self.cohorted_commentable_id,
|
||||
self.student,
|
||||
@@ -1097,10 +1208,29 @@ class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-
|
||||
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdTestMixin): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
cs_endpoint = "/threads"
|
||||
|
||||
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True, is_ajax=False): # pylint: disable=arguments-differ
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def call_view(
|
||||
self,
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
commentable_id,
|
||||
user,
|
||||
group_id,
|
||||
pass_group_id=True,
|
||||
is_ajax=False
|
||||
): # pylint: disable=arguments-differ
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
kwargs = {}
|
||||
if group_id:
|
||||
kwargs['group_id'] = group_id
|
||||
@@ -1120,8 +1250,9 @@ class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdT
|
||||
**headers
|
||||
)
|
||||
|
||||
def test_group_info_in_html_response(self, mock_request):
|
||||
def test_group_info_in_html_response(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.student,
|
||||
@@ -1129,8 +1260,9 @@ class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdT
|
||||
)
|
||||
self._assert_html_response_contains_group_info(response)
|
||||
|
||||
def test_group_info_in_ajax_response(self, mock_request):
|
||||
def test_group_info_in_ajax_response(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.student,
|
||||
@@ -1143,16 +1275,38 @@ class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdT
|
||||
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdTestMixin): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
cs_endpoint = "/active_threads"
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def call_view_for_profiled_user(
|
||||
self, mock_request, requesting_user, profiled_user, group_id, pass_group_id, is_ajax=False
|
||||
self,
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
requesting_user,
|
||||
profiled_user,
|
||||
group_id,
|
||||
pass_group_id,
|
||||
is_ajax=False
|
||||
):
|
||||
"""
|
||||
Calls "user_profile" view method on behalf of "requesting_user" to get information about
|
||||
the user "profiled_user".
|
||||
"""
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
kwargs = {}
|
||||
if group_id:
|
||||
kwargs['group_id'] = group_id
|
||||
@@ -1172,13 +1326,23 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
**headers
|
||||
)
|
||||
|
||||
def call_view(self, mock_request, _commentable_id, user, group_id, pass_group_id=True, is_ajax=False): # pylint: disable=arguments-differ
|
||||
def call_view(
|
||||
self,
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
_commentable_id,
|
||||
user,
|
||||
group_id,
|
||||
pass_group_id=True,
|
||||
is_ajax=False
|
||||
): # pylint: disable=arguments-differ
|
||||
return self.call_view_for_profiled_user(
|
||||
mock_request, user, user, group_id, pass_group_id=pass_group_id, is_ajax=is_ajax
|
||||
mock_is_forum_v2_enabled, mock_request, user, user, group_id, pass_group_id=pass_group_id, is_ajax=is_ajax
|
||||
)
|
||||
|
||||
def test_group_info_in_html_response(self, mock_request):
|
||||
def test_group_info_in_html_response(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.student,
|
||||
@@ -1187,8 +1351,9 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
)
|
||||
self._assert_html_response_contains_group_info(response)
|
||||
|
||||
def test_group_info_in_ajax_response(self, mock_request):
|
||||
def test_group_info_in_ajax_response(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.student,
|
||||
@@ -1200,7 +1365,14 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
)
|
||||
|
||||
def _test_group_id_passed_to_user_profile(
|
||||
self, mock_request, expect_group_id_in_request, requesting_user, profiled_user, group_id, pass_group_id
|
||||
self,
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
expect_group_id_in_request,
|
||||
requesting_user,
|
||||
profiled_user,
|
||||
group_id,
|
||||
pass_group_id
|
||||
):
|
||||
"""
|
||||
Helper method for testing whether or not group_id was passed to the user_profile request.
|
||||
@@ -1221,10 +1393,11 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
has_course_id = "course_id" in params
|
||||
if (for_specific_course and has_course_id) or (not for_specific_course and not has_course_id):
|
||||
return params
|
||||
pytest.fail("Did not find appropriate user_profile call for 'for_specific_course'=" + for_specific_course)
|
||||
pytest.fail(f"Did not find appropriate user_profile call for 'for_specific_course'={for_specific_course}")
|
||||
|
||||
mock_request.reset_mock()
|
||||
self.call_view_for_profiled_user(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
requesting_user,
|
||||
profiled_user,
|
||||
@@ -1243,7 +1416,7 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
else:
|
||||
assert 'group_id' not in params_with_course_id
|
||||
|
||||
def test_group_id_passed_to_user_profile_student(self, mock_request):
|
||||
def test_group_id_passed_to_user_profile_student(self, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Test that the group id is always included when requesting user profile information for a particular
|
||||
course if the requester does not have discussion moderation privileges.
|
||||
@@ -1254,7 +1427,13 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
(non-privileged user).
|
||||
"""
|
||||
self._test_group_id_passed_to_user_profile(
|
||||
mock_request, True, self.student, profiled_user, self.student_cohort.id, pass_group_id
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
True,
|
||||
self.student,
|
||||
profiled_user,
|
||||
self.student_cohort.id,
|
||||
pass_group_id
|
||||
)
|
||||
|
||||
# In all these test cases, the requesting_user is the student (non-privileged user).
|
||||
@@ -1264,7 +1443,7 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
verify_group_id_always_present(profiled_user=self.moderator, pass_group_id=True)
|
||||
verify_group_id_always_present(profiled_user=self.moderator, pass_group_id=False)
|
||||
|
||||
def test_group_id_user_profile_moderator(self, mock_request):
|
||||
def test_group_id_user_profile_moderator(self, mock_is_forum_v2_enabled, mock_request):
|
||||
"""
|
||||
Test that the group id is only included when a privileged user requests user profile information for a
|
||||
particular course and user if the group_id is explicitly passed in.
|
||||
@@ -1274,7 +1453,13 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
Helper method to verify that group_id is present.
|
||||
"""
|
||||
self._test_group_id_passed_to_user_profile(
|
||||
mock_request, True, self.moderator, profiled_user, requested_cohort.id, pass_group_id
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
True,
|
||||
self.moderator,
|
||||
profiled_user,
|
||||
requested_cohort.id,
|
||||
pass_group_id
|
||||
)
|
||||
|
||||
def verify_group_id_not_present(profiled_user, pass_group_id, requested_cohort=self.moderator_cohort):
|
||||
@@ -1282,7 +1467,13 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
Helper method to verify that group_id is not present.
|
||||
"""
|
||||
self._test_group_id_passed_to_user_profile(
|
||||
mock_request, False, self.moderator, profiled_user, requested_cohort.id, pass_group_id
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
False,
|
||||
self.moderator,
|
||||
profiled_user,
|
||||
requested_cohort.id,
|
||||
pass_group_id
|
||||
)
|
||||
|
||||
# In all these test cases, the requesting_user is the moderator (privileged user).
|
||||
@@ -1301,10 +1492,28 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
|
||||
|
||||
|
||||
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
|
||||
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
|
||||
class FollowedThreadsDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdTestMixin): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
cs_endpoint = "/subscribed_threads"
|
||||
|
||||
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def call_view(
|
||||
self,
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
commentable_id,
|
||||
user,
|
||||
group_id,
|
||||
pass_group_id=True
|
||||
): # pylint: disable=arguments-differ
|
||||
mock_is_forum_v2_enabled.return_value = False
|
||||
kwargs = {}
|
||||
if group_id:
|
||||
kwargs['group_id'] = group_id
|
||||
@@ -1325,8 +1534,9 @@ class FollowedThreadsDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGr
|
||||
user.id
|
||||
)
|
||||
|
||||
def test_group_info_in_ajax_response(self, mock_request):
|
||||
def test_group_info_in_ajax_response(self, mock_is_forum_v2_enabled, mock_request):
|
||||
response = self.call_view(
|
||||
mock_is_forum_v2_enabled,
|
||||
mock_request,
|
||||
"cohorted_topic",
|
||||
self.student,
|
||||
@@ -1528,6 +1738,22 @@ class CommentsServiceRequestHeadersTestCase(ForumsEnableMixin, UrlResetMixin, Mo
|
||||
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
|
||||
)
|
||||
self.mock_get_course_id_by_comment = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
username = "foo"
|
||||
password = "bar"
|
||||
@@ -1742,6 +1968,20 @@ class SingleThreadUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCase,
|
||||
with super().setUpClassAndTestData():
|
||||
cls.course = CourseFactory.create(discussion_topics={'dummy_discussion_id': {'id': 'dummy_discussion_id'}})
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super().setUpTestData()
|
||||
@@ -1858,7 +2098,17 @@ class EnterpriseConsentTestCase(EnterpriseTestConsentRequired, ForumsEnableMixin
|
||||
def setUp(self):
|
||||
# Invoke UrlResetMixin setUp
|
||||
super().setUp()
|
||||
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
username = "foo"
|
||||
password = "bar"
|
||||
|
||||
@@ -2195,6 +2445,17 @@ class ThreadViewedEventTestCase(EventTestMixin, ForumsEnableMixin, UrlResetMixin
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super().setUp('lms.djangoapps.discussion.django_comment_client.base.views.tracker')
|
||||
|
||||
patcher = mock.patch(
|
||||
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
|
||||
return_value=False
|
||||
)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
patcher = mock.patch(
|
||||
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
|
||||
)
|
||||
self.mock_get_course_id_by_thread = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
self.course = CourseFactory.create(
|
||||
teams_configuration=TeamsConfig({
|
||||
'topics': [{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Discussions feature toggles
|
||||
"""
|
||||
|
||||
from openedx.core.djangoapps.discussions.config.waffle import WAFFLE_FLAG_NAMESPACE
|
||||
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
|
||||
|
||||
@@ -11,4 +12,6 @@ from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
|
||||
# .. toggle_use_cases: temporary, open_edx
|
||||
# .. toggle_creation_date: 2021-11-05
|
||||
# .. toggle_target_removal_date: 2022-12-05
|
||||
ENABLE_DISCUSSIONS_MFE = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_discussions_mfe', __name__)
|
||||
ENABLE_DISCUSSIONS_MFE = CourseWaffleFlag(
|
||||
f"{WAFFLE_FLAG_NAMESPACE}.enable_discussions_mfe", __name__
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
This module contains various configuration settings via
|
||||
waffle switches for the discussions app.
|
||||
"""
|
||||
from django.conf import settings
|
||||
|
||||
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
|
||||
|
||||
WAFFLE_FLAG_NAMESPACE = "discussions"
|
||||
@@ -43,3 +45,31 @@ ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND = CourseWaffleFlag(
|
||||
ENABLE_NEW_STRUCTURE_DISCUSSIONS = CourseWaffleFlag(
|
||||
f"{WAFFLE_FLAG_NAMESPACE}.enable_new_structure_discussions", __name__
|
||||
)
|
||||
|
||||
# .. toggle_name: discussions.enable_forum_v2
|
||||
# .. toggle_implementation: CourseWaffleFlag
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: Waffle flag to use the forum v2 instead of v1(cs_comment_service)
|
||||
# .. toggle_use_cases: temporary, open_edx
|
||||
# .. toggle_creation_date: 2024-9-26
|
||||
# .. toggle_target_removal_date: 2025-12-05
|
||||
ENABLE_FORUM_V2 = CourseWaffleFlag(f"{WAFFLE_FLAG_NAMESPACE}.enable_forum_v2", __name__)
|
||||
|
||||
|
||||
def is_forum_v2_enabled(course_id):
|
||||
"""
|
||||
Returns whether forum V2 is enabled on the course. This is a 2-step check:
|
||||
|
||||
1. Check value of settings.DISABLE_FORUM_V2: if it exists and is true, this setting overrides any course flag.
|
||||
2. Else, check the value of the corresponding course waffle flag.
|
||||
"""
|
||||
if is_forum_v2_disabled_globally():
|
||||
return False
|
||||
return ENABLE_FORUM_V2.is_enabled(course_id)
|
||||
|
||||
|
||||
def is_forum_v2_disabled_globally() -> bool:
|
||||
"""
|
||||
Return True if DISABLE_FORUM_V2 is defined and true-ish.
|
||||
"""
|
||||
return getattr(settings, "DISABLE_FORUM_V2", False)
|
||||
|
||||
@@ -4,7 +4,9 @@ from bs4 import BeautifulSoup
|
||||
from openedx.core.djangoapps.django_comment_common.comment_client import models, settings
|
||||
|
||||
from .thread import Thread, _url_for_flag_abuse_thread, _url_for_unflag_abuse_thread
|
||||
from .utils import CommentClientRequestError, perform_request
|
||||
from .utils import CommentClientRequestError, get_course_key, perform_request
|
||||
from forum import api as forum_api
|
||||
from openedx.core.djangoapps.discussions.config.waffle import is_forum_v2_enabled
|
||||
|
||||
|
||||
class Comment(models.Model):
|
||||
@@ -68,14 +70,21 @@ class Comment(models.Model):
|
||||
url = _url_for_flag_abuse_comment(voteable.id)
|
||||
else:
|
||||
raise CommentClientRequestError("Can only flag/unflag threads or comments")
|
||||
params = {'user_id': user.id}
|
||||
response = perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='comment.abuse.flagged'
|
||||
)
|
||||
course_key = get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
if voteable.type == 'thread':
|
||||
response = forum_api.update_thread_flag(voteable.id, "flag", user.id, str(course_key))
|
||||
else:
|
||||
response = forum_api.update_comment_flag(voteable.id, "flag", user.id, str(course_key))
|
||||
else:
|
||||
params = {'user_id': user.id}
|
||||
response = perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='comment.abuse.flagged'
|
||||
)
|
||||
voteable._update_from_response(response)
|
||||
|
||||
def unFlagAbuse(self, user, voteable, removeAll):
|
||||
@@ -85,18 +94,37 @@ class Comment(models.Model):
|
||||
url = _url_for_unflag_abuse_comment(voteable.id)
|
||||
else:
|
||||
raise CommentClientRequestError("Can flag/unflag for threads or comments")
|
||||
params = {'user_id': user.id}
|
||||
course_key = get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
if voteable.type == "thread":
|
||||
response = forum_api.update_thread_flag(
|
||||
thread_id=voteable.id,
|
||||
action="unflag",
|
||||
user_id=user.id,
|
||||
update_all=bool(removeAll),
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
response = forum_api.update_comment_flag(
|
||||
comment_id=voteable.id,
|
||||
action="unflag",
|
||||
user_id=user.id,
|
||||
update_all=bool(removeAll),
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
params = {'user_id': user.id}
|
||||
|
||||
if removeAll:
|
||||
params['all'] = True
|
||||
if removeAll:
|
||||
params['all'] = True
|
||||
|
||||
response = perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='comment.abuse.unflagged'
|
||||
)
|
||||
response = perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='comment.abuse.unflagged'
|
||||
)
|
||||
voteable._update_from_response(response)
|
||||
|
||||
@property
|
||||
|
||||
@@ -7,8 +7,10 @@ from typing import Dict, Optional
|
||||
from edx_django_utils.monitoring import function_trace
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from forum import api as forum_api
|
||||
from openedx.core.djangoapps.django_comment_common.comment_client import settings
|
||||
from openedx.core.djangoapps.django_comment_common.comment_client.utils import perform_request
|
||||
from openedx.core.djangoapps.discussions.config.waffle import is_forum_v2_enabled
|
||||
|
||||
|
||||
def get_course_commentable_counts(course_key: CourseKey) -> Dict[str, Dict[str, int]]:
|
||||
@@ -29,17 +31,20 @@ def get_course_commentable_counts(course_key: CourseKey) -> Dict[str, Dict[str,
|
||||
}
|
||||
|
||||
"""
|
||||
url = f"{settings.PREFIX}/commentables/{course_key}/counts"
|
||||
response = perform_request(
|
||||
'get',
|
||||
url,
|
||||
metric_tags=[
|
||||
f"course_key:{course_key}",
|
||||
"function:get_course_commentable_counts",
|
||||
],
|
||||
metric_action='commentable_stats.retrieve',
|
||||
)
|
||||
return response
|
||||
if is_forum_v2_enabled(course_key):
|
||||
commentable_stats = forum_api.get_commentables_stats(str(course_key))
|
||||
else:
|
||||
url = f"{settings.PREFIX}/commentables/{course_key}/counts"
|
||||
commentable_stats = perform_request(
|
||||
'get',
|
||||
url,
|
||||
metric_tags=[
|
||||
f"course_key:{course_key}",
|
||||
"function:get_course_commentable_counts",
|
||||
],
|
||||
metric_action='commentable_stats.retrieve',
|
||||
)
|
||||
return commentable_stats
|
||||
|
||||
|
||||
@function_trace("get_course_user_stats")
|
||||
@@ -76,17 +81,21 @@ def get_course_user_stats(course_key: CourseKey, params: Optional[Dict] = None)
|
||||
"""
|
||||
if params is None:
|
||||
params = {}
|
||||
url = f"{settings.PREFIX}/users/{course_key}/stats"
|
||||
return perform_request(
|
||||
'get',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.course_stats',
|
||||
metric_tags=[
|
||||
f"course_key:{course_key}",
|
||||
"function:get_course_user_stats",
|
||||
],
|
||||
)
|
||||
if is_forum_v2_enabled(course_key):
|
||||
course_stats = forum_api.get_user_course_stats(str(course_key), **params)
|
||||
else:
|
||||
url = f"{settings.PREFIX}/users/{course_key}/stats"
|
||||
course_stats = perform_request(
|
||||
'get',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.course_stats',
|
||||
metric_tags=[
|
||||
f"course_key:{course_key}",
|
||||
"function:get_course_user_stats",
|
||||
],
|
||||
)
|
||||
return course_stats
|
||||
|
||||
|
||||
@function_trace("update_course_users_stats")
|
||||
@@ -100,13 +109,17 @@ def update_course_users_stats(course_key: CourseKey) -> Dict:
|
||||
Returns:
|
||||
dict: data returned by API. Contains count of users updated.
|
||||
"""
|
||||
url = f"{settings.PREFIX}/users/{course_key}/update_stats"
|
||||
return perform_request(
|
||||
'post',
|
||||
url,
|
||||
metric_action='user.update_course_stats',
|
||||
metric_tags=[
|
||||
f"course_key:{course_key}",
|
||||
"function:update_course_users_stats",
|
||||
],
|
||||
)
|
||||
if is_forum_v2_enabled(course_key):
|
||||
course_stats = forum_api.update_users_in_course(str(course_key))
|
||||
else:
|
||||
url = f"{settings.PREFIX}/users/{course_key}/update_stats"
|
||||
course_stats = perform_request(
|
||||
'post',
|
||||
url,
|
||||
metric_action='user.update_course_stats',
|
||||
metric_tags=[
|
||||
f"course_key:{course_key}",
|
||||
"function:update_course_users_stats",
|
||||
],
|
||||
)
|
||||
return course_stats
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
|
||||
import logging
|
||||
import typing as t
|
||||
|
||||
from .utils import CommentClientRequestError, extract, perform_request
|
||||
from .utils import CommentClientRequestError, extract, perform_request, get_course_key
|
||||
from forum import api as forum_api
|
||||
from openedx.core.djangoapps.discussions.config.waffle import is_forum_v2_enabled, is_forum_v2_disabled_globally
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -69,14 +72,26 @@ class Model:
|
||||
return self
|
||||
|
||||
def _retrieve(self, *args, **kwargs):
|
||||
url = self.url(action='get', params=self.attributes)
|
||||
response = perform_request(
|
||||
'get',
|
||||
url,
|
||||
self.default_retrieve_params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='model.retrieve'
|
||||
)
|
||||
course_id = self.attributes.get("course_id") or kwargs.get("course_id")
|
||||
if course_id:
|
||||
use_forumv2 = is_forum_v2_enabled(course_id)
|
||||
else:
|
||||
use_forumv2, course_id = is_forum_v2_enabled_for_comment(self.id)
|
||||
response = None
|
||||
if use_forumv2:
|
||||
if self.type == "comment":
|
||||
response = forum_api.get_parent_comment(comment_id=self.attributes["id"], course_id=course_id)
|
||||
if response is None:
|
||||
raise CommentClientRequestError("Forum v2 API call is missing")
|
||||
else:
|
||||
url = self.url(action='get', params=self.attributes)
|
||||
response = perform_request(
|
||||
'get',
|
||||
url,
|
||||
self.default_retrieve_params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='model.retrieve'
|
||||
)
|
||||
self._update_from_response(response)
|
||||
|
||||
@property
|
||||
@@ -151,33 +166,27 @@ class Model:
|
||||
"""
|
||||
self.before_save(self)
|
||||
if self.id: # if we have id already, treat this as an update
|
||||
request_params = self.updatable_attributes()
|
||||
if params:
|
||||
request_params.update(params)
|
||||
url = self.url(action='put', params=self.attributes)
|
||||
response = perform_request(
|
||||
'put',
|
||||
url,
|
||||
request_params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='model.update'
|
||||
)
|
||||
else: # otherwise, treat this as an insert
|
||||
url = self.url(action='post', params=self.attributes)
|
||||
response = perform_request(
|
||||
'post',
|
||||
url,
|
||||
self.initializable_attributes(),
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='model.insert'
|
||||
)
|
||||
response = self.handle_update(params)
|
||||
else: # otherwise, treat this as an insert
|
||||
response = self.handle_create(params)
|
||||
|
||||
self.retrieved = True
|
||||
self._update_from_response(response)
|
||||
self.after_save(self)
|
||||
|
||||
def delete(self):
|
||||
url = self.url(action='delete', params=self.attributes)
|
||||
response = perform_request('delete', url, metric_tags=self._metric_tags, metric_action='model.delete')
|
||||
course_key = get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
response = None
|
||||
if self.type == "comment":
|
||||
response = forum_api.delete_comment(comment_id=self.attributes["id"], course_id=str(course_key))
|
||||
elif self.type == "thread":
|
||||
response = forum_api.delete_thread(thread_id=self.attributes["id"], course_id=str(course_key))
|
||||
if response is None:
|
||||
raise CommentClientRequestError("Forum v2 API call is missing")
|
||||
else:
|
||||
url = self.url(action='delete', params=self.attributes)
|
||||
response = perform_request('delete', url, metric_tags=self._metric_tags, metric_action='model.delete')
|
||||
self.retrieved = True
|
||||
self._update_from_response(response)
|
||||
|
||||
@@ -208,3 +217,176 @@ class Model:
|
||||
raise CommentClientRequestError(f"Cannot perform action {action} without id") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
else: # action must be in DEFAULT_ACTIONS_WITHOUT_ID now
|
||||
return cls.url_without_id()
|
||||
|
||||
def handle_update(self, params=None):
|
||||
request_params = self.updatable_attributes()
|
||||
if params:
|
||||
request_params.update(params)
|
||||
course_id = self.attributes.get("course_id") or request_params.get("course_id")
|
||||
course_key = get_course_key(course_id)
|
||||
if is_forum_v2_enabled(course_key):
|
||||
response = None
|
||||
if self.type == "comment":
|
||||
response = self.handle_update_comment(request_params, str(course_key))
|
||||
elif self.type == "thread":
|
||||
response = self.handle_update_thread(request_params, str(course_key))
|
||||
elif self.type == "user":
|
||||
response = self.handle_update_user(request_params, str(course_key))
|
||||
if response is None:
|
||||
raise CommentClientRequestError("Forum v2 API call is missing")
|
||||
else:
|
||||
response = self.perform_http_put_request(request_params)
|
||||
return response
|
||||
|
||||
def handle_update_user(self, request_params, course_id):
|
||||
try:
|
||||
username = request_params["username"]
|
||||
external_id = str(request_params["external_id"])
|
||||
except KeyError as e:
|
||||
raise e
|
||||
response = forum_api.update_user(
|
||||
external_id,
|
||||
username=username,
|
||||
course_id=course_id,
|
||||
)
|
||||
return response
|
||||
|
||||
def handle_update_comment(self, request_params, course_id):
|
||||
request_data = {
|
||||
"comment_id": self.attributes["id"],
|
||||
"body": request_params.get("body"),
|
||||
"course_id": request_params.get("course_id"),
|
||||
"user_id": request_params.get("user_id"),
|
||||
"anonymous": request_params.get("anonymous"),
|
||||
"anonymous_to_peers": request_params.get("anonymous_to_peers"),
|
||||
"endorsed": request_params.get("endorsed"),
|
||||
"closed": request_params.get("closed"),
|
||||
"editing_user_id": request_params.get("editing_user_id"),
|
||||
"edit_reason_code": request_params.get("edit_reason_code"),
|
||||
"endorsement_user_id": request_params.get("endorsement_user_id"),
|
||||
"course_key": course_id
|
||||
}
|
||||
request_data = {k: v for k, v in request_data.items() if v is not None}
|
||||
response = forum_api.update_comment(**request_data)
|
||||
return response
|
||||
|
||||
def handle_update_thread(self, request_params, course_id):
|
||||
request_data = {
|
||||
"thread_id": self.attributes["id"],
|
||||
"title": request_params.get("title"),
|
||||
"body": request_params.get("body"),
|
||||
"course_id": request_params.get("course_id"),
|
||||
"anonymous": request_params.get("anonymous"),
|
||||
"anonymous_to_peers": request_params.get("anonymous_to_peers"),
|
||||
"closed": request_params.get("closed"),
|
||||
"commentable_id": request_params.get("commentable_id"),
|
||||
"user_id": request_params.get("user_id"),
|
||||
"editing_user_id": request_params.get("editing_user_id"),
|
||||
"pinned": request_params.get("pinned"),
|
||||
"thread_type": request_params.get("thread_type"),
|
||||
"edit_reason_code": request_params.get("edit_reason_code"),
|
||||
"close_reason_code": request_params.get("close_reason_code"),
|
||||
"closing_user_id": request_params.get("closing_user_id"),
|
||||
"endorsed": request_params.get("endorsed"),
|
||||
"course_key": course_id
|
||||
}
|
||||
request_data = {k: v for k, v in request_data.items() if v is not None}
|
||||
response = forum_api.update_thread(**request_data)
|
||||
return response
|
||||
|
||||
def perform_http_put_request(self, request_params):
|
||||
url = self.url(action="put", params=self.attributes)
|
||||
response = perform_request(
|
||||
"put",
|
||||
url,
|
||||
request_params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action="model.update",
|
||||
)
|
||||
return response
|
||||
|
||||
def perform_http_post_request(self):
|
||||
url = self.url(action="post", params=self.attributes)
|
||||
response = perform_request(
|
||||
"post",
|
||||
url,
|
||||
self.initializable_attributes(),
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action="model.insert",
|
||||
)
|
||||
return response
|
||||
|
||||
def handle_create(self, params=None):
|
||||
course_id = self.attributes.get("course_id") or params.get("course_id")
|
||||
course_key = get_course_key(course_id)
|
||||
if is_forum_v2_enabled(course_key):
|
||||
response = None
|
||||
if self.type == "comment":
|
||||
response = self.handle_create_comment(str(course_key))
|
||||
elif self.type == "thread":
|
||||
response = self.handle_create_thread(str(course_key))
|
||||
if response is None:
|
||||
raise CommentClientRequestError("Forum v2 API call is missing")
|
||||
else:
|
||||
response = self.perform_http_post_request()
|
||||
return response
|
||||
|
||||
def handle_create_comment(self, course_id):
|
||||
request_data = self.initializable_attributes()
|
||||
body = request_data["body"]
|
||||
user_id = request_data["user_id"]
|
||||
course_id = course_id or str(request_data["course_id"])
|
||||
if parent_id := self.attributes.get("parent_id"):
|
||||
response = forum_api.create_child_comment(
|
||||
parent_id,
|
||||
body,
|
||||
user_id,
|
||||
course_id,
|
||||
request_data.get("anonymous", False),
|
||||
request_data.get("anonymous_to_peers", False),
|
||||
)
|
||||
else:
|
||||
response = forum_api.create_parent_comment(
|
||||
self.attributes["thread_id"],
|
||||
body,
|
||||
user_id,
|
||||
course_id,
|
||||
request_data.get("anonymous", False),
|
||||
request_data.get("anonymous_to_peers", False),
|
||||
)
|
||||
return response
|
||||
|
||||
def handle_create_thread(self, course_id):
|
||||
request_data = self.initializable_attributes()
|
||||
response = forum_api.create_thread(
|
||||
title=request_data["title"],
|
||||
body=request_data["body"],
|
||||
course_id=course_id or str(request_data["course_id"]),
|
||||
user_id=str(request_data["user_id"]),
|
||||
anonymous=request_data.get("anonymous", False),
|
||||
anonymous_to_peers=request_data.get("anonymous_to_peers", False),
|
||||
commentable_id=request_data.get("commentable_id", "course"),
|
||||
thread_type=request_data.get("thread_type", "discussion"),
|
||||
group_id=request_data.get("group_id", None),
|
||||
context=request_data.get("context", None),
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def is_forum_v2_enabled_for_comment(comment_id: str) -> tuple[bool, t.Optional[str]]:
|
||||
"""
|
||||
Figure out whether we use forum v2 for a given comment.
|
||||
|
||||
See is_forum_v2_enabled_for_thread.
|
||||
|
||||
Return:
|
||||
|
||||
enabled (bool)
|
||||
course_id (str or None)
|
||||
"""
|
||||
if is_forum_v2_disabled_globally():
|
||||
return False, None
|
||||
|
||||
course_id = forum_api.get_course_id_by_comment(comment_id)
|
||||
course_key = get_course_key(course_id)
|
||||
return is_forum_v2_enabled(course_key), course_id
|
||||
|
||||
@@ -4,6 +4,8 @@ Subscription model is used to get users who are subscribed to the main thread/po
|
||||
import logging
|
||||
|
||||
from . import models, settings, utils
|
||||
from forum import api as forum_api
|
||||
from openedx.core.djangoapps.discussions.config.waffle import is_forum_v2_enabled
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -21,7 +23,7 @@ class Subscription(models.Model):
|
||||
base_url = f"{settings.PREFIX}/threads"
|
||||
|
||||
@classmethod
|
||||
def fetch(cls, thread_id, query_params):
|
||||
def fetch(cls, thread_id, course_id, query_params):
|
||||
"""
|
||||
Fetches the subscriptions for a given thread_id
|
||||
"""
|
||||
@@ -33,14 +35,23 @@ class Subscription(models.Model):
|
||||
params.update(
|
||||
utils.strip_blank(utils.strip_none(query_params))
|
||||
)
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
cls.url(action='get', params=params) + "/subscriptions",
|
||||
params,
|
||||
metric_tags=[],
|
||||
metric_action='subscription.get',
|
||||
paged_results=True
|
||||
)
|
||||
course_key = utils.get_course_key(course_id)
|
||||
if is_forum_v2_enabled(course_key):
|
||||
response = forum_api.get_thread_subscriptions(
|
||||
thread_id=thread_id,
|
||||
page=params["page"],
|
||||
per_page=params["per_page"],
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
cls.url(action='get', params=params) + "/subscriptions",
|
||||
params,
|
||||
metric_tags=[],
|
||||
metric_action='subscription.get',
|
||||
paged_results=True
|
||||
)
|
||||
return utils.SubscriptionsPaginatedResult(
|
||||
collection=response.get('collection', []),
|
||||
page=response.get('page', 1),
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
|
||||
import logging
|
||||
import typing as t
|
||||
|
||||
from eventtracking import tracker
|
||||
|
||||
from . import models, settings, utils
|
||||
from forum import api as forum_api
|
||||
from openedx.core.djangoapps.discussions.config.waffle import is_forum_v2_enabled, is_forum_v2_disabled_globally
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -59,14 +62,35 @@ class Thread(models.Model):
|
||||
url = cls.url(action='get_all', params=utils.extract(params, 'commentable_id'))
|
||||
if params.get('commentable_id'):
|
||||
del params['commentable_id']
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
params,
|
||||
metric_tags=['course_id:{}'.format(query_params['course_id'])],
|
||||
metric_action='thread.search',
|
||||
paged_results=True
|
||||
)
|
||||
|
||||
if is_forum_v2_enabled(utils.get_course_key(query_params['course_id'])):
|
||||
if query_params.get('text'):
|
||||
search_params = utils.strip_none(params)
|
||||
if user_id := search_params.get('user_id'):
|
||||
search_params['user_id'] = str(user_id)
|
||||
if group_ids := search_params.get('group_ids'):
|
||||
search_params['group_ids'] = [int(group_id) for group_id in group_ids.split(',')]
|
||||
elif group_id := search_params.get('group_id'):
|
||||
search_params['group_ids'] = [int(group_id)]
|
||||
search_params.pop('group_id', None)
|
||||
if commentable_ids := search_params.get('commentable_ids'):
|
||||
search_params['commentable_ids'] = commentable_ids.split(',')
|
||||
elif commentable_id := search_params.get('commentable_id'):
|
||||
search_params['commentable_ids'] = [commentable_id]
|
||||
search_params.pop('commentable_id', None)
|
||||
response = forum_api.search_threads(**search_params)
|
||||
else:
|
||||
response = forum_api.get_user_threads(**params)
|
||||
else:
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
params,
|
||||
metric_tags=['course_id:{}'.format(query_params['course_id'])],
|
||||
metric_action='thread.search',
|
||||
paged_results=True
|
||||
)
|
||||
|
||||
if query_params.get('text'):
|
||||
search_query = query_params['text']
|
||||
course_id = query_params['course_id']
|
||||
@@ -148,14 +172,27 @@ class Thread(models.Model):
|
||||
'merge_question_type_responses': kwargs.get('merge_question_type_responses', False)
|
||||
}
|
||||
request_params = utils.strip_none(request_params)
|
||||
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
request_params,
|
||||
metric_action='model.retrieve',
|
||||
metric_tags=self._metric_tags
|
||||
)
|
||||
course_id = kwargs.get("course_id")
|
||||
if course_id:
|
||||
use_forumv2 = is_forum_v2_enabled(course_id)
|
||||
else:
|
||||
use_forumv2, course_id = is_forum_v2_enabled_for_thread(self.id)
|
||||
if use_forumv2:
|
||||
if user_id := request_params.get('user_id'):
|
||||
request_params['user_id'] = str(user_id)
|
||||
response = forum_api.get_thread(
|
||||
thread_id=self.id,
|
||||
params=request_params,
|
||||
course_id=course_id,
|
||||
)
|
||||
else:
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
request_params,
|
||||
metric_action='model.retrieve',
|
||||
metric_tags=self._metric_tags
|
||||
)
|
||||
self._update_from_response(response)
|
||||
|
||||
def flagAbuse(self, user, voteable):
|
||||
@@ -163,14 +200,18 @@ class Thread(models.Model):
|
||||
url = _url_for_flag_abuse_thread(voteable.id)
|
||||
else:
|
||||
raise utils.CommentClientRequestError("Can only flag/unflag threads or comments")
|
||||
params = {'user_id': user.id}
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_action='thread.abuse.flagged',
|
||||
metric_tags=self._metric_tags
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
response = forum_api.update_thread_flag(voteable.id, "flag", user.id, str(course_key))
|
||||
else:
|
||||
params = {'user_id': user.id}
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_action='thread.abuse.flagged',
|
||||
metric_tags=self._metric_tags
|
||||
)
|
||||
voteable._update_from_response(response)
|
||||
|
||||
def unFlagAbuse(self, user, voteable, removeAll):
|
||||
@@ -178,42 +219,68 @@ class Thread(models.Model):
|
||||
url = _url_for_unflag_abuse_thread(voteable.id)
|
||||
else:
|
||||
raise utils.CommentClientRequestError("Can only flag/unflag for threads or comments")
|
||||
params = {'user_id': user.id}
|
||||
#if you're an admin, when you unflag, remove ALL flags
|
||||
if removeAll:
|
||||
params['all'] = True
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
response = forum_api.update_thread_flag(
|
||||
thread_id=voteable.id,
|
||||
action="unflag",
|
||||
user_id=user.id,
|
||||
update_all=bool(removeAll),
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
params = {'user_id': user.id}
|
||||
#if you're an admin, when you unflag, remove ALL flags
|
||||
if removeAll:
|
||||
params['all'] = True
|
||||
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='thread.abuse.unflagged'
|
||||
)
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='thread.abuse.unflagged'
|
||||
)
|
||||
voteable._update_from_response(response)
|
||||
|
||||
def pin(self, user, thread_id):
|
||||
url = _url_for_pin_thread(thread_id)
|
||||
params = {'user_id': user.id}
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='thread.pin'
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
response = forum_api.pin_thread(
|
||||
user_id=user.id,
|
||||
thread_id=thread_id,
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
url = _url_for_pin_thread(thread_id)
|
||||
params = {'user_id': user.id}
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='thread.pin'
|
||||
)
|
||||
self._update_from_response(response)
|
||||
|
||||
def un_pin(self, user, thread_id):
|
||||
url = _url_for_un_pin_thread(thread_id)
|
||||
params = {'user_id': user.id}
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='thread.unpin'
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
response = forum_api.unpin_thread(
|
||||
user_id=user.id,
|
||||
thread_id=thread_id,
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
url = _url_for_un_pin_thread(thread_id)
|
||||
params = {'user_id': user.id}
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_tags=self._metric_tags,
|
||||
metric_action='thread.unpin'
|
||||
)
|
||||
self._update_from_response(response)
|
||||
|
||||
|
||||
@@ -231,3 +298,28 @@ def _url_for_pin_thread(thread_id):
|
||||
|
||||
def _url_for_un_pin_thread(thread_id):
|
||||
return f"{settings.PREFIX}/threads/{thread_id}/unpin"
|
||||
|
||||
|
||||
def is_forum_v2_enabled_for_thread(thread_id: str) -> tuple[bool, t.Optional[str]]:
|
||||
"""
|
||||
Figure out whether we use forum v2 for a given thread.
|
||||
|
||||
This is a complex affair... First, we check the value of the DISABLE_FORUM_V2
|
||||
setting, which overrides everything. If this setting does not exist, then we need to
|
||||
find the course ID that corresponds to the thread ID. Then, we return the value of
|
||||
the course waffle flag for this course ID.
|
||||
|
||||
Note that to fetch the course ID associated to a thread ID, we need to connect both
|
||||
to mongodb and mysql. As a consequence, when forum v2 needs adequate connection
|
||||
strings for both backends.
|
||||
|
||||
Return:
|
||||
|
||||
enabled (bool)
|
||||
course_id (str or None)
|
||||
"""
|
||||
if is_forum_v2_disabled_globally():
|
||||
return False, None
|
||||
course_id = forum_api.get_course_id_by_thread(thread_id)
|
||||
course_key = utils.get_course_key(course_id)
|
||||
return is_forum_v2_enabled(course_key), course_id
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# pylint: disable=missing-docstring,protected-access
|
||||
""" User model wrapper for comment service"""
|
||||
|
||||
|
||||
from . import models, settings, utils
|
||||
from forum import api as forum_api
|
||||
from forum.utils import ForumV2RequestError, str_to_bool
|
||||
from openedx.core.djangoapps.discussions.config.waffle import is_forum_v2_enabled
|
||||
|
||||
|
||||
class User(models.Model):
|
||||
@@ -34,34 +36,55 @@ class User(models.Model):
|
||||
"""
|
||||
Calls cs_comments_service to mark thread as read for the user
|
||||
"""
|
||||
params = {'source_type': source.type, 'source_id': source.id}
|
||||
utils.perform_request(
|
||||
'post',
|
||||
_url_for_read(self.id),
|
||||
params,
|
||||
metric_action='user.read',
|
||||
metric_tags=self._metric_tags + [f'target.type:{source.type}'],
|
||||
)
|
||||
course_id = self.attributes.get("course_id")
|
||||
course_key = utils.get_course_key(course_id)
|
||||
if is_forum_v2_enabled(course_key):
|
||||
forum_api.mark_thread_as_read(self.id, source.id, course_id=str(course_id))
|
||||
else:
|
||||
params = {'source_type': source.type, 'source_id': source.id}
|
||||
utils.perform_request(
|
||||
'post',
|
||||
_url_for_read(self.id),
|
||||
params,
|
||||
metric_action='user.read',
|
||||
metric_tags=self._metric_tags + [f'target.type:{source.type}'],
|
||||
)
|
||||
|
||||
def follow(self, source):
|
||||
params = {'source_type': source.type, 'source_id': source.id}
|
||||
utils.perform_request(
|
||||
'post',
|
||||
_url_for_subscription(self.id),
|
||||
params,
|
||||
metric_action='user.follow',
|
||||
metric_tags=self._metric_tags + [f'target.type:{source.type}'],
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
forum_api.create_subscription(
|
||||
user_id=self.id,
|
||||
source_id=source.id,
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
params = {'source_type': source.type, 'source_id': source.id}
|
||||
utils.perform_request(
|
||||
'post',
|
||||
_url_for_subscription(self.id),
|
||||
params,
|
||||
metric_action='user.follow',
|
||||
metric_tags=self._metric_tags + [f'target.type:{source.type}'],
|
||||
)
|
||||
|
||||
def unfollow(self, source):
|
||||
params = {'source_type': source.type, 'source_id': source.id}
|
||||
utils.perform_request(
|
||||
'delete',
|
||||
_url_for_subscription(self.id),
|
||||
params,
|
||||
metric_action='user.unfollow',
|
||||
metric_tags=self._metric_tags + [f'target.type:{source.type}'],
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
forum_api.delete_subscription(
|
||||
user_id=self.id,
|
||||
source_id=source.id,
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
params = {'source_type': source.type, 'source_id': source.id}
|
||||
utils.perform_request(
|
||||
'delete',
|
||||
_url_for_subscription(self.id),
|
||||
params,
|
||||
metric_action='user.unfollow',
|
||||
metric_tags=self._metric_tags + [f'target.type:{source.type}'],
|
||||
)
|
||||
|
||||
def vote(self, voteable, value):
|
||||
if voteable.type == 'thread':
|
||||
@@ -70,14 +93,31 @@ class User(models.Model):
|
||||
url = _url_for_vote_comment(voteable.id)
|
||||
else:
|
||||
raise utils.CommentClientRequestError("Can only vote / unvote for threads or comments")
|
||||
params = {'user_id': self.id, 'value': value}
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.vote',
|
||||
metric_tags=self._metric_tags + [f'target.type:{voteable.type}'],
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
if voteable.type == 'thread':
|
||||
response = forum_api.update_thread_votes(
|
||||
thread_id=voteable.id,
|
||||
user_id=self.id,
|
||||
value=value,
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
response = forum_api.update_comment_votes(
|
||||
comment_id=voteable.id,
|
||||
user_id=self.id,
|
||||
value=value,
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
params = {'user_id': self.id, 'value': value}
|
||||
response = utils.perform_request(
|
||||
'put',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.vote',
|
||||
metric_tags=self._metric_tags + [f'target.type:{voteable.type}'],
|
||||
)
|
||||
voteable._update_from_response(response)
|
||||
|
||||
def unvote(self, voteable):
|
||||
@@ -87,14 +127,29 @@ class User(models.Model):
|
||||
url = _url_for_vote_comment(voteable.id)
|
||||
else:
|
||||
raise utils.CommentClientRequestError("Can only vote / unvote for threads or comments")
|
||||
params = {'user_id': self.id}
|
||||
response = utils.perform_request(
|
||||
'delete',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.unvote',
|
||||
metric_tags=self._metric_tags + [f'target.type:{voteable.type}'],
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
if voteable.type == 'thread':
|
||||
response = forum_api.delete_thread_vote(
|
||||
thread_id=voteable.id,
|
||||
user_id=self.id,
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
response = forum_api.delete_comment_vote(
|
||||
comment_id=voteable.id,
|
||||
user_id=self.id,
|
||||
course_id=str(course_key)
|
||||
)
|
||||
else:
|
||||
params = {'user_id': self.id}
|
||||
response = utils.perform_request(
|
||||
'delete',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.unvote',
|
||||
metric_tags=self._metric_tags + [f'target.type:{voteable.type}'],
|
||||
)
|
||||
voteable._update_from_response(response)
|
||||
|
||||
def active_threads(self, query_params=None):
|
||||
@@ -105,14 +160,28 @@ class User(models.Model):
|
||||
url = _url_for_user_active_threads(self.id)
|
||||
params = {'course_id': str(self.course_id)}
|
||||
params.update(query_params)
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.active_threads',
|
||||
metric_tags=self._metric_tags,
|
||||
paged_results=True,
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
if user_id := params.get("user_id"):
|
||||
params["user_id"] = str(user_id)
|
||||
if page := params.get("page"):
|
||||
params["page"] = int(page)
|
||||
if per_page := params.get("per_page"):
|
||||
params["per_page"] = int(per_page)
|
||||
if count_flagged := params.get("count_flagged", False):
|
||||
params["count_flagged"] = str_to_bool(count_flagged)
|
||||
if not params.get("course_id"):
|
||||
params["course_id"] = str(course_key)
|
||||
response = forum_api.get_user_active_threads(**params)
|
||||
else:
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.active_threads',
|
||||
metric_tags=self._metric_tags,
|
||||
paged_results=True,
|
||||
)
|
||||
return response.get('collection', []), response.get('page', 1), response.get('num_pages', 1)
|
||||
|
||||
def subscribed_threads(self, query_params=None):
|
||||
@@ -125,14 +194,28 @@ class User(models.Model):
|
||||
url = _url_for_user_subscribed_threads(self.id)
|
||||
params = {'course_id': str(self.course_id)}
|
||||
params.update(query_params)
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.subscribed_threads',
|
||||
metric_tags=self._metric_tags,
|
||||
paged_results=True
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
if user_id := params.get("user_id"):
|
||||
params["user_id"] = str(user_id)
|
||||
if page := params.get("page"):
|
||||
params["page"] = int(page)
|
||||
if per_page := params.get("per_page"):
|
||||
params["per_page"] = int(per_page)
|
||||
if count_flagged := params.get("count_flagged", False):
|
||||
params["count_flagged"] = str_to_bool(count_flagged)
|
||||
if not params.get("course_id"):
|
||||
params["course_id"] = str(course_key)
|
||||
response = forum_api.get_user_threads(**params)
|
||||
else:
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
params,
|
||||
metric_action='user.subscribed_threads',
|
||||
metric_tags=self._metric_tags,
|
||||
paged_results=True
|
||||
)
|
||||
return utils.CommentClientPaginatedResult(
|
||||
collection=response.get('collection', []),
|
||||
page=response.get('page', 1),
|
||||
@@ -144,23 +227,39 @@ class User(models.Model):
|
||||
url = self.url(action='get', params=self.attributes)
|
||||
retrieve_params = self.default_retrieve_params.copy()
|
||||
retrieve_params.update(kwargs)
|
||||
|
||||
if self.attributes.get('course_id'):
|
||||
retrieve_params['course_id'] = str(self.course_id)
|
||||
if self.attributes.get('group_id'):
|
||||
retrieve_params['group_id'] = self.group_id
|
||||
try:
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
retrieve_params,
|
||||
metric_action='model.retrieve',
|
||||
metric_tags=self._metric_tags,
|
||||
)
|
||||
except utils.CommentClientRequestError as e:
|
||||
if e.status_code == 404:
|
||||
# attempt to gracefully recover from a previous failure
|
||||
# to sync this user to the comments service.
|
||||
self.save()
|
||||
|
||||
# course key -> id conversation
|
||||
course_id = retrieve_params.get('course_id')
|
||||
if course_id:
|
||||
course_id = str(course_id)
|
||||
retrieve_params['course_id'] = course_id
|
||||
course_key = utils.get_course_key(course_id)
|
||||
|
||||
if is_forum_v2_enabled(course_key):
|
||||
group_ids = [retrieve_params['group_id']] if 'group_id' in retrieve_params else []
|
||||
is_complete = retrieve_params['complete']
|
||||
try:
|
||||
response = forum_api.get_user(
|
||||
self.attributes["id"],
|
||||
group_ids=group_ids,
|
||||
course_id=course_id,
|
||||
complete=is_complete
|
||||
)
|
||||
except ForumV2RequestError as e:
|
||||
self.save({"course_id": course_id})
|
||||
response = forum_api.get_user(
|
||||
self.attributes["id"],
|
||||
group_ids=group_ids,
|
||||
course_id=course_id,
|
||||
complete=is_complete
|
||||
)
|
||||
else:
|
||||
try:
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
@@ -168,33 +267,52 @@ class User(models.Model):
|
||||
metric_action='model.retrieve',
|
||||
metric_tags=self._metric_tags,
|
||||
)
|
||||
else:
|
||||
raise
|
||||
except utils.CommentClientRequestError as e:
|
||||
if e.status_code == 404:
|
||||
# attempt to gracefully recover from a previous failure
|
||||
# to sync this user to the comments service.
|
||||
self.save()
|
||||
response = utils.perform_request(
|
||||
'get',
|
||||
url,
|
||||
retrieve_params,
|
||||
metric_action='model.retrieve',
|
||||
metric_tags=self._metric_tags,
|
||||
)
|
||||
else:
|
||||
raise
|
||||
self._update_from_response(response)
|
||||
|
||||
def retire(self, retired_username):
|
||||
url = _url_for_retire(self.id)
|
||||
params = {'retired_username': retired_username}
|
||||
|
||||
utils.perform_request(
|
||||
'post',
|
||||
url,
|
||||
params,
|
||||
raw=True,
|
||||
metric_action='user.retire',
|
||||
metric_tags=self._metric_tags
|
||||
)
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
forum_api.retire_user(user_id=self.id, retired_username=retired_username, course_id=str(course_key))
|
||||
else:
|
||||
url = _url_for_retire(self.id)
|
||||
params = {'retired_username': retired_username}
|
||||
utils.perform_request(
|
||||
'post',
|
||||
url,
|
||||
params,
|
||||
raw=True,
|
||||
metric_action='user.retire',
|
||||
metric_tags=self._metric_tags
|
||||
)
|
||||
|
||||
def replace_username(self, new_username):
|
||||
url = _url_for_username_replacement(self.id)
|
||||
params = {"new_username": new_username}
|
||||
course_key = utils.get_course_key(self.attributes.get("course_id"))
|
||||
if is_forum_v2_enabled(course_key):
|
||||
forum_api.update_username(user_id=self.id, new_username=new_username, course_id=str(course_key))
|
||||
else:
|
||||
url = _url_for_username_replacement(self.id)
|
||||
params = {"new_username": new_username}
|
||||
|
||||
utils.perform_request(
|
||||
'post',
|
||||
url,
|
||||
params,
|
||||
raw=True,
|
||||
)
|
||||
utils.perform_request(
|
||||
'post',
|
||||
url,
|
||||
params,
|
||||
raw=True,
|
||||
)
|
||||
|
||||
|
||||
def _url_for_vote_comment(comment_id):
|
||||
|
||||
@@ -7,6 +7,7 @@ from uuid import uuid4
|
||||
|
||||
import requests
|
||||
from django.utils.translation import get_language
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from .settings import SERVICE_HOST as COMMENTS_SERVICE
|
||||
|
||||
@@ -167,3 +168,19 @@ def check_forum_heartbeat():
|
||||
return 'forum', False, res.get('check', 'Forum heartbeat failed')
|
||||
except Exception as fail:
|
||||
return 'forum', False, str(fail)
|
||||
|
||||
|
||||
def get_course_key(course_id: CourseKey | str | None) -> CourseKey | None:
|
||||
"""
|
||||
Returns a CourseKey if the provided course_id is a valid string representation of a CourseKey.
|
||||
If course_id is None or already a CourseKey object, it returns the course_id as is.
|
||||
Args:
|
||||
course_id (CourseKey | str | None): The course ID to be converted.
|
||||
Returns:
|
||||
CourseKey | None: The corresponding CourseKey object or None if the input is None.
|
||||
Raises:
|
||||
KeyError: If course_id is not a valid string representation of a CourseKey.
|
||||
"""
|
||||
if course_id and isinstance(course_id, str):
|
||||
course_id = CourseKey.from_string(course_id)
|
||||
return course_id
|
||||
|
||||
@@ -57,7 +57,9 @@ backoff==1.10.0
|
||||
bcrypt==4.2.1
|
||||
# via paramiko
|
||||
beautifulsoup4==4.12.3
|
||||
# via pynliner
|
||||
# via
|
||||
# openedx-forum
|
||||
# pynliner
|
||||
billiard==4.2.1
|
||||
# via celery
|
||||
bleach[css]==6.2.0
|
||||
@@ -234,6 +236,7 @@ django==4.2.17
|
||||
# openedx-django-wiki
|
||||
# openedx-events
|
||||
# openedx-filters
|
||||
# openedx-forum
|
||||
# openedx-learning
|
||||
# ora2
|
||||
# social-auth-app-django
|
||||
@@ -383,6 +386,7 @@ djangorestframework==3.14.0
|
||||
# edx-organizations
|
||||
# edx-proctoring
|
||||
# edx-submissions
|
||||
# openedx-forum
|
||||
# openedx-learning
|
||||
# ora2
|
||||
# super-csv
|
||||
@@ -516,7 +520,9 @@ edx-rest-api-client==6.0.0
|
||||
# edx-enterprise
|
||||
# edx-proctoring
|
||||
edx-search==4.1.1
|
||||
# via -r requirements/edx/kernel.in
|
||||
# via
|
||||
# -r requirements/edx/kernel.in
|
||||
# openedx-forum
|
||||
edx-sga==0.25.0
|
||||
# via -r requirements/edx/bundled.in
|
||||
edx-submissions==3.8.3
|
||||
@@ -549,6 +555,7 @@ elasticsearch==7.9.1
|
||||
# via
|
||||
# -c requirements/edx/../common_constraints.txt
|
||||
# edx-search
|
||||
# openedx-forum
|
||||
enmerkar==0.7.1
|
||||
# via enmerkar-underscore
|
||||
enmerkar-underscore==2.3.1
|
||||
@@ -774,7 +781,9 @@ multidict==6.1.0
|
||||
# aiohttp
|
||||
# yarl
|
||||
mysqlclient==2.2.6
|
||||
# via -r requirements/edx/kernel.in
|
||||
# via
|
||||
# -r requirements/edx/kernel.in
|
||||
# openedx-forum
|
||||
newrelic==10.3.1
|
||||
# via edx-django-utils
|
||||
nh3==0.2.19
|
||||
@@ -804,7 +813,9 @@ openai==0.28.1
|
||||
# -c requirements/edx/../constraints.txt
|
||||
# edx-enterprise
|
||||
openedx-atlas==0.6.2
|
||||
# via -r requirements/edx/kernel.in
|
||||
# via
|
||||
# -r requirements/edx/kernel.in
|
||||
# openedx-forum
|
||||
openedx-calc==4.0.1
|
||||
# via -r requirements/edx/kernel.in
|
||||
openedx-django-pyfs==3.7.0
|
||||
@@ -830,6 +841,8 @@ openedx-filters==1.11.0
|
||||
# -r requirements/edx/kernel.in
|
||||
# lti-consumer-xblock
|
||||
# ora2
|
||||
openedx-forum==0.1.5
|
||||
# via -r requirements/edx/kernel.in
|
||||
openedx-learning==0.18.1
|
||||
# via
|
||||
# -c requirements/edx/../constraints.txt
|
||||
@@ -966,6 +979,7 @@ pymongo==4.4.0
|
||||
# edx-opaque-keys
|
||||
# event-tracking
|
||||
# mongoengine
|
||||
# openedx-forum
|
||||
# openedx-mongodbproxy
|
||||
pynacl==1.5.0
|
||||
# via
|
||||
@@ -1075,6 +1089,7 @@ requests==2.32.3
|
||||
# mailsnake
|
||||
# meilisearch
|
||||
# openai
|
||||
# openedx-forum
|
||||
# optimizely-sdk
|
||||
# pyjwkest
|
||||
# pylti1p3
|
||||
|
||||
@@ -122,6 +122,7 @@ beautifulsoup4==4.12.3
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
# -r requirements/edx/testing.txt
|
||||
# openedx-forum
|
||||
# pydata-sphinx-theme
|
||||
# pynliner
|
||||
billiard==4.2.1
|
||||
@@ -406,6 +407,7 @@ django==4.2.17
|
||||
# openedx-django-wiki
|
||||
# openedx-events
|
||||
# openedx-filters
|
||||
# openedx-forum
|
||||
# openedx-learning
|
||||
# ora2
|
||||
# social-auth-app-django
|
||||
@@ -619,6 +621,7 @@ djangorestframework==3.14.0
|
||||
# edx-organizations
|
||||
# edx-proctoring
|
||||
# edx-submissions
|
||||
# openedx-forum
|
||||
# openedx-learning
|
||||
# ora2
|
||||
# super-csv
|
||||
@@ -815,6 +818,7 @@ edx-search==4.1.1
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
# -r requirements/edx/testing.txt
|
||||
# openedx-forum
|
||||
edx-sga==0.25.0
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
@@ -861,6 +865,7 @@ elasticsearch==7.9.1
|
||||
# -r requirements/edx/doc.txt
|
||||
# -r requirements/edx/testing.txt
|
||||
# edx-search
|
||||
# openedx-forum
|
||||
enmerkar==0.7.1
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
@@ -1304,6 +1309,7 @@ mysqlclient==2.2.6
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
# -r requirements/edx/testing.txt
|
||||
# openedx-forum
|
||||
newrelic==10.3.1
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
@@ -1354,6 +1360,7 @@ openedx-atlas==0.6.2
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
# -r requirements/edx/testing.txt
|
||||
# openedx-forum
|
||||
openedx-calc==4.0.1
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
@@ -1389,6 +1396,10 @@ openedx-filters==1.11.0
|
||||
# -r requirements/edx/testing.txt
|
||||
# lti-consumer-xblock
|
||||
# ora2
|
||||
openedx-forum==0.1.5
|
||||
# via
|
||||
# -r requirements/edx/doc.txt
|
||||
# -r requirements/edx/testing.txt
|
||||
openedx-learning==0.18.1
|
||||
# via
|
||||
# -c requirements/edx/../constraints.txt
|
||||
@@ -1659,6 +1670,7 @@ pymongo==4.4.0
|
||||
# edx-opaque-keys
|
||||
# event-tracking
|
||||
# mongoengine
|
||||
# openedx-forum
|
||||
# openedx-mongodbproxy
|
||||
pynacl==1.5.0
|
||||
# via
|
||||
@@ -1855,6 +1867,7 @@ requests==2.32.3
|
||||
# mailsnake
|
||||
# meilisearch
|
||||
# openai
|
||||
# openedx-forum
|
||||
# optimizely-sdk
|
||||
# pact-python
|
||||
# pyjwkest
|
||||
|
||||
@@ -89,6 +89,7 @@ bcrypt==4.2.1
|
||||
beautifulsoup4==4.12.3
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
# openedx-forum
|
||||
# pydata-sphinx-theme
|
||||
# pynliner
|
||||
billiard==4.2.1
|
||||
@@ -292,6 +293,7 @@ django==4.2.17
|
||||
# openedx-django-wiki
|
||||
# openedx-events
|
||||
# openedx-filters
|
||||
# openedx-forum
|
||||
# openedx-learning
|
||||
# ora2
|
||||
# social-auth-app-django
|
||||
@@ -457,6 +459,7 @@ djangorestframework==3.14.0
|
||||
# edx-organizations
|
||||
# edx-proctoring
|
||||
# edx-submissions
|
||||
# openedx-forum
|
||||
# openedx-learning
|
||||
# ora2
|
||||
# super-csv
|
||||
@@ -601,7 +604,9 @@ edx-rest-api-client==6.0.0
|
||||
# edx-enterprise
|
||||
# edx-proctoring
|
||||
edx-search==4.1.1
|
||||
# via -r requirements/edx/base.txt
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
# openedx-forum
|
||||
edx-sga==0.25.0
|
||||
# via -r requirements/edx/base.txt
|
||||
edx-submissions==3.8.3
|
||||
@@ -637,6 +642,7 @@ elasticsearch==7.9.1
|
||||
# -c requirements/edx/../common_constraints.txt
|
||||
# -r requirements/edx/base.txt
|
||||
# edx-search
|
||||
# openedx-forum
|
||||
enmerkar==0.7.1
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
@@ -937,7 +943,9 @@ multidict==6.1.0
|
||||
# aiohttp
|
||||
# yarl
|
||||
mysqlclient==2.2.6
|
||||
# via -r requirements/edx/base.txt
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
# openedx-forum
|
||||
newrelic==10.3.1
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
@@ -973,7 +981,9 @@ openai==0.28.1
|
||||
# -r requirements/edx/base.txt
|
||||
# edx-enterprise
|
||||
openedx-atlas==0.6.2
|
||||
# via -r requirements/edx/base.txt
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
# openedx-forum
|
||||
openedx-calc==4.0.1
|
||||
# via -r requirements/edx/base.txt
|
||||
openedx-django-pyfs==3.7.0
|
||||
@@ -1000,6 +1010,8 @@ openedx-filters==1.11.0
|
||||
# -r requirements/edx/base.txt
|
||||
# lti-consumer-xblock
|
||||
# ora2
|
||||
openedx-forum==0.1.5
|
||||
# via -r requirements/edx/base.txt
|
||||
openedx-learning==0.18.1
|
||||
# via
|
||||
# -c requirements/edx/../constraints.txt
|
||||
@@ -1171,6 +1183,7 @@ pymongo==4.4.0
|
||||
# edx-opaque-keys
|
||||
# event-tracking
|
||||
# mongoengine
|
||||
# openedx-forum
|
||||
# openedx-mongodbproxy
|
||||
pynacl==1.5.0
|
||||
# via
|
||||
@@ -1296,6 +1309,7 @@ requests==2.32.3
|
||||
# mailsnake
|
||||
# meilisearch
|
||||
# openai
|
||||
# openedx-forum
|
||||
# optimizely-sdk
|
||||
# pyjwkest
|
||||
# pylti1p3
|
||||
|
||||
@@ -119,6 +119,7 @@ openedx-calc # Library supporting mathematical calculatio
|
||||
openedx-django-require
|
||||
openedx-events # Open edX Events from Hooks Extension Framework (OEP-50)
|
||||
openedx-filters # Open edX Filters from Hooks Extension Framework (OEP-50)
|
||||
openedx-forum # Open edX forum v2 application
|
||||
openedx-learning # Open edX Learning core (experimental)
|
||||
openedx-mongodbproxy
|
||||
openedx-django-wiki
|
||||
|
||||
@@ -87,6 +87,7 @@ beautifulsoup4==4.12.3
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
# -r requirements/edx/testing.in
|
||||
# openedx-forum
|
||||
# pynliner
|
||||
billiard==4.2.1
|
||||
# via
|
||||
@@ -318,6 +319,7 @@ django==4.2.17
|
||||
# openedx-django-wiki
|
||||
# openedx-events
|
||||
# openedx-filters
|
||||
# openedx-forum
|
||||
# openedx-learning
|
||||
# ora2
|
||||
# social-auth-app-django
|
||||
@@ -483,6 +485,7 @@ djangorestframework==3.14.0
|
||||
# edx-organizations
|
||||
# edx-proctoring
|
||||
# edx-submissions
|
||||
# openedx-forum
|
||||
# openedx-learning
|
||||
# ora2
|
||||
# super-csv
|
||||
@@ -624,7 +627,9 @@ edx-rest-api-client==6.0.0
|
||||
# edx-enterprise
|
||||
# edx-proctoring
|
||||
edx-search==4.1.1
|
||||
# via -r requirements/edx/base.txt
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
# openedx-forum
|
||||
edx-sga==0.25.0
|
||||
# via -r requirements/edx/base.txt
|
||||
edx-submissions==3.8.3
|
||||
@@ -660,6 +665,7 @@ elasticsearch==7.9.1
|
||||
# -c requirements/edx/../common_constraints.txt
|
||||
# -r requirements/edx/base.txt
|
||||
# edx-search
|
||||
# openedx-forum
|
||||
enmerkar==0.7.1
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
@@ -982,7 +988,9 @@ multidict==6.1.0
|
||||
# aiohttp
|
||||
# yarl
|
||||
mysqlclient==2.2.6
|
||||
# via -r requirements/edx/base.txt
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
# openedx-forum
|
||||
newrelic==10.3.1
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
@@ -1018,7 +1026,9 @@ openai==0.28.1
|
||||
# -r requirements/edx/base.txt
|
||||
# edx-enterprise
|
||||
openedx-atlas==0.6.2
|
||||
# via -r requirements/edx/base.txt
|
||||
# via
|
||||
# -r requirements/edx/base.txt
|
||||
# openedx-forum
|
||||
openedx-calc==4.0.1
|
||||
# via -r requirements/edx/base.txt
|
||||
openedx-django-pyfs==3.7.0
|
||||
@@ -1045,6 +1055,8 @@ openedx-filters==1.11.0
|
||||
# -r requirements/edx/base.txt
|
||||
# lti-consumer-xblock
|
||||
# ora2
|
||||
openedx-forum==0.1.5
|
||||
# via -r requirements/edx/base.txt
|
||||
openedx-learning==0.18.1
|
||||
# via
|
||||
# -c requirements/edx/../constraints.txt
|
||||
@@ -1251,6 +1263,7 @@ pymongo==4.4.0
|
||||
# edx-opaque-keys
|
||||
# event-tracking
|
||||
# mongoengine
|
||||
# openedx-forum
|
||||
# openedx-mongodbproxy
|
||||
pynacl==1.5.0
|
||||
# via
|
||||
@@ -1407,6 +1420,7 @@ requests==2.32.3
|
||||
# mailsnake
|
||||
# meilisearch
|
||||
# openai
|
||||
# openedx-forum
|
||||
# optimizely-sdk
|
||||
# pact-python
|
||||
# pyjwkest
|
||||
|
||||
Reference in New Issue
Block a user