Skip to content

Commit e28fc4c

Browse files
committed
fix: inconsistent loading of discussion thread response
1 parent e98af83 commit e28fc4c

2 files changed

Lines changed: 188 additions & 121 deletions

File tree

‎lms/djangoapps/discussion/rest_api/api.py‎

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from django.urls import reverse
2323
from django.utils.html import strip_tags
2424
from edx_django_utils.monitoring import function_trace, set_custom_attribute
25+
from forum.backend import get_backend
2526
from opaque_keys import InvalidKeyError
2627
from opaque_keys.edx.locator import CourseKey
2728
from pytz import UTC
@@ -1671,7 +1672,7 @@ def get_comment_list(
16711672
discussion.rest_api.views.CommentViewSet for more detail.
16721673
"""
16731674
response_skip = page_size * (page - 1)
1674-
reverse_order = request.GET.get("reverse_order", False)
1675+
reverse_order = request.GET.get("reverse_order", "").lower() in ("true", "1")
16751676
from_mfe_sidebar = request.GET.get("enable_in_context_sidebar", False)
16761677
cc_thread, context = _get_thread_and_context(
16771678
request,
@@ -2292,74 +2293,107 @@ def get_response_comments(request, comment_id, page, page_size, requested_fields
22922293
"""
22932294
try:
22942295
cc_comment = Comment(id=comment_id).retrieve()
2295-
reverse_order = request.GET.get("reverse_order", False)
2296+
reverse_order = request.GET.get("reverse_order", "").lower() in ("true", "1")
22962297
show_deleted = request.GET.get("show_deleted", False)
22972298
show_deleted = show_deleted in ["true", "True", True]
22982299

2300+
# Only fetch thread for context/permissions — NOT the full response tree
22992301
cc_thread, context = _get_thread_and_context(
23002302
request,
23012303
cc_comment["thread_id"],
23022304
retrieve_kwargs={
2303-
"with_responses": True,
2304-
"recursive": True,
2305-
"reverse_order": reverse_order,
2306-
"show_deleted": show_deleted,
2305+
"with_responses": False,
2306+
"recursive": False,
23072307
},
23082308
)
2309-
if cc_thread["thread_type"] == "question":
2310-
thread_responses = itertools.chain(
2311-
cc_thread["endorsed_responses"], cc_thread["non_endorsed_responses"]
2309+
2310+
if show_deleted and not context["has_moderation_privilege"]:
2311+
raise PermissionDenied(
2312+
"`show_deleted` can only be set by users with moderation roles."
23122313
)
2313-
else:
2314-
thread_responses = cc_thread["children"]
2315-
response_comments = []
2316-
for response in thread_responses:
2317-
if response["id"] == comment_id:
2318-
response_comments = response["children"]
2319-
break
2320-
2321-
# Filter deleted content from the FULL list first
2314+
2315+
# Determine sort order
2316+
sorting_order = -1 if reverse_order else 1
2317+
2318+
# Get the course_id from the thread for backend initialization
2319+
course_id = cc_thread["course_id"]
2320+
backend = get_backend(course_id)()
2321+
2322+
# Fetch ONLY this comment's children directly from the backend
2323+
# This avoids loading the entire thread tree (the main performance fix)
2324+
response_comments = backend.get_comments(
2325+
parent_id=str(comment_id),
2326+
depth=1,
2327+
sort=sorting_order,
2328+
)
2329+
2330+
# Filter deleted content
23222331
if not show_deleted:
23232332
response_comments = [
2324-
response
2325-
for response in response_comments
2326-
if not response.get("is_deleted", False)
2333+
comment for comment in response_comments
2334+
if not comment.get("is_deleted", False)
23272335
]
2328-
else:
2329-
if not context["has_moderation_privilege"]:
2330-
raise PermissionDenied(
2331-
"`show_deleted` can only be set by users with moderation roles."
2332-
)
23332336

2334-
# Filter muted content from the FULL list
2335-
include_muted = request.GET.get("include_muted", False)
2336-
include_muted = include_muted in ["true", "True", True]
2337-
if not include_muted:
2337+
# Filter muted content
2338+
include_muted_param = request.GET.get("include_muted", False)
2339+
include_muted_param = include_muted_param in ["true", "True", True]
2340+
if not include_muted_param:
23382341
response_comments = filter_muted_content(
23392342
request.user,
23402343
context["course"].id,
23412344
response_comments
23422345
)
23432346

2344-
# NOW calculate pagination based on FILTERED total
2347+
# Calculate pagination based on FILTERED total
23452348
total_comments_count = len(response_comments)
23462349
num_pages = (
23472350
(total_comments_count + page_size - 1) // page_size
23482351
if total_comments_count else 1
23492352
)
23502353

2351-
# Then paginate the filtered list
2354+
# Paginate the filtered list
23522355
response_skip = page_size * (page - 1)
23532356
paged_response_comments = response_comments[
23542357
response_skip: (response_skip + page_size)
23552358
]
2359+
23562360
if not paged_response_comments and page != 1:
23572361
raise PageNotFoundError("Page not found (No results on this page).")
23582362

2363+
# Normalize comment data for serialization (backend returns raw dict format)
2364+
normalized_comments = []
2365+
for comment in paged_response_comments:
2366+
normalized = {
2367+
"id": str(comment.get("_id", comment.get("id", ""))),
2368+
"body": comment.get("body", ""),
2369+
"course_id": comment.get("course_id", course_id),
2370+
"user_id": comment.get("author_id", comment.get("user_id")),
2371+
"username": comment.get("author_username", comment.get("username", "")),
2372+
"thread_id": str(comment.get("comment_thread_id", comment.get("thread_id", ""))),
2373+
"parent_id": str(comment.get("parent_id", "")) if comment.get("parent_id") else None,
2374+
"created_at": comment.get("created_at"),
2375+
"updated_at": comment.get("updated_at"),
2376+
"depth": comment.get("depth", 1),
2377+
"type": "comment",
2378+
"anonymous": comment.get("anonymous", False),
2379+
"anonymous_to_peers": comment.get("anonymous_to_peers", False),
2380+
"endorsed": comment.get("endorsed", False),
2381+
"abuse_flaggers": comment.get("abuse_flaggers", []),
2382+
"votes": comment.get("votes", {"up_count": 0}),
2383+
"child_count": comment.get("child_count", 0),
2384+
"children": [],
2385+
"closed": comment.get("closed", False),
2386+
}
2387+
# Carry over any additional fields
2388+
for key in ("endorsement", "edit_history", "is_deleted", "deleted_at", "deleted_by"):
2389+
if key in comment:
2390+
normalized[key] = comment[key]
2391+
normalized_comments.append(normalized)
2392+
23592393
results = _serialize_discussion_entities(
23602394
request,
23612395
context,
2362-
paged_response_comments,
2396+
normalized_comments,
23632397
requested_fields,
23642398
DiscussionEntity.comment,
23652399
)

0 commit comments

Comments
 (0)