def test_request_cached_with_caches_despite_changing_wrapped_result(self): """ Ensure that after caching a result, we always send it back, even if the underlying result changes. """ RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [1, 2, 3] self.assertEqual(to_be_wrapped.call_count, 0) def mock_wrapper(*args, **kwargs): """Simple wrapper to let us decorate our mock.""" return to_be_wrapped(*args, **kwargs) wrapped = request_cached(mock_wrapper) result = wrapped() self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 1) result = wrapped() self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 1) direct_result = mock_wrapper() self.assertEqual(direct_result, 2) self.assertEqual(to_be_wrapped.call_count, 2) result = wrapped() self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 2) direct_result = mock_wrapper() self.assertEqual(direct_result, 3) self.assertEqual(to_be_wrapped.call_count, 3)
def test_request_context_caching(self): """ Test that the RequestContext is cached in the RequestCache. """ with patch('edxmako.request_context.get_current_request', return_value=None): # requestcontext should be None, because the cache isn't filled self.assertIsNone(get_template_request_context()) with patch('edxmako.request_context.get_current_request', return_value=self.request): # requestcontext should not be None, and should fill the cache self.assertIsNotNone(get_template_request_context()) mock_get_current_request = Mock() with patch('edxmako.request_context.get_current_request', mock_get_current_request): # requestcontext should not be None, because the cache is filled self.assertIsNotNone(get_template_request_context()) mock_get_current_request.assert_not_called() RequestCache.clear_request_cache() with patch('edxmako.request_context.get_current_request', return_value=None): # requestcontext should be None, because the cache isn't filled self.assertIsNone(get_template_request_context())
def instrument_course_progress_render( self, course_width, enable_ccx, view_as_ccx, sql_queries, mongo_reads, ): """ Renders the progress page, instrumenting Mongo reads and SQL queries. """ course_key = self.setup_course(course_width, enable_ccx, view_as_ccx) # Switch to published-only mode to simulate the LMS with self.settings(MODULESTORE_BRANCH='published-only'): # Clear all caches before measuring for cache in settings.CACHES: caches[cache].clear() # Refill the metadata inheritance cache get_course_in_cache(course_key) # We clear the request cache to simulate a new request in the LMS. RequestCache.clear_request_cache() # Reset the list of provider classes, so that our django settings changes # can actually take affect. OverrideFieldData.provider_classes = None with self.assertNumQueries(sql_queries, using='default', table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with self.assertNumQueries(0, using='student_module_history'): with self.assertMongoCallCount(mongo_reads): with self.assertXBlockInstantiations(1): self.grade_course(course_key)
def test_request_cached_mixed_unicode_str_args(self): """ Ensure that request_cached can work with mixed str and Unicode parameters. """ RequestCache.clear_request_cache() def dummy_function(arg1, arg2): """ A dummy function that expects an str and unicode arguments. """ assert isinstance(arg1, str), 'First parameter has to be of type `str`' assert isinstance( arg2, unicode), 'Second parameter has to be of type `unicode`' return True self.assertTrue(dummy_function('Hello', u'World'), 'Should be callable with ASCII chars') self.assertTrue(dummy_function('H∂llå', u'Wørld'), 'Should be callable with non-ASCII chars') wrapped = request_cached(dummy_function) self.assertTrue(wrapped('Hello', u'World'), 'Wrapper should handle ASCII only chars') self.assertTrue(wrapped('H∂llå', u'Wørld'), 'Wrapper should handle non-ASCII chars')
def test_setting_override(self, is_enabled, override_choice, expected_result): RequestCache.clear_request_cache() self.set_waffle_course_override(override_choice, is_enabled) override_value = WaffleFlagCourseOverrideModel.override_value( self.WAFFLE_TEST_NAME, self.TEST_COURSE_KEY ) self.assertEqual(override_value, expected_result)
def test_request_cached_with_caches_despite_changing_wrapped_result(self): """ Ensure that after caching a result, we always send it back, even if the underlying result changes. """ RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [1, 2, 3] self.assertEqual(to_be_wrapped.call_count, 0) def mock_wrapper(*args, **kwargs): """Simple wrapper to let us decorate our mock.""" return to_be_wrapped(*args, **kwargs) wrapped = request_cached(mock_wrapper) result = wrapped() self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 1) result = wrapped() self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 1) direct_result = mock_wrapper() self.assertEqual(direct_result, 2) self.assertEqual(to_be_wrapped.call_count, 2) result = wrapped() self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 2) direct_result = mock_wrapper() self.assertEqual(direct_result, 3) self.assertEqual(to_be_wrapped.call_count, 3)
def test_get_template_path(self): """ Tests to make sure the get_template_path function works as expected. """ # if the current site has associated SiteTheme then get_template_path should return the argument as is. with patch( "openedx.core.djangoapps.theming.helpers.current_request_has_associated_site_theme", Mock(return_value=True), ): with patch( "openedx.core.djangoapps.theming.helpers.microsite.is_request_in_microsite", Mock(return_value=True), ): with patch("microsite_configuration.microsite.TEMPLATES_BACKEND") as mock_microsite_backend: mock_microsite_backend.get_template = Mock(return_value="/microsite/about.html") self.assertEqual(theming_helpers.get_template_path("about.html"), "about.html") RequestCache.clear_request_cache() # if the current site does not have associated SiteTheme then get_template_path should return microsite override with patch( "openedx.core.djangoapps.theming.helpers.current_request_has_associated_site_theme", Mock(return_value=False), ): with patch( "openedx.core.djangoapps.theming.helpers.microsite.is_request_in_microsite", Mock(return_value=True), ): with patch("microsite_configuration.microsite.TEMPLATES_BACKEND") as mock_microsite_backend: mock_microsite_backend.get_template_path = Mock(return_value="/microsite/about.html") self.assertEqual(theming_helpers.get_template_path("about.html"), "/microsite/about.html")
def test_setting_override_multiple_times(self): RequestCache.clear_request_cache() self.set_waffle_course_override(self.OVERRIDE_CHOICES.on) self.set_waffle_course_override(self.OVERRIDE_CHOICES.off) override_value = WaffleFlagCourseOverrideModel.override_value( self.WAFFLE_TEST_NAME, self.TEST_COURSE_KEY ) self.assertEqual(override_value, self.OVERRIDE_CHOICES.off)
def lti_consumer_fields_editing_flag(course_id, enabled_for_course=False): """ Yields CourseEditLTIFieldsEnabledFlag record for unit tests Arguments: course_id (CourseLocator): course locator to control this feature for. enabled_for_course (bool): whether feature is enabled for 'course_id' """ RequestCache.clear_request_cache() CourseEditLTIFieldsEnabledFlag.objects.create(course_id=course_id, enabled=enabled_for_course) yield
def assert_access_to_gated_content(self, user): """ Verifies access to gated content for the given user is as expected. """ # clear the request cache to flush any cached access results RequestCache.clear_request_cache() # access to gating content (seq1) remains constant self.assertTrue(bool(has_access(user, 'load', self.seq1, self.course.id))) # access to gated content (seq2) remains constant, access is prevented in SeqModule loading self.assertTrue(bool(has_access(user, 'load', self.seq2, self.course.id)))
def assert_access_to_gated_content(self, user): """ Verifies access to gated content for the given user is as expected. """ # clear the request cache to flush any cached access results RequestCache.clear_request_cache() # access to gating content (seq1) remains constant self.assertTrue( bool(has_access(user, 'load', self.seq1, self.course.id))) # access to gated content (seq2) remains constant, access is prevented in SeqModule loading self.assertTrue( bool(has_access(user, 'load', self.seq2, self.course.id)))
def new_assets_page_feature_flags(global_flag, enabled_for_all_courses=False, course_id=None, enabled_for_course=False): """ Most test cases will use a single call to this manager, as they need to set the global setting and the course-specific setting for a single course. """ RequestCache.clear_request_cache() NewAssetsPageFlag.objects.create( enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses) if course_id: CourseNewAssetsPageFlag.objects.create(course_id=course_id, enabled=enabled_for_course) yield
def clear_caches(cls): """ Clear all of the caches defined in settings.CACHES. """ # N.B. As of 2016-04-20, Django won't return any caches # from django.core.cache.caches.all() that haven't been # accessed using caches[name] previously, so we loop # over our list of overridden caches, instead. for cache in settings.CACHES: caches[cache].clear() # The sites framework caches in a module-level dictionary. # Clear that. sites.models.SITE_CACHE.clear() RequestCache.clear_request_cache()
def persistent_grades_feature_flags(global_flag, enabled_for_all_courses=False, course_id=None, enabled_for_course=False): """ Most test cases will use a single call to this manager, as they need to set the global setting and the course-specific setting for a single course. """ RequestCache.clear_request_cache() PersistentGradesEnabledFlag.objects.create( enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses) if course_id: CoursePersistentGradesFlag.objects.create(course_id=course_id, enabled=enabled_for_course) yield
def persistent_grades_feature_flags( global_flag, enabled_for_all_courses=False, course_id=None, enabled_for_course=False ): """ Most test cases will use a single call to this manager, as they need to set the global setting and the course-specific setting for a single course. """ RequestCache.clear_request_cache() PersistentGradesEnabledFlag.objects.create(enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses) if course_id: CoursePersistentGradesFlag.objects.create(course_id=course_id, enabled=enabled_for_course) yield
def check_question_author(user, content): """ Check if the given user is the author of the original question for both threads and comments. """ if not content: return False try: request_cache_dict = RequestCache.get_request_cache().data if content["type"] == "thread": cache_key = "django_comment_client.permissions._check_condition.check_question_author.{}.{}".format( user.id, content['id'] ) if cache_key in request_cache_dict: return request_cache_dict[cache_key] else: result = content["thread_type"] == "question" and content["user_id"] == str(user.id) request_cache_dict[cache_key] = result return result else: cache_key = "django_comment_client.permissions._check_condition.check_question_author.{}.{}".format( user.id, content['thread_id'] ) if cache_key in request_cache_dict: return request_cache_dict[cache_key] else: # make the now-unavoidable comments service query thread = Thread(id=content['thread_id']).to_dict() return check_question_author(user, thread) except KeyError: return False
def new_assets_page_feature_flags( global_flag, enabled_for_all_courses=False, course_id=None, enabled_for_course=False ): """ Most test cases will use a single call to this manager, as they need to set the global setting and the course-specific setting for a single course. """ RequestCache.clear_request_cache() NewAssetsPageFlag.objects.create(enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses) if course_id: CourseNewAssetsPageFlag.objects.create(course_id=course_id, enabled=enabled_for_course) yield
def check_question_author(user, content): """ Check if the given user is the author of the original question for both threads and comments. """ if not content: return False try: request_cache_dict = RequestCache.get_request_cache().data if content["type"] == "thread": cache_key = "django_comment_client.permissions._check_condition.check_question_author.{}.{}".format( user.id, content['id']) if cache_key in request_cache_dict: return request_cache_dict[cache_key] else: result = content["thread_type"] == "question" and content[ "user_id"] == str(user.id) request_cache_dict[cache_key] = result return result else: cache_key = "django_comment_client.permissions._check_condition.check_question_author.{}.{}".format( user.id, content['thread_id']) if cache_key in request_cache_dict: return request_cache_dict[cache_key] else: # make the now-unavoidable comments service query thread = Thread(id=content['thread_id']).to_dict() return check_question_author(user, thread) except KeyError: return False
def check_team_member(user, content): """ If the content has a commentable_id, verifies that either it is not associated with a team, or if it is, that the user is a member of that team. """ if not content: return False try: commentable_id = content['commentable_id'] request_cache_dict = RequestCache.get_request_cache().data cache_key = u"django_comment_client.check_team_member.{}.{}".format( user.id, commentable_id) if cache_key in request_cache_dict: return request_cache_dict[cache_key] team = get_team(commentable_id) if team is None: passes_condition = True else: passes_condition = team.users.filter(id=user.id).exists() request_cache_dict[cache_key] = passes_condition except KeyError: # We do not expect KeyError in production-- it usually indicates an improper test mock. logging.warning("Did not find key commentable_id in content.") passes_condition = False return passes_condition
def clear_caches(cls): """ Clear all of the caches defined in settings.CACHES. """ # N.B. As of 2016-04-20, Django won't return any caches # from django.core.cache.caches.all() that haven't been # accessed using caches[name] previously, so we loop # over our list of overridden caches, instead. for cache in settings.CACHES: caches[cache].clear() # The sites framework caches in a module-level dictionary. # Clear that. sites.models.SITE_CACHE.clear() RequestCache.clear_request_cache()
def get_current_request(): """ Return current request instance. Returns: (HttpRequest): returns current request """ return RequestCache.get_current_request()
def get_current_request(): """ Return current request instance. Returns: (HttpRequest): returns current request """ return RequestCache.get_current_request()
def test_request_cached_with_changing_kwargs(self): """ Ensure that calling a decorated function with different keyword arguments will not use a cached value invoked by a previous call with different arguments. """ RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [1, 2, 3, 4, 5, 6] self.assertEqual(to_be_wrapped.call_count, 0) def mock_wrapper(*args, **kwargs): """Simple wrapper to let us decorate our mock.""" return to_be_wrapped(*args, **kwargs) wrapped = request_cached(mock_wrapper) # This will be a miss, and make an underlying call. result = wrapped(1, foo=1) self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 1) # This will be a miss, and make an underlying call. result = wrapped(2, foo=2) self.assertEqual(result, 2) self.assertEqual(to_be_wrapped.call_count, 2) # This is bypass of the decorator. direct_result = mock_wrapper(3, foo=3) self.assertEqual(direct_result, 3) self.assertEqual(to_be_wrapped.call_count, 3) # These will be hits, and not make an underlying call. result = wrapped(1, foo=1) self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 3) result = wrapped(2, foo=2) self.assertEqual(result, 2) self.assertEqual(to_be_wrapped.call_count, 3) # Since we're changing foo, this will be a miss. result = wrapped(2, foo=5) self.assertEqual(result, 4) self.assertEqual(to_be_wrapped.call_count, 4)
def test_request_cached_with_changing_kwargs(self): """ Ensure that calling a decorated function with different keyword arguments will not use a cached value invoked by a previous call with different arguments. """ RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [1, 2, 3, 4, 5, 6] self.assertEqual(to_be_wrapped.call_count, 0) def mock_wrapper(*args, **kwargs): """Simple wrapper to let us decorate our mock.""" return to_be_wrapped(*args, **kwargs) wrapped = request_cached(mock_wrapper) # This will be a miss, and make an underlying call. result = wrapped(1, foo=1) self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 1) # This will be a miss, and make an underlying call. result = wrapped(2, foo=2) self.assertEqual(result, 2) self.assertEqual(to_be_wrapped.call_count, 2) # This is bypass of the decorator. direct_result = mock_wrapper(3, foo=3) self.assertEqual(direct_result, 3) self.assertEqual(to_be_wrapped.call_count, 3) # These will be hits, and not make an underlying call. result = wrapped(1, foo=1) self.assertEqual(result, 1) self.assertEqual(to_be_wrapped.call_count, 3) result = wrapped(2, foo=2) self.assertEqual(result, 2) self.assertEqual(to_be_wrapped.call_count, 3) # Since we're changing foo, this will be a miss. result = wrapped(2, foo=5) self.assertEqual(result, 4) self.assertEqual(to_be_wrapped.call_count, 4)
def get_request_user(): """ Helper to get the authenticated user from the current HTTP request (if applicable). If the requester of an unenrollment is not the same person as the student being unenrolled, we authenticate to the commerce service as the requester. """ request = RequestCache.get_current_request() return getattr(request, 'user', None)
def dump_courses_to_neo4j(self, credentials, override_cache=False): """ Method that iterates through a list of courses in a modulestore, serializes them, then submits tasks to write them to neo4j. Arguments: credentials (dict): the necessary credentials to connect to neo4j and create a py2neo `Graph` object override_cache: serialize the courses even if they'be been recently serialized Returns: two lists--one of the courses that were successfully written to neo4j and one of courses that were not. """ total_number_of_courses = len(self.course_keys) submitted_courses = [] skipped_courses = [] graph = authenticate_and_create_graph(credentials) for index, course_key in enumerate(self.course_keys): # first, clear the request cache to prevent memory leaks RequestCache.clear_request_cache() log.info( "Now submitting %s for export to neo4j: course %d of %d total courses", course_key, index + 1, total_number_of_courses, ) if not (override_cache or should_dump_course(course_key, graph)): log.info("skipping submitting %s, since it hasn't changed", course_key) skipped_courses.append(six.text_type(course_key)) continue dump_course_to_neo4j.apply_async( args=[six.text_type(course_key), credentials], ) submitted_courses.append(six.text_type(course_key)) return submitted_courses, skipped_courses
def dump_courses_to_neo4j(self, credentials, override_cache=False): """ Method that iterates through a list of courses in a modulestore, serializes them, then submits tasks to write them to neo4j. Arguments: credentials (dict): the necessary credentials to connect to neo4j and create a py2neo `Graph` object override_cache: serialize the courses even if they'be been recently serialized Returns: two lists--one of the courses that were successfully written to neo4j and one of courses that were not. """ total_number_of_courses = len(self.course_keys) submitted_courses = [] skipped_courses = [] graph = authenticate_and_create_graph(credentials) for index, course_key in enumerate(self.course_keys): # first, clear the request cache to prevent memory leaks RequestCache.clear_request_cache() log.info( "Now submitting %s for export to neo4j: course %d of %d total courses", course_key, index + 1, total_number_of_courses, ) if not (override_cache or should_dump_course(course_key, graph)): log.info("skipping submitting %s, since it hasn't changed", course_key) skipped_courses.append(six.text_type(course_key)) continue dump_course_to_neo4j.apply_async( args=[six.text_type(course_key), credentials], ) submitted_courses.append(six.text_type(course_key)) return submitted_courses, skipped_courses
def test_request_cached_with_none_result(self): """ Ensure that calling a decorated function that returns None properly caches the result and doesn't recall the underlying function. """ RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [None, None, None, 1, 1] self.assertEqual(to_be_wrapped.call_count, 0) def mock_wrapper(*args, **kwargs): """Simple wrapper to let us decorate our mock.""" return to_be_wrapped(*args, **kwargs) wrapped = request_cached(mock_wrapper) # This will be a miss, and make an underlying call. result = wrapped(1) self.assertEqual(result, None) self.assertEqual(to_be_wrapped.call_count, 1) # This will be a miss, and make an underlying call. result = wrapped(2) self.assertEqual(result, None) self.assertEqual(to_be_wrapped.call_count, 2) # This is bypass of the decorator. direct_result = mock_wrapper(3) self.assertEqual(direct_result, None) self.assertEqual(to_be_wrapped.call_count, 3) # These will be hits, and not make an underlying call. result = wrapped(1) self.assertEqual(result, None) self.assertEqual(to_be_wrapped.call_count, 3) result = wrapped(2) self.assertEqual(result, None) self.assertEqual(to_be_wrapped.call_count, 3)
def test_request_cached_with_none_result(self): """ Ensure that calling a decorated function that returns None properly caches the result and doesn't recall the underlying function. """ RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [None, None, None, 1, 1] self.assertEqual(to_be_wrapped.call_count, 0) def mock_wrapper(*args, **kwargs): """Simple wrapper to let us decorate our mock.""" return to_be_wrapped(*args, **kwargs) wrapped = request_cached(mock_wrapper) # This will be a miss, and make an underlying call. result = wrapped(1) self.assertEqual(result, None) self.assertEqual(to_be_wrapped.call_count, 1) # This will be a miss, and make an underlying call. result = wrapped(2) self.assertEqual(result, None) self.assertEqual(to_be_wrapped.call_count, 2) # This is bypass of the decorator. direct_result = mock_wrapper(3) self.assertEqual(direct_result, None) self.assertEqual(to_be_wrapped.call_count, 3) # These will be hits, and not make an underlying call. result = wrapped(1) self.assertEqual(result, None) self.assertEqual(to_be_wrapped.call_count, 3) result = wrapped(2) self.assertEqual(result, None) self.assertEqual(to_be_wrapped.call_count, 3)
def has_permission(user, permission, course_id=None): assert isinstance(course_id, (NoneType, CourseKey)) request_cache_dict = RequestCache.get_request_cache().data cache_key = "django_comment_client.permissions.has_permission.all_permissions.{}.{}".format( user.id, course_id) if cache_key in request_cache_dict: all_permissions = request_cache_dict[cache_key] else: all_permissions = all_permissions_for_user_in_course(user, course_id) request_cache_dict[cache_key] = all_permissions return permission in all_permissions
def test_request_cached_mixed_unicode_str_args(self): """ Ensure that request_cached can work with mixed str and Unicode parameters. """ RequestCache.clear_request_cache() def dummy_function(arg1, arg2): """ A dummy function that expects an str and unicode arguments. """ assert isinstance(arg1, str), 'First parameter has to be of type `str`' assert isinstance(arg2, unicode), 'Second parameter has to be of type `unicode`' return True self.assertTrue(dummy_function('Hello', u'World'), 'Should be callable with ASCII chars') self.assertTrue(dummy_function('H∂llå', u'Wørld'), 'Should be callable with non-ASCII chars') wrapped = request_cached(dummy_function) self.assertTrue(wrapped('Hello', u'World'), 'Wrapper should handle ASCII only chars') self.assertTrue(wrapped('H∂llå', u'Wørld'), 'Wrapper should handle non-ASCII chars')
def has_permission(user, permission, course_id=None): assert isinstance(course_id, (NoneType, CourseKey)) request_cache_dict = RequestCache.get_request_cache().data cache_key = "django_comment_client.permissions.has_permission.all_permissions.{}.{}".format( user.id, course_id ) if cache_key in request_cache_dict: all_permissions = request_cache_dict[cache_key] else: all_permissions = all_permissions_for_user_in_course(user, course_id) request_cache_dict[cache_key] = all_permissions return permission in all_permissions
def test_request_cached_miss_and_then_hit(self): """ Ensure that after a cache miss, we fill the cache and can hit it. """ RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.return_value = 42 self.assertEqual(to_be_wrapped.call_count, 0) def mock_wrapper(*args, **kwargs): """Simple wrapper to let us decorate our mock.""" return to_be_wrapped(*args, **kwargs) wrapped = request_cached(mock_wrapper) result = wrapped() self.assertEqual(result, 42) self.assertEqual(to_be_wrapped.call_count, 1) result = wrapped() self.assertEqual(result, 42) self.assertEqual(to_be_wrapped.call_count, 1)
def test_request_cached_miss_and_then_hit(self): """ Ensure that after a cache miss, we fill the cache and can hit it. """ RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.return_value = 42 self.assertEqual(to_be_wrapped.call_count, 0) def mock_wrapper(*args, **kwargs): """Simple wrapper to let us decorate our mock.""" return to_be_wrapped(*args, **kwargs) wrapped = request_cached(mock_wrapper) result = wrapped() self.assertEqual(result, 42) self.assertEqual(to_be_wrapped.call_count, 1) result = wrapped() self.assertEqual(result, 42) self.assertEqual(to_be_wrapped.call_count, 1)
def _bookmarks_cache(self, course_key, fetch=False): """ Return the user's bookmarks cache for a particular course. Arguments: course_key (CourseKey): course_key of the course whose bookmarks cache should be returned. fetch (Bool): if the bookmarks should be fetched and cached if they already aren't. """ store = modulestore() course_key = store.fill_in_run(course_key) if course_key.run is None: return [] cache_key = CACHE_KEY_TEMPLATE.format(self._user.id, course_key) bookmarks_cache = RequestCache.get_request_cache().data.get(cache_key, None) if bookmarks_cache is None and fetch is True: bookmarks_cache = api.get_bookmarks( self._user, course_key=course_key, fields=DEFAULT_FIELDS ) RequestCache.get_request_cache().data[cache_key] = bookmarks_cache return bookmarks_cache
def _bookmarks_cache(self, course_key, fetch=False): """ Return the user's bookmarks cache for a particular course. Arguments: course_key (CourseKey): course_key of the course whose bookmarks cache should be returned. fetch (Bool): if the bookmarks should be fetched and cached if they already aren't. """ store = modulestore() course_key = store.fill_in_run(course_key) if course_key.run is None: return [] cache_key = CACHE_KEY_TEMPLATE.format(self._user.id, course_key) bookmarks_cache = RequestCache.get_request_cache().data.get( cache_key, None) if bookmarks_cache is None and fetch is True: bookmarks_cache = api.get_bookmarks(self._user, course_key=course_key, fields=DEFAULT_FIELDS) RequestCache.get_request_cache().data[cache_key] = bookmarks_cache return bookmarks_cache
def test_request_context_caching(self): """ Test that the RequestContext is cached in the RequestCache. """ with patch('edxmako.request_context.get_current_request', return_value=None): # requestcontext should be None, because the cache isn't filled self.assertIsNone(get_template_request_context()) with patch('edxmako.request_context.get_current_request', return_value=self.request): # requestcontext should not be None, and should fill the cache self.assertIsNotNone(get_template_request_context()) mock_get_current_request = Mock() with patch('edxmako.request_context.get_current_request', mock_get_current_request): # requestcontext should not be None, because the cache is filled self.assertIsNotNone(get_template_request_context()) mock_get_current_request.assert_not_called() RequestCache.clear_request_cache() with patch('edxmako.request_context.get_current_request', return_value=None): # requestcontext should be None, because the cache isn't filled self.assertIsNone(get_template_request_context())
def instrument_course_progress_render( self, course_width, enable_ccx, view_as_ccx, sql_queries, mongo_reads, ): """ Renders the progress page, instrumenting Mongo reads and SQL queries. """ course_key = self.setup_course(course_width, enable_ccx, view_as_ccx) # Switch to published-only mode to simulate the LMS with self.settings(MODULESTORE_BRANCH='published-only'): # Clear all caches before measuring for cache in settings.CACHES: caches[cache].clear() # Refill the metadata inheritance cache get_course_in_cache(course_key) # We clear the request cache to simulate a new request in the LMS. RequestCache.clear_request_cache() # Reset the list of provider classes, so that our django settings changes # can actually take affect. OverrideFieldData.provider_classes = None with self.assertNumQueries( sql_queries, using='default', table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with self.assertNumQueries(0, using='student_module_history'): with self.assertMongoCallCount(mongo_reads): with self.assertXBlockInstantiations(1): self.grade_course(course_key)
def __init__(self, **kwargs): request_cache_dict = RequestCache.get_request_cache().data store = modulestore() services = kwargs.setdefault('services', {}) services['completion'] = CompletionService( user=kwargs.get('user'), course_key=kwargs.get('course_id')) services['fs'] = xblock.reference.plugins.FSService() services['i18n'] = ModuleI18nService services['library_tools'] = LibraryToolsService(store) services['partitions'] = PartitionService( course_id=kwargs.get('course_id'), cache=request_cache_dict) services['settings'] = SettingsService() services['user_tags'] = UserTagsService(self) if badges_enabled(): services['badging'] = BadgingService( course_id=kwargs.get('course_id'), modulestore=store) self.request_token = kwargs.pop('request_token', None) super(LmsModuleSystem, self).__init__(**kwargs)
def __init__(self, **kwargs): request_cache_dict = RequestCache.get_request_cache().data store = modulestore() services = kwargs.setdefault('services', {}) services['completion'] = CompletionService(user=kwargs.get('user'), course_key=kwargs.get('course_id')) services['fs'] = xblock.reference.plugins.FSService() services['i18n'] = ModuleI18nService services['library_tools'] = LibraryToolsService(store) services['partitions'] = PartitionService( course_id=kwargs.get('course_id'), cache=request_cache_dict ) services['settings'] = SettingsService() services['user_tags'] = UserTagsService(self) if badges_enabled(): services['badging'] = BadgingService(course_id=kwargs.get('course_id'), modulestore=store) self.request_token = kwargs.pop('request_token', None) super(LmsModuleSystem, self).__init__(**kwargs)
def _providers_for_block(cls, block): """ Computes a list of enabled providers based on the given XBlock. The result is cached per request to avoid the overhead incurred by filtering override providers hundreds of times. Arguments: block: An XBlock """ course_id = unicode(block.location.course_key) cache_key = ENABLED_MODULESTORE_OVERRIDE_PROVIDERS_KEY.format(course_id=course_id) request_cache = RequestCache.get_request_cache() enabled_providers = request_cache.data.get(cache_key) if enabled_providers is None: enabled_providers = [ provider_class for provider_class in cls.provider_classes if provider_class.enabled_for(block) ] request_cache.data[cache_key] = enabled_providers return enabled_providers
def _providers_for_course(cls, course): """ Return a filtered list of enabled providers based on the course passed in. Cache this result per request to avoid needing to call the provider filter api hundreds of times. Arguments: course: The course XBlock """ request_cache = RequestCache.get_request_cache() if course is None: cache_key = ENABLED_OVERRIDE_PROVIDERS_KEY.format(course_id='None') else: cache_key = ENABLED_OVERRIDE_PROVIDERS_KEY.format(course_id=unicode(course.id)) enabled_providers = request_cache.data.get(cache_key, NOTSET) if enabled_providers == NOTSET: enabled_providers = tuple( (provider_class for provider_class in cls.provider_classes if provider_class.enabled_for(course)) ) request_cache.data[cache_key] = enabled_providers return enabled_providers
def check_team_member(user, content): """ If the content has a commentable_id, verifies that either it is not associated with a team, or if it is, that the user is a member of that team. """ if not content: return False try: commentable_id = content['commentable_id'] request_cache_dict = RequestCache.get_request_cache().data cache_key = u"django_comment_client.check_team_member.{}.{}".format(user.id, commentable_id) if cache_key in request_cache_dict: return request_cache_dict[cache_key] team = get_team(commentable_id) if team is None: passes_condition = True else: passes_condition = team.users.filter(id=user.id).exists() request_cache_dict[cache_key] = passes_condition except KeyError: # We do not expect KeyError in production-- it usually indicates an improper test mock. logging.warning("Did not find key commentable_id in content.") passes_condition = False return passes_condition
def invalidate_credit_requirement_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of credit requirements. """ RequestCache.clear_request_cache(name=CreditRequirement.CACHE_NAMESPACE)
def invalidate_course_mode_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of course modes. """ RequestCache.clear_request_cache(name=CourseMode.CACHE_NAMESPACE)
def invalidate_credit_requirement_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of credit requirements. """ RequestCache.clear_request_cache(name=CreditRequirement.CACHE_NAMESPACE)
def invalidate_course_mode_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of course modes. """ RequestCache.clear_request_cache(name=CourseMode.CACHE_NAMESPACE)
def create_modulestore_instance( engine, content_store, doc_store_config, options, i18n_service=None, fs_service=None, user_service=None, signal_handler=None, ): """ This will return a new instance of a modulestore given an engine and options """ # Import is placed here to avoid model import at project startup. import xblock.reference.plugins class_ = load_function(engine) _options = {} _options.update(options) FUNCTION_KEYS = ['render_template'] for key in FUNCTION_KEYS: if key in _options and isinstance(_options[key], basestring): _options[key] = load_function(_options[key]) request_cache = RequestCache.get_request_cache() try: metadata_inheritance_cache = caches['mongo_metadata_inheritance'] except InvalidCacheBackendError: metadata_inheritance_cache = caches['default'] if issubclass(class_, MixedModuleStore): _options['create_modulestore_instance'] = create_modulestore_instance if issubclass(class_, BranchSettingMixin): _options['branch_setting_func'] = _get_modulestore_branch_setting if HAS_USER_SERVICE and not user_service: xb_user_service = DjangoXBlockUserService(get_current_user()) else: xb_user_service = None xblock_field_data_wrappers = [load_function(path) for path in settings.XBLOCK_FIELD_DATA_WRAPPERS] def fetch_disabled_xblock_types(): """ Get the disabled xblock names, using the request_cache if possible to avoid hitting a database every time the list is needed. """ # If the import could not be loaded, return an empty list. if disabled_xblocks is None: return [] if request_cache: if 'disabled_xblock_types' not in request_cache.data: request_cache.data['disabled_xblock_types'] = [block.name for block in disabled_xblocks()] return request_cache.data['disabled_xblock_types'] else: disabled_xblock_types = [block.name for block in disabled_xblocks()] return disabled_xblock_types return class_( contentstore=content_store, metadata_inheritance_cache_subsystem=metadata_inheritance_cache, request_cache=request_cache, xblock_mixins=getattr(settings, 'XBLOCK_MIXINS', ()), xblock_select=getattr(settings, 'XBLOCK_SELECT_FUNCTION', None), xblock_field_data_wrappers=xblock_field_data_wrappers, disabled_xblock_types=fetch_disabled_xblock_types, doc_store_config=doc_store_config, i18n_service=i18n_service or ModuleI18nService, fs_service=fs_service or xblock.reference.plugins.FSService(), user_service=user_service or xb_user_service, signal_handler=signal_handler or SignalHandler(class_), **_options )
def setUp(self): super(TestCourseWaffleFlag, self).setUp() request = RequestFactory().request() crum.set_current_request(request) RequestCache.clear_request_cache()
def create_modulestore_instance( engine, content_store, doc_store_config, options, i18n_service=None, fs_service=None, user_service=None, signal_handler=None, ): """ This will return a new instance of a modulestore given an engine and options """ # Import is placed here to avoid model import at project startup. import xblock.reference.plugins class_ = load_function(engine) _options = {} _options.update(options) FUNCTION_KEYS = ['render_template'] for key in FUNCTION_KEYS: if key in _options and isinstance(_options[key], basestring): _options[key] = load_function(_options[key]) request_cache = RequestCache.get_request_cache() try: metadata_inheritance_cache = caches['mongo_metadata_inheritance'] except InvalidCacheBackendError: metadata_inheritance_cache = caches['default'] if issubclass(class_, MixedModuleStore): _options['create_modulestore_instance'] = create_modulestore_instance if issubclass(class_, BranchSettingMixin): _options['branch_setting_func'] = _get_modulestore_branch_setting if HAS_USER_SERVICE and not user_service: xb_user_service = DjangoXBlockUserService(get_current_user()) else: xb_user_service = None xblock_field_data_wrappers = [ load_function(path) for path in settings.XBLOCK_FIELD_DATA_WRAPPERS ] def fetch_disabled_xblock_types(): """ Get the disabled xblock names, using the request_cache if possible to avoid hitting a database every time the list is needed. """ # If the import could not be loaded, return an empty list. if disabled_xblocks is None: return [] if request_cache: if 'disabled_xblock_types' not in request_cache.data: request_cache.data['disabled_xblock_types'] = [ block.name for block in disabled_xblocks() ] return request_cache.data['disabled_xblock_types'] else: disabled_xblock_types = [ block.name for block in disabled_xblocks() ] return disabled_xblock_types return class_( contentstore=content_store, metadata_inheritance_cache_subsystem=metadata_inheritance_cache, request_cache=request_cache, xblock_mixins=getattr(settings, 'XBLOCK_MIXINS', ()), xblock_select=getattr(settings, 'XBLOCK_SELECT_FUNCTION', None), xblock_field_data_wrappers=xblock_field_data_wrappers, disabled_xblock_types=fetch_disabled_xblock_types, doc_store_config=doc_store_config, i18n_service=i18n_service or ModuleI18nService, fs_service=fs_service or xblock.reference.plugins.FSService(), user_service=user_service or xb_user_service, signal_handler=signal_handler or SignalHandler(class_), **_options)
def invalidate_verified_track_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of VerifiedTrackCohortedCourse. """ RequestCache.clear_request_cache(name=VerifiedTrackCohortedCourse.CACHE_NAMESPACE)
def invalidate_verified_track_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of VerifiedTrackCohortedCourse. """ RequestCache.clear_request_cache(name=VerifiedTrackCohortedCourse.CACHE_NAMESPACE)
def setUp(self): super(TestCourseWaffleFlag, self).setUp() request = RequestFactory().request() crum.set_current_request(request) RequestCache.clear_request_cache()
def setUp(self): super(OverrideWaffleFlagTests, self).setUp() request = RequestFactory().request() self.addCleanup(crum.set_current_request, None) crum.set_current_request(request) RequestCache.clear_request_cache()