Skip to content

Commit ab6cf6e

Browse files
authored
revert: feat: [FC-0092] Optimize Course Info Blocks API (#37122) (#37661)
This reverts commit 7cd4170.
1 parent 1253831 commit ab6cf6e

6 files changed

Lines changed: 5 additions & 168 deletions

File tree

lms/djangoapps/course_api/blocks/api.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
API function for retrieving course blocks data
33
"""
4-
from edx_django_utils.cache import RequestCache
4+
55

66
import lms.djangoapps.course_blocks.api as course_blocks_api
77
from lms.djangoapps.course_blocks.transformers.access_denied_filter import AccessDeniedMessageFilterTransformer
@@ -14,7 +14,6 @@
1414
from .toggles import HIDE_ACCESS_DENIALS_FLAG
1515
from .transformers.blocks_api import BlocksAPITransformer
1616
from .transformers.milestones import MilestonesAndSpecialExamsTransformer
17-
from .utils import COURSE_API_REQUEST_CACHE_NAMESPACE, REUSABLE_BLOCKS_CACHE_KEY
1817

1918

2019
def get_blocks(
@@ -30,7 +29,6 @@ def get_blocks(
3029
block_types_filter=None,
3130
hide_access_denials=False,
3231
allow_start_dates_in_future=False,
33-
cache_with_future_dates=False,
3432
):
3533
"""
3634
Return a serialized representation of the course blocks.
@@ -63,7 +61,6 @@ def get_blocks(
6361
allow_start_dates_in_future (bool): When True, will allow blocks to be
6462
returned that can bypass the StartDateTransformer's filter to show
6563
blocks with start dates in the future.
66-
cache_with_future_dates (bool): When True, will use the block caching logic using RequestCache
6764
"""
6865

6966
if HIDE_ACCESS_DENIALS_FLAG.is_enabled():
@@ -121,10 +118,6 @@ def get_blocks(
121118
),
122119
]
123120

124-
if cache_with_future_dates:
125-
# Include future dates such that get_course_assignments can reuse the block structure from RequestCache
126-
allow_start_dates_in_future = True
127-
128121
# transform
129122
blocks = course_blocks_api.get_course_blocks(
130123
user,
@@ -135,19 +128,6 @@ def get_blocks(
135128
include_has_scheduled_content=include_has_scheduled_content
136129
)
137130

138-
if cache_with_future_dates:
139-
# Store a copy of the transformed, but still unfiltered, course blocks in RequestCache to be reused
140-
# wherever possible for optimization. Copying is required to make sure the cached structure is not mutated
141-
# by the filtering below.
142-
request_cache = RequestCache(COURSE_API_REQUEST_CACHE_NAMESPACE)
143-
request_cache.set(REUSABLE_BLOCKS_CACHE_KEY, blocks.copy())
144-
145-
# Since we included blocks with future start dates in our block structure,
146-
# we need to include the 'start' field to filter out such blocks before returning the response.
147-
# If 'start' field is not requested, it will be removed from the response.
148-
requested_fields = set(requested_fields)
149-
requested_fields.add('start')
150-
151131
# filter blocks by types
152132
if block_types_filter:
153133
block_keys_to_remove = []
@@ -162,7 +142,7 @@ def get_blocks(
162142
serializer_context = {
163143
'request': request,
164144
'block_structure': blocks,
165-
'requested_fields': requested_fields,
145+
'requested_fields': requested_fields or [],
166146
}
167147

168148
if return_type == 'dict':

lms/djangoapps/course_api/blocks/utils.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""
22
Utils for Blocks
33
"""
4-
from edx_django_utils.cache import RequestCache
54
from rest_framework.utils.serializer_helpers import ReturnList
65

76
from openedx.core.djangoapps.discussions.models import (
@@ -10,10 +9,6 @@
109
)
1110

1211

13-
COURSE_API_REQUEST_CACHE_NAMESPACE = "course_api"
14-
REUSABLE_BLOCKS_CACHE_KEY = "reusable_transformed_blocks"
15-
16-
1712
def filter_discussion_xblocks_from_response(response, course_key):
1813
"""
1914
Removes discussion xblocks if discussion provider is openedx.
@@ -68,18 +63,3 @@ def filter_discussion_xblocks_from_response(response, course_key):
6863
response.data['blocks'] = filtered_blocks
6964

7065
return response
71-
72-
73-
def get_cached_transformed_blocks():
74-
"""
75-
Helper function to get an unfiltered course structure from RequestCache,
76-
including blocks with start dates in the future.
77-
78-
Caution: For performance reasons, the function returns the structure object itself, not its copy.
79-
This means the retrieved structure is supposed to be read-only and should not be mutated by consumers.
80-
"""
81-
request_cache = RequestCache(COURSE_API_REQUEST_CACHE_NAMESPACE)
82-
cached_response = request_cache.get_cached_response(REUSABLE_BLOCKS_CACHE_KEY)
83-
reusable_transformed_blocks = cached_response.value if cached_response.is_found else None
84-
85-
return reusable_transformed_blocks

lms/djangoapps/course_api/blocks/views.py

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
CourseBlocks API views
33
"""
44

5-
from datetime import datetime, timezone
65

76
from django.core.exceptions import ValidationError
87
from django.db import transaction
@@ -238,7 +237,6 @@ def list(self, request, usage_key_string, hide_access_denials=False): # pylint:
238237
params.cleaned_data['return_type'],
239238
params.cleaned_data.get('block_types_filter', None),
240239
hide_access_denials=hide_access_denials,
241-
cache_with_future_dates=True
242240
)
243241
)
244242
# If the username is an empty string, and not None, then we are requesting
@@ -341,50 +339,9 @@ def list(self, request, hide_access_denials=False): # pylint: disable=arguments
341339
if not root:
342340
raise ValidationError(f"Unable to find course block in '{course_key_string}'")
343341

344-
# Earlier we included blocks with future start dates in the collected/cached block structure.
345-
# Now we need to emulate allow_start_dates_in_future=False by removing any such blocks.
346-
include_start = "start" in request.query_params['requested_fields']
347-
self.remove_future_blocks(course_blocks, include_start)
348-
349342
recurse_mark_complete(root, course_blocks)
350343
return response
351344

352-
@staticmethod
353-
def remove_future_blocks(course_blocks, include_start: bool):
354-
"""
355-
Mutates course_blocks in place:
356-
- removes blocks whose 'start' is in the future
357-
- also removes references to them from parents' 'children' lists
358-
- removes 'start' key from all blocks if it wasn't requested
359-
"""
360-
if not course_blocks:
361-
return course_blocks
362-
363-
now = datetime.now(timezone.utc)
364-
365-
# 1. Collect IDs of blocks to remove
366-
to_remove = set()
367-
for block_id, block in course_blocks.items():
368-
get_field = block.get if include_start else block.pop
369-
start = get_field("start")
370-
if start and start > now:
371-
to_remove.add(block_id)
372-
373-
if not to_remove:
374-
return course_blocks
375-
376-
# 2. Remove the blocks themselves
377-
for block_id in to_remove:
378-
course_blocks.pop(block_id, None)
379-
380-
# 3. Clean up children lists
381-
for block in course_blocks.values():
382-
children = block.get("children")
383-
if children:
384-
block["children"] = [cid for cid in children if cid not in to_remove]
385-
386-
return course_blocks
387-
388345

389346
@method_decorator(transaction.non_atomic_requests, name='dispatch')
390347
@view_auth_classes(is_authenticated=False)

lms/djangoapps/courseware/courses.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
from common.djangoapps.static_replace import replace_static_urls
2727
from common.djangoapps.util.date_utils import strftime_localized
2828
from lms.djangoapps import branding
29-
from lms.djangoapps.course_api.blocks.utils import get_cached_transformed_blocks
3029
from lms.djangoapps.course_blocks.api import get_course_blocks
3130
from lms.djangoapps.courseware.access import has_access
3231
from lms.djangoapps.courseware.access_response import (
@@ -637,10 +636,7 @@ def get_course_assignments(course_key, user, include_access=False, include_witho
637636

638637
store = modulestore()
639638
course_usage_key = store.make_course_usage_key(course_key)
640-
641-
block_data = get_cached_transformed_blocks() or get_course_blocks(
642-
user, course_usage_key, allow_start_dates_in_future=True, include_completion=True
643-
)
639+
block_data = get_course_blocks(user, course_usage_key, allow_start_dates_in_future=True, include_completion=True)
644640

645641
now = datetime.now(pytz.UTC)
646642
assignments = []

lms/djangoapps/grades/course_data.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
Code used to get and cache the requested course-data
33
"""
44

5+
56
from lms.djangoapps.course_blocks.api import get_course_blocks
67
from openedx.core.djangoapps.content.block_structure.api import get_block_structure_manager
78
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
89

910
from .transformer import GradesTransformer
10-
from ..course_api.blocks.utils import get_cached_transformed_blocks
1111

1212

1313
class CourseData:
@@ -56,11 +56,7 @@ def location(self): # lint-amnesty, pylint: disable=missing-function-docstring
5656
@property
5757
def structure(self): # lint-amnesty, pylint: disable=missing-function-docstring
5858
if self._structure is None:
59-
# The get_course_blocks function proved to be a major time sink during a request at "blocks/".
60-
# This caching logic helps improve the response time by getting a copy of the already transformed, but still
61-
# unfiltered, course blocks from RequestCache and thus reducing the number of times that
62-
# the get_course_blocks function is called.
63-
self._structure = get_cached_transformed_blocks() or get_course_blocks(
59+
self._structure = get_course_blocks(
6460
self.user,
6561
self.location,
6662
collected_block_structure=self._collected_block_structure,

lms/djangoapps/mobile_api/tests/test_course_info_views.py

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -432,78 +432,6 @@ def test_extend_sequential_info_with_assignment_progress_for_other_types(self, b
432432
for block_info in response.data['blocks'].values():
433433
self.assertNotEqual('assignment_progress', block_info)
434434

435-
def test_response_keys(self):
436-
response = self.verify_response(url=self.url)
437-
data = response.data
438-
439-
expected_top_level_keys = {
440-
'blocks',
441-
'certificate',
442-
'course_about',
443-
'course_access_details',
444-
'course_handouts',
445-
'course_modes',
446-
'course_progress',
447-
'course_sharing_utm_parameters',
448-
'course_updates',
449-
'deprecate_youtube',
450-
'discussion_url',
451-
'end',
452-
'enrollment_details',
453-
'id',
454-
'is_self_paced',
455-
'media',
456-
'name',
457-
'number',
458-
'org',
459-
'org_logo',
460-
'root',
461-
'start',
462-
'start_display',
463-
'start_type'
464-
}
465-
expected_course_access_keys = {
466-
"has_unmet_prerequisites",
467-
"is_too_early",
468-
"is_staff",
469-
"audit_access_expires",
470-
"courseware_access"
471-
}
472-
expected_courseware_access_keys = {
473-
"has_access",
474-
"error_code",
475-
"developer_message",
476-
"user_message",
477-
"additional_context_user_message",
478-
"user_fragment"
479-
}
480-
expected_enrollment_details_keys = {"created", "mode", "is_active", "upgrade_deadline"}
481-
expected_media_keys = {"image"}
482-
expected_image_keys = {"raw", "small", "large"}
483-
expected_course_sharing_keys = {"facebook", "twitter"}
484-
expected_course_modes_keys = {"slug", "sku", "android_sku", "ios_sku", "min_price"}
485-
expected_course_progress_keys = {"total_assignments_count", "assignments_completed"}
486-
487-
self.assertSetEqual(set(data), expected_top_level_keys)
488-
self.assertSetEqual(set(data["course_access_details"]), expected_course_access_keys)
489-
self.assertSetEqual(set(data["course_access_details"]["courseware_access"]), expected_courseware_access_keys)
490-
self.assertSetEqual(set(data["enrollment_details"]), expected_enrollment_details_keys)
491-
self.assertSetEqual(set(data["media"]), expected_media_keys)
492-
self.assertSetEqual(set(data["media"]["image"]), expected_image_keys)
493-
self.assertSetEqual(set(data["course_sharing_utm_parameters"]), expected_course_sharing_keys)
494-
self.assertSetEqual(set(data["course_modes"][0]), expected_course_modes_keys)
495-
self.assertSetEqual(set(data["course_progress"]), expected_course_progress_keys)
496-
497-
def test_block_count_depends_on_depth_in_request_params(self):
498-
response_depth_zero = self.verify_response(url=self.url, params={'depth': 0})
499-
response_depth_one = self.verify_response(url=self.url, params={'depth': 1})
500-
blocks_depth_zero = [block for block in self.store.get_items(self.course_key) if block.category == "course"]
501-
blocks_depth_one = [
502-
block for block in self.store.get_items(self.course_key) if block.category in ("course", "chapter")
503-
]
504-
self.assertEqual(len(response_depth_zero.data["blocks"]), len(blocks_depth_zero))
505-
self.assertEqual(len(response_depth_one.data["blocks"]), len(blocks_depth_one))
506-
507435

508436
class TestCourseEnrollmentDetailsView(MobileAPITestCase, MilestonesTestCaseMixin): # lint-amnesty, pylint: disable=test-inherits-tests
509437
"""

0 commit comments

Comments
 (0)