""" Discussion API internal interface """ from django.http import Http404 from collections import defaultdict from discussion_api.pagination import get_paginated_data from django_comment_client.utils import get_accessible_discussion_modules from django_comment_common.models import ( FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_COMMUNITY_TA, FORUM_ROLE_MODERATOR, Role, ) from lms.lib.comment_client.thread import Thread from lms.lib.comment_client.user import User from openedx.core.djangoapps.course_groups.cohorts import get_cohort_id, get_cohort_names def get_course_topics(course, user): """ Return the course topic listing for the given course and user. Parameters: course: The course to get topics for user: The requesting user, for access control Returns: A course topic listing dictionary; see discussion_api.views.CourseTopicViews for more detail. """ def get_module_sort_key(module): """ Get the sort key for the module (falling back to the discussion_target setting if absent) """ return module.sort_key or module.discussion_target discussion_modules = get_accessible_discussion_modules(course, user) modules_by_category = defaultdict(list) for module in discussion_modules: modules_by_category[module.discussion_category].append(module) courseware_topics = [ { "id": None, "name": category, "children": [ { "id": module.discussion_id, "name": module.discussion_target, "children": [], } for module in sorted(modules_by_category[category], key=get_module_sort_key) ], } for category in sorted(modules_by_category.keys()) ] non_courseware_topics = [ { "id": entry["id"], "name": name, "children": [], } for name, entry in sorted( course.discussion_topics.items(), key=lambda item: item[1].get("sort_key", item[0]) ) ] return { "courseware_topics": courseware_topics, "non_courseware_topics": non_courseware_topics, } 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 representation of the format returned by the comments service) to the format 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 = ( thread["anonymous"] or ( thread["anonymous_to_peers"] and int(cc_user["id"]) not in (staff_user_ids | ta_user_ids) ) ) ret = { key: thread[key] for key in [ "id", "course_id", "group_id", "created_at", "updated_at", "title", "pinned", "closed", ] } ret.update({ "topic_id": thread["commentable_id"], "group_name": group_ids_to_names.get(thread["group_id"]), "author": None if is_anonymous else thread["username"], "author_label": ( None if is_anonymous else "staff" if int(thread["user_id"]) in staff_user_ids else "community_ta" if int(thread["user_id"]) in ta_user_ids else None ), "type": thread["thread_type"], "raw_body": thread["body"], "following": thread["id"] in cc_user["subscribed_thread_ids"], "abuse_flagged": cc_user["id"] in thread["abuse_flaggers"], "voted": thread["id"] in cc_user["upvoted_ids"], "vote_count": thread["votes"]["up_count"], "comment_count": thread["comments_count"], "unread_comment_count": thread["unread_comments_count"], }) return ret def get_thread_list(request, course, page, page_size): """ Return the list of all discussion threads pertaining to the given course Parameters: request: The django request objects used for build_absolute_uri course: The course to get discussion threads for page: The page number (1-indexed) to retrieve page_size: The number of threads to retrieve per page Returns: A paginated result containing a list of threads; see discussion_api.views.ThreadViewSet for more detail. """ user_is_privileged = Role.objects.filter( course_id=course.id, name__in=[FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA], users=request.user ).exists() cc_user = User.from_django_user(request.user).retrieve() threads, result_page, num_pages, _ = Thread.search({ "course_id": unicode(course.id), "group_id": None if user_is_privileged else get_cohort_id(request.user, course.id), "sort_key": "date", "sort_order": "desc", "page": page, "per_page": page_size, }) # The comments service returns the last page of results if the requested # page is beyond the last page, but we want be consistent with DRF's general # behavior and return a 404 in that case if result_page != page: raise Http404 # TODO: cache staff_user_ids and ta_user_ids if we need to improve perf staff_user_ids = { user.id for role in Role.objects.filter( name__in=[FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR], course_id=course.id ) for user in role.users.all() } ta_user_ids = { user.id for role in Role.objects.filter(name=FORUM_ROLE_COMMUNITY_TA, course_id=course.id) for user in role.users.all() } # For now, the only groups are cohorts group_ids_to_names = get_cohort_names(course) results = [ _cc_thread_to_api_thread(thread, cc_user, staff_user_ids, ta_user_ids, group_ids_to_names) for thread in threads ] return get_paginated_data(request, results, page, num_pages)