def get_response_comments(request, comment_id, page, page_size, requested_fields=None): """ Return the list of comments for the given thread response. Arguments: request: The django request object used for build_absolute_uri and determining the requesting user. comment_id: The id of the comment/response to get child comments for. page: The page number (1-indexed) to retrieve page_size: The number of comments to retrieve per page requested_fields: Indicates which additional fields to return for each child comment. (i.e. ['profile_image']) Returns: A paginated result containing a list of comments """ try: cc_comment = Comment(id=comment_id).retrieve() cc_thread, context = _get_thread_and_context( request, cc_comment["thread_id"], retrieve_kwargs={ "with_responses": True, "recursive": True, } ) if cc_thread["thread_type"] == "question": thread_responses = itertools.chain(cc_thread["endorsed_responses"], cc_thread["non_endorsed_responses"]) else: thread_responses = cc_thread["children"] response_comments = [] for response in thread_responses: if response["id"] == comment_id: response_comments = response["children"] break response_skip = page_size * (page - 1) paged_response_comments = response_comments[response_skip:(response_skip + page_size)] if len(paged_response_comments) == 0 and page != 1: raise PageNotFoundError("Page not found (No results on this page).") results = _serialize_discussion_entities( request, context, paged_response_comments, requested_fields, DiscussionEntity.comment ) comments_count = len(response_comments) num_pages = (comments_count + page_size - 1) / page_size if comments_count else 1 paginator = DiscussionAPIPagination(request, page, num_pages, comments_count) return paginator.get_paginated_response(results) except CommentClientRequestError: raise CommentNotFoundError("Comment not found")
def get_response_comments(request, comment_id, page, page_size): """ Return the list of comments for the given thread response. Arguments: request: The django request object used for build_absolute_uri and determining the requesting user. comment_id: The id of the comment/response to get child comments for. page: The page number (1-indexed) to retrieve page_size: The number of comments to retrieve per page Returns: A paginated result containing a list of comments """ try: cc_comment = Comment(id=comment_id).retrieve() cc_thread, context = _get_thread_and_context( request, cc_comment["thread_id"], retrieve_kwargs={ "recursive": True, } ) if cc_thread["thread_type"] == "question": thread_responses = itertools.chain(cc_thread["endorsed_responses"], cc_thread["non_endorsed_responses"]) else: thread_responses = cc_thread["children"] response_comments = [] for response in thread_responses: if response["id"] == comment_id: response_comments = response["children"] break response_skip = page_size * (page - 1) paged_response_comments = response_comments[response_skip:(response_skip + page_size)] results = [CommentSerializer(comment, context=context).data for comment in paged_response_comments] comments_count = len(response_comments) num_pages = (comments_count + page_size - 1) / page_size if comments_count else 1 paginator = DiscussionAPIPagination(request, page, num_pages) return paginator.get_paginated_response(results) except CommentClientRequestError: raise CommentNotFoundError("Comment not found")
def get_comment_list(request, thread_id, endorsed, page, page_size, requested_fields=None): """ Return the list of comments in the given thread. Arguments: request: The django request object used for build_absolute_uri and determining the requesting user. thread_id: The id of the thread to get comments for. endorsed: Boolean indicating whether to get endorsed or non-endorsed comments (or None for all comments). Must be None for a discussion thread and non-None for a question thread. page: The page number (1-indexed) to retrieve page_size: The number of comments to retrieve per page requested_fields: Indicates which additional fields to return for each comment. (i.e. ['profile_image']) Returns: A paginated result containing a list of comments; see discussion_api.views.CommentViewSet for more detail. """ response_skip = page_size * (page - 1) cc_thread, context = _get_thread_and_context(request, thread_id, retrieve_kwargs={ "with_responses": True, "recursive": False, "user_id": request.user.id, "response_skip": response_skip, "response_limit": page_size, }) # Responses to discussion threads cannot be separated by endorsed, but # responses to question threads must be separated by endorsed due to the # existing comments service interface if cc_thread["thread_type"] == "question": if endorsed is None: raise ValidationError( {"endorsed": ["This field is required for question threads."]}) elif endorsed: # CS does not apply resp_skip and resp_limit to endorsed responses # of a question post responses = cc_thread["endorsed_responses"][response_skip:( response_skip + page_size)] resp_total = len(cc_thread["endorsed_responses"]) else: responses = cc_thread["non_endorsed_responses"] resp_total = cc_thread["non_endorsed_resp_total"] else: if endorsed is not None: raise ValidationError({ "endorsed": ["This field may not be specified for discussion threads."] }) responses = cc_thread["children"] resp_total = cc_thread["resp_total"] # 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 PageNotFoundError in that case if not responses and page != 1: raise PageNotFoundError("Page not found (No results on this page).") num_pages = (resp_total + page_size - 1) / page_size if resp_total else 1 results = _serialize_discussion_entities(request, context, responses, requested_fields, DiscussionEntity.comment) paginator = DiscussionAPIPagination(request, page, num_pages, resp_total) return paginator.get_paginated_response(results)
def get_thread_list( request, course_key, page, page_size, topic_id_list=None, text_search=None, following=False, view=None, order_by="last_activity_at", order_direction="desc", requested_fields=None, ): """ Return the list of all discussion threads pertaining to the given course Parameters: request: The django request objects used for build_absolute_uri course_key: The key of 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 topic_id_list: The list of topic_ids to get the discussion threads for text_search A text search query string to match following: If true, retrieve only threads the requester is following view: filters for either "unread" or "unanswered" threads order_by: The key in which to sort the threads by. The only values are "last_activity_at", "comment_count", and "vote_count". The default is "last_activity_at". order_direction: The direction in which to sort the threads by. The default and only value is "desc". This will be removed in a future major version. requested_fields: Indicates which additional fields to return for each thread. (i.e. ['profile_image']) Note that topic_id_list, text_search, and following are mutually exclusive. Returns: A paginated result containing a list of threads; see discussion_api.views.ThreadViewSet for more detail. Raises: ValidationError: if an invalid value is passed for a field. ValueError: if more than one of the mutually exclusive parameters is provided CourseNotFoundError: if the requesting user does not have access to the requested course PageNotFoundError: if page requested is beyond the last """ exclusive_param_count = sum( 1 for param in [topic_id_list, text_search, following] if param) if exclusive_param_count > 1: # pragma: no cover raise ValueError( "More than one mutually exclusive param passed to get_thread_list") cc_map = { "last_activity_at": "activity", "comment_count": "comments", "vote_count": "votes" } if order_by not in cc_map: raise ValidationError({ "order_by": [ "Invalid value. '{}' must be 'last_activity_at', 'comment_count', or 'vote_count'" .format(order_by) ] }) if order_direction != "desc": raise ValidationError({ "order_direction": ["Invalid value. '{}' must be 'desc'".format(order_direction)] }) course = _get_course(course_key, request.user) context = get_context(course, request) query_params = { "user_id": unicode(request.user.id), "group_id": (None if context["is_requester_privileged"] else get_cohort_id( request.user, course.id)), "page": page, "per_page": page_size, "text": text_search, "sort_key": cc_map.get(order_by), } if view: if view in ["unread", "unanswered"]: query_params[view] = "true" else: ValidationError({ "view": [ "Invalid value. '{}' must be 'unread' or 'unanswered'". format(view) ] }) if following: paginated_results = context["cc_requester"].subscribed_threads( query_params) else: query_params["course_id"] = unicode(course.id) query_params["commentable_ids"] = ",".join( topic_id_list) if topic_id_list else None query_params["text"] = text_search paginated_results = Thread.search(query_params) # 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 PageNotFoundError in that case if paginated_results.page != page: raise PageNotFoundError("Page not found (No results on this page).") results = _serialize_discussion_entities(request, context, paginated_results.collection, requested_fields, DiscussionEntity.thread) paginator = DiscussionAPIPagination(request, paginated_results.page, paginated_results.num_pages, paginated_results.thread_count) return paginator.get_paginated_response({ "results": results, "text_search_rewrite": paginated_results.corrected_text, })
def get_comment_list(request, thread_id, endorsed, page, page_size, requested_fields=None): """ Return the list of comments in the given thread. Arguments: request: The django request object used for build_absolute_uri and determining the requesting user. thread_id: The id of the thread to get comments for. endorsed: Boolean indicating whether to get endorsed or non-endorsed comments (or None for all comments). Must be None for a discussion thread and non-None for a question thread. page: The page number (1-indexed) to retrieve page_size: The number of comments to retrieve per page requested_fields: Indicates which additional fields to return for each comment. (i.e. ['profile_image']) Returns: A paginated result containing a list of comments; see discussion_api.views.CommentViewSet for more detail. """ response_skip = page_size * (page - 1) cc_thread, context = _get_thread_and_context( request, thread_id, retrieve_kwargs={ "with_responses": True, "recursive": False, "user_id": request.user.id, "response_skip": response_skip, "response_limit": page_size, } ) # Responses to discussion threads cannot be separated by endorsed, but # responses to question threads must be separated by endorsed due to the # existing comments service interface if cc_thread["thread_type"] == "question": if endorsed is None: raise ValidationError({"endorsed": ["This field is required for question threads."]}) elif endorsed: # CS does not apply resp_skip and resp_limit to endorsed responses # of a question post responses = cc_thread["endorsed_responses"][response_skip:(response_skip + page_size)] resp_total = len(cc_thread["endorsed_responses"]) else: responses = cc_thread["non_endorsed_responses"] resp_total = cc_thread["non_endorsed_resp_total"] else: if endorsed is not None: raise ValidationError( {"endorsed": ["This field may not be specified for discussion threads."]} ) responses = cc_thread["children"] resp_total = cc_thread["resp_total"] # 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 PageNotFoundError in that case if not responses and page != 1: raise PageNotFoundError("Page not found (No results on this page).") num_pages = (resp_total + page_size - 1) / page_size if resp_total else 1 results = _serialize_discussion_entities(request, context, responses, requested_fields, DiscussionEntity.comment) paginator = DiscussionAPIPagination(request, page, num_pages, resp_total) return paginator.get_paginated_response(results)
def get_thread_list( request, course_key, page, page_size, topic_id_list=None, text_search=None, following=False, view=None, order_by="last_activity_at", order_direction="desc", requested_fields=None, ): """ Return the list of all discussion threads pertaining to the given course Parameters: request: The django request objects used for build_absolute_uri course_key: The key of 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 topic_id_list: The list of topic_ids to get the discussion threads for text_search A text search query string to match following: If true, retrieve only threads the requester is following view: filters for either "unread" or "unanswered" threads order_by: The key in which to sort the threads by. The only values are "last_activity_at", "comment_count", and "vote_count". The default is "last_activity_at". order_direction: The direction in which to sort the threads by. The default and only value is "desc". This will be removed in a future major version. requested_fields: Indicates which additional fields to return for each thread. (i.e. ['profile_image']) Note that topic_id_list, text_search, and following are mutually exclusive. Returns: A paginated result containing a list of threads; see discussion_api.views.ThreadViewSet for more detail. Raises: ValidationError: if an invalid value is passed for a field. ValueError: if more than one of the mutually exclusive parameters is provided CourseNotFoundError: if the requesting user does not have access to the requested course PageNotFoundError: if page requested is beyond the last """ exclusive_param_count = sum(1 for param in [topic_id_list, text_search, following] if param) if exclusive_param_count > 1: # pragma: no cover raise ValueError("More than one mutually exclusive param passed to get_thread_list") cc_map = {"last_activity_at": "activity", "comment_count": "comments", "vote_count": "votes"} if order_by not in cc_map: raise ValidationError({ "order_by": ["Invalid value. '{}' must be 'last_activity_at', 'comment_count', or 'vote_count'".format(order_by)] }) if order_direction != "desc": raise ValidationError({ "order_direction": ["Invalid value. '{}' must be 'desc'".format(order_direction)] }) course = _get_course(course_key, request.user) context = get_context(course, request) query_params = { "user_id": unicode(request.user.id), "group_id": ( None if context["is_requester_privileged"] else get_cohort_id(request.user, course.id) ), "page": page, "per_page": page_size, "text": text_search, "sort_key": cc_map.get(order_by), } if view: if view in ["unread", "unanswered"]: query_params[view] = "true" else: ValidationError({ "view": ["Invalid value. '{}' must be 'unread' or 'unanswered'".format(view)] }) if following: paginated_results = context["cc_requester"].subscribed_threads(query_params) else: query_params["course_id"] = unicode(course.id) query_params["commentable_ids"] = ",".join(topic_id_list) if topic_id_list else None query_params["text"] = text_search paginated_results = Thread.search(query_params) # 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 PageNotFoundError in that case if paginated_results.page != page: raise PageNotFoundError("Page not found (No results on this page).") results = _serialize_discussion_entities( request, context, paginated_results.collection, requested_fields, DiscussionEntity.thread ) paginator = DiscussionAPIPagination( request, paginated_results.page, paginated_results.num_pages, paginated_results.thread_count ) return paginator.get_paginated_response({ "results": results, "text_search_rewrite": paginated_results.corrected_text, })