def get_courseware_topics(request, course_key, course, topic_ids): """ Returns a list of topic trees for courseware-linked topics. Parameters: 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 for which topics are requested. topic_ids: A list of topic IDs for which details are requested. This is optional. If None then all course topics are returned. Returns: A list of courseware topics and a set of existing topics among topic_ids. """ courseware_topics = [] existing_topic_ids = set() 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 def get_sorted_modules(category): """Returns key sorted modules by category""" return sorted(modules_by_category[category], key=get_module_sort_key) discussion_modules = get_accessible_discussion_modules(course, request.user) modules_by_category = defaultdict(list) for module in discussion_modules: modules_by_category[module.discussion_category].append(module) for category in sorted(modules_by_category.keys()): children = [] for module in get_sorted_modules(category): if not topic_ids or module.discussion_id in topic_ids: discussion_topic = DiscussionTopic( module.discussion_id, module.discussion_target, get_thread_list_url(request, course_key, [module.discussion_id]), ) children.append(discussion_topic) if topic_ids and module.discussion_id in topic_ids: existing_topic_ids.add(module.discussion_id) if not topic_ids or children: discussion_topic = DiscussionTopic( None, category, get_thread_list_url(request, course_key, [item.discussion_id for item in get_sorted_modules(category)]), children, ) courseware_topics.append(DiscussionTopicSerializer(discussion_topic).data) return courseware_topics, existing_topic_ids
def get_course_topics(course_key, user): """ Return the course topic listing for the given course and user. Parameters: course_key: The key of 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 course = _get_course_or_404(course_key, user) 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 get_courseware_topics(request, course_key, course, topic_ids): """ Returns a list of topic trees for courseware-linked topics. Parameters: 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 for which topics are requested. topic_ids: A list of topic IDs for which details are requested. This is optional. If None then all course topics are returned. Returns: A list of courseware topics and a set of existing topics among topic_ids. """ courseware_topics = [] existing_topic_ids = set() 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 def get_sorted_modules(category): """Returns key sorted modules by category""" return sorted(modules_by_category[category], key=get_module_sort_key) discussion_modules = get_accessible_discussion_modules( course, request.user) modules_by_category = defaultdict(list) for module in discussion_modules: modules_by_category[module.discussion_category].append(module) for category in sorted(modules_by_category.keys()): children = [] for module in get_sorted_modules(category): if not topic_ids or module.discussion_id in topic_ids: discussion_topic = DiscussionTopic( module.discussion_id, module.discussion_target, get_thread_list_url(request, course_key, [module.discussion_id]), ) children.append(discussion_topic) if topic_ids and module.discussion_id in topic_ids: existing_topic_ids.add(module.discussion_id) if not topic_ids or children: discussion_topic = DiscussionTopic( None, category, get_thread_list_url(request, course_key, [ item.discussion_id for item in get_sorted_modules(category) ]), children, ) courseware_topics.append( DiscussionTopicSerializer(discussion_topic).data) return courseware_topics, existing_topic_ids
def single_thread(request, course_key, discussion_id, thread_id): """ Renders a response to display a single discussion thread. """ nr_transaction = newrelic.agent.current_transaction() course = get_course_with_access(request.user, 'load_forum', course_key) course_settings = make_course_settings(course, request.user) cc_user = cc.User.from_django_user(request.user) user_info = cc_user.to_dict() is_moderator = cached_has_permission(request.user, "see_all_cohorts", course_key) # Verify that the student has access to this thread if belongs to a discussion module accessible_discussion_ids = [ module.discussion_id for module in utils.get_accessible_discussion_modules( course, request.user) ] if discussion_id not in set(course.top_level_discussion_topic_ids + accessible_discussion_ids): raise Http404 # Currently, the front end always loads responses via AJAX, even for this # page; it would be a nice optimization to avoid that extra round trip to # the comments service. try: thread = cc.Thread.find(thread_id).retrieve( recursive=request.is_ajax(), user_id=request.user.id, response_skip=request.GET.get("resp_skip"), response_limit=request.GET.get("resp_limit")) except cc.utils.CommentClientRequestError as e: if e.status_code == 404: raise Http404 raise # verify that the thread belongs to the requesting student's cohort if is_commentable_cohorted(course_key, discussion_id) and not is_moderator: user_group_id = get_cohort_id(request.user, course_key) if getattr(thread, "group_id", None) is not None and user_group_id != thread.group_id: raise Http404 is_staff = cached_has_permission(request.user, 'openclose_thread', course.id) if request.is_ajax(): with newrelic.agent.FunctionTrace(nr_transaction, "get_annotated_content_infos"): annotated_content_info = utils.get_annotated_content_infos( course_key, thread, request.user, user_info=user_info) content = utils.prepare_content(thread.to_dict(), course_key, is_staff) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context([content], course, request.user) return utils.JsonResponse({ 'content': content, 'annotated_content_info': annotated_content_info, }) else: try: threads, query_params = get_threads(request, course) except ValueError: return HttpResponseBadRequest("Invalid group_id") threads.append(thread.to_dict()) with newrelic.agent.FunctionTrace(nr_transaction, "add_courseware_context"): add_courseware_context(threads, course, request.user) for thread in threads: # patch for backward compatibility with comments service if "pinned" not in thread: thread["pinned"] = False threads = [ utils.prepare_content(thread, course_key, is_staff) for thread in threads ] with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"): annotated_content_info = utils.get_metadata_for_threads( course_key, threads, request.user, user_info) with newrelic.agent.FunctionTrace(nr_transaction, "get_cohort_info"): user_cohort = get_cohort_id(request.user, course_key) context = { 'discussion_id': discussion_id, 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': _attr_safe_json(user_info), 'annotated_content_info': _attr_safe_json(annotated_content_info), 'course': course, #'recent_active_threads': recent_active_threads, 'course_id': course.id.to_deprecated_string( ), # TODO: Why pass both course and course.id to template? 'thread_id': thread_id, 'threads': _attr_safe_json(threads), 'roles': _attr_safe_json(utils.get_role_ids(course_key)), 'is_moderator': is_moderator, 'thread_pages': query_params['num_pages'], 'is_course_cohorted': is_course_cohorted(course_key), 'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course), 'cohorts': course_settings["cohorts"], 'user_cohort': user_cohort, 'sort_preference': cc_user.default_sort_key, 'category_map': course_settings["category_map"], 'course_settings': _attr_safe_json(course_settings) } return render_to_response('discussion/index.html', context)