Commit 7ce579c2 by Greg Price

Add group fields to thread list endpoint

parent b1a9d334
...@@ -15,7 +15,7 @@ from django_comment_common.models import ( ...@@ -15,7 +15,7 @@ from django_comment_common.models import (
) )
from lms.lib.comment_client.thread import Thread from lms.lib.comment_client.thread import Thread
from lms.lib.comment_client.user import User from lms.lib.comment_client.user import User
from openedx.core.djangoapps.course_groups.cohorts import get_cohort_id from openedx.core.djangoapps.course_groups.cohorts import get_cohort_id, get_cohort_names
def get_course_topics(course, user): def get_course_topics(course, user):
...@@ -77,11 +77,24 @@ def get_course_topics(course, user): ...@@ -77,11 +77,24 @@ def get_course_topics(course, user):
} }
def _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids): def _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids, group_ids_to_names):
""" """
Convert a thread data dict from the comment_client format (which is a direct Convert a thread data dict from the comment_client format (which is a direct
representation of the format returned by the comments service) to the format representation of the format returned by the comments service) to the format
used in this API used in this API
Arguments:
thread (comment_client.thread.Thread): The thread to convert
cc_user (comment_client.user.User): The comment_client representation of
the requesting user
staff_user_ids (set): The set of user ids for users with the Moderator or
Administrator role in the course
ta_user_ids (set): The set of user ids for users with the Community TA
role in the course
group_ids_to_names (dict): A mapping of group ids to names
Returns:
dict: The discussion_api format representation of the thread.
""" """
is_anonymous = ( is_anonymous = (
thread["anonymous"] or thread["anonymous"] or
...@@ -95,6 +108,7 @@ def _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids): ...@@ -95,6 +108,7 @@ def _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids):
for key in [ for key in [
"id", "id",
"course_id", "course_id",
"group_id",
"created_at", "created_at",
"updated_at", "updated_at",
"type", "type",
...@@ -105,6 +119,7 @@ def _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids): ...@@ -105,6 +119,7 @@ def _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids):
} }
ret.update({ ret.update({
"topic_id": thread["commentable_id"], "topic_id": thread["commentable_id"],
"group_name": group_ids_to_names.get(thread["group_id"]),
"author": None if is_anonymous else thread["username"], "author": None if is_anonymous else thread["username"],
"author_label": ( "author_label": (
None if is_anonymous else None if is_anonymous else
...@@ -123,14 +138,14 @@ def _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids): ...@@ -123,14 +138,14 @@ def _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids):
return ret return ret
def get_thread_list(request, course_key, page, page_size): def get_thread_list(request, course, page, page_size):
""" """
Return the list of all discussion threads pertaining to the given course Return the list of all discussion threads pertaining to the given course
Parameters: Parameters:
request: The django request objects used for build_absolute_uri request: The django request objects used for build_absolute_uri
course_key: The key of the course to get discussion threads for course: The course to get discussion threads for
page: The page number (1-indexed) to retrieve page: The page number (1-indexed) to retrieve
page_size: The number of threads to retrieve per page page_size: The number of threads to retrieve per page
...@@ -140,14 +155,14 @@ def get_thread_list(request, course_key, page, page_size): ...@@ -140,14 +155,14 @@ def get_thread_list(request, course_key, page, page_size):
discussion_api.views.ThreadViewSet for more detail. discussion_api.views.ThreadViewSet for more detail.
""" """
user_is_privileged = Role.objects.filter( user_is_privileged = Role.objects.filter(
course_id=course_key, course_id=course.id,
name__in=[FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA], name__in=[FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA],
users=request.user users=request.user
).exists() ).exists()
cc_user = User.from_django_user(request.user).retrieve() cc_user = User.from_django_user(request.user).retrieve()
threads, result_page, num_pages, _ = Thread.search({ threads, result_page, num_pages, _ = Thread.search({
"course_id": unicode(course_key), "course_id": unicode(course.id),
"group_id": None if user_is_privileged else get_cohort_id(request.user, course_key), "group_id": None if user_is_privileged else get_cohort_id(request.user, course.id),
"sort_key": "date", "sort_key": "date",
"sort_order": "desc", "sort_order": "desc",
"page": page, "page": page,
...@@ -163,18 +178,20 @@ def get_thread_list(request, course_key, page, page_size): ...@@ -163,18 +178,20 @@ def get_thread_list(request, course_key, page, page_size):
user.id user.id
for role in Role.objects.filter( for role in Role.objects.filter(
name__in=[FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR], name__in=[FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR],
course_id=course_key course_id=course.id
) )
for user in role.users.all() for user in role.users.all()
} }
ta_user_ids = { ta_user_ids = {
user.id user.id
for role in Role.objects.filter(name=FORUM_ROLE_COMMUNITY_TA, course_id=course_key) for role in Role.objects.filter(name=FORUM_ROLE_COMMUNITY_TA, course_id=course.id)
for user in role.users.all() for user in role.users.all()
} }
# For now, the only groups are cohorts
group_ids_to_names = get_cohort_names(course)
results = [ results = [
_cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids) _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids, group_ids_to_names)
for thread in threads for thread in threads
] ]
return get_paginated_data(request, results, page, num_pages) return get_paginated_data(request, results, page, num_pages)
...@@ -12,8 +12,6 @@ from pytz import UTC ...@@ -12,8 +12,6 @@ from pytz import UTC
from django.http import Http404 from django.http import Http404
from django.test.client import RequestFactory from django.test.client import RequestFactory
from opaque_keys.edx.locator import CourseLocator
from courseware.tests.factories import BetaTesterFactory, StaffFactory from courseware.tests.factories import BetaTesterFactory, StaffFactory
from discussion_api.api import get_course_topics, get_thread_list from discussion_api.api import get_course_topics, get_thread_list
from discussion_api.tests.utils import CommentsServiceMockMixin from discussion_api.tests.utils import CommentsServiceMockMixin
...@@ -337,6 +335,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -337,6 +335,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
self.request.user = self.user self.request.user = self.user
self.course = CourseFactory.create() self.course = CourseFactory.create()
self.author = UserFactory.create() self.author = UserFactory.create()
self.cohort = CohortFactory.create(course_id=self.course.id)
def get_thread_list(self, threads, page=1, page_size=1, num_pages=1, course=None): def get_thread_list(self, threads, page=1, page_size=1, num_pages=1, course=None):
""" """
...@@ -345,7 +344,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -345,7 +344,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
""" """
course = course or self.course course = course or self.course
self.register_get_threads_response(threads, page, num_pages) self.register_get_threads_response(threads, page, num_pages)
ret = get_thread_list(self.request, course.id, page, page_size) ret = get_thread_list(self.request, course, page, page_size)
return ret return ret
def create_role(self, role_name, users): def create_role(self, role_name, users):
...@@ -363,6 +362,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -363,6 +362,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"id": "dummy", "id": "dummy",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "dummy", "commentable_id": "dummy",
"group_id": None,
"user_id": str(self.author.id), "user_id": str(self.author.id),
"username": self.author.username, "username": self.author.username,
"anonymous": False, "anonymous": False,
...@@ -414,6 +414,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -414,6 +414,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"id": "test_thread_id_0", "id": "test_thread_id_0",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "topic_x", "commentable_id": "topic_x",
"group_id": None,
"user_id": str(self.author.id), "user_id": str(self.author.id),
"username": self.author.username, "username": self.author.username,
"anonymous": False, "anonymous": False,
...@@ -434,6 +435,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -434,6 +435,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"id": "test_thread_id_1", "id": "test_thread_id_1",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "topic_y", "commentable_id": "topic_y",
"group_id": self.cohort.id,
"user_id": str(self.author.id), "user_id": str(self.author.id),
"username": self.author.username, "username": self.author.username,
"anonymous": False, "anonymous": False,
...@@ -454,6 +456,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -454,6 +456,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"id": "test_thread_id_2", "id": "test_thread_id_2",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "topic_x", "commentable_id": "topic_x",
"group_id": self.cohort.id + 1, # non-existent group
"user_id": str(self.author.id), "user_id": str(self.author.id),
"username": self.author.username, "username": self.author.username,
"anonymous": False, "anonymous": False,
...@@ -476,6 +479,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -476,6 +479,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"id": "test_thread_id_0", "id": "test_thread_id_0",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"topic_id": "topic_x", "topic_id": "topic_x",
"group_id": None,
"group_name": None,
"author": self.author.username, "author": self.author.username,
"author_label": None, "author_label": None,
"created_at": "2015-04-28T00:00:00Z", "created_at": "2015-04-28T00:00:00Z",
...@@ -496,6 +501,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -496,6 +501,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"id": "test_thread_id_1", "id": "test_thread_id_1",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"topic_id": "topic_y", "topic_id": "topic_y",
"group_id": self.cohort.id,
"group_name": self.cohort.name,
"author": self.author.username, "author": self.author.username,
"author_label": None, "author_label": None,
"created_at": "2015-04-28T22:22:22Z", "created_at": "2015-04-28T22:22:22Z",
...@@ -516,6 +523,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -516,6 +523,8 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
"id": "test_thread_id_2", "id": "test_thread_id_2",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"topic_id": "topic_x", "topic_id": "topic_x",
"group_id": self.cohort.id + 1,
"group_name": None,
"author": self.author.username, "author": self.author.username,
"author_label": None, "author_label": None,
"created_at": "2015-04-28T00:44:44Z", "created_at": "2015-04-28T00:44:44Z",
...@@ -594,7 +603,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase): ...@@ -594,7 +603,7 @@ class GetThreadListTest(CommentsServiceMockMixin, ModuleStoreTestCase):
# Test page past the last one # Test page past the last one
self.register_get_threads_response([], page=3, num_pages=3) self.register_get_threads_response([], page=3, num_pages=3)
with self.assertRaises(Http404): with self.assertRaises(Http404):
get_thread_list(self.request, self.course.id, page=4, page_size=10) get_thread_list(self.request, self.course, page=4, page_size=10)
@ddt.data( @ddt.data(
(FORUM_ROLE_ADMINISTRATOR, True, False, True), (FORUM_ROLE_ADMINISTRATOR, True, False, True),
......
...@@ -147,6 +147,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): ...@@ -147,6 +147,7 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"id": "test_thread", "id": "test_thread",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"commentable_id": "test_topic", "commentable_id": "test_topic",
"group_id": None,
"user_id": str(self.author.id), "user_id": str(self.author.id),
"username": self.author.username, "username": self.author.username,
"anonymous": False, "anonymous": False,
...@@ -167,6 +168,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase): ...@@ -167,6 +168,8 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
"id": "test_thread", "id": "test_thread",
"course_id": unicode(self.course.id), "course_id": unicode(self.course.id),
"topic_id": "test_topic", "topic_id": "test_topic",
"group_id": None,
"group_name": None,
"author": self.author.username, "author": self.author.username,
"author_label": None, "author_label": None,
"created_at": "2015-04-28T00:00:00Z", "created_at": "2015-04-28T00:00:00Z",
......
...@@ -133,12 +133,11 @@ class ThreadViewSet(_ViewMixin, DeveloperErrorViewMixin, ViewSet): ...@@ -133,12 +133,11 @@ class ThreadViewSet(_ViewMixin, DeveloperErrorViewMixin, ViewSet):
form = ThreadListGetForm(request.GET) form = ThreadListGetForm(request.GET)
if not form.is_valid(): if not form.is_valid():
raise ValidationError(form.errors) raise ValidationError(form.errors)
course_key = form.cleaned_data["course_id"] course = self.get_course_or_404(request.user, form.cleaned_data["course_id"])
self.get_course_or_404(request.user, course_key)
return Response( return Response(
get_thread_list( get_thread_list(
request, request,
course_key, course,
form.cleaned_data["page"], form.cleaned_data["page"],
form.cleaned_data["page_size"] form.cleaned_data["page_size"]
) )
......
...@@ -293,6 +293,12 @@ def get_course_cohorts(course, assignment_type=None): ...@@ -293,6 +293,12 @@ def get_course_cohorts(course, assignment_type=None):
query_set = query_set.filter(cohort__assignment_type=assignment_type) if assignment_type else query_set query_set = query_set.filter(cohort__assignment_type=assignment_type) if assignment_type else query_set
return list(query_set) return list(query_set)
def get_cohort_names(course):
"""Return a dict that maps cohort ids to names for the given course"""
return {cohort.id: cohort.name for cohort in get_course_cohorts(course)}
### Helpers for cohort management views ### Helpers for cohort management views
......
...@@ -462,6 +462,15 @@ class TestCohorts(ModuleStoreTestCase): ...@@ -462,6 +462,15 @@ class TestCohorts(ModuleStoreTestCase):
cohort_set = {c.name for c in cohorts.get_course_cohorts(course)} cohort_set = {c.name for c in cohorts.get_course_cohorts(course)}
self.assertEqual(cohort_set, {"AutoGroup1", "AutoGroup2", "ManualCohort", "ManualCohort2"}) self.assertEqual(cohort_set, {"AutoGroup1", "AutoGroup2", "ManualCohort", "ManualCohort2"})
def test_get_cohort_names(self):
course = modulestore().get_course(self.toy_course_key)
cohort1 = CohortFactory(course_id=course.id, name="Cohort1")
cohort2 = CohortFactory(course_id=course.id, name="Cohort2")
self.assertEqual(
cohorts.get_cohort_names(course),
{cohort1.id: cohort1.name, cohort2.id: cohort2.name}
)
def test_is_commentable_cohorted(self): def test_is_commentable_cohorted(self):
course = modulestore().get_course(self.toy_course_key) course = modulestore().get_course(self.toy_course_key)
self.assertFalse(cohorts.is_course_cohorted(course.id)) self.assertFalse(cohorts.is_course_cohorted(course.id))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment