class LibraryToolsServiceTest(MixedSplitTestCase): """ Tests for library service. """ shard = 1 def setUp(self): super(LibraryToolsServiceTest, self).setUp() self.tools = LibraryToolsService(self.store) def test_list_available_libraries(self): """ Test listing of libraries. """ _ = LibraryFactory.create(modulestore=self.store) all_libraries = self.tools.list_available_libraries() self.assertTrue(all_libraries) self.assertEqual(len(all_libraries), 1) @patch('xmodule.modulestore.split_mongo.split.SplitMongoModuleStore.get_library_summaries') def test_list_available_libraries_fetch(self, mock_get_library_summaries): """ Test that library list is compiled using light weight library summary objects. """ _ = self.tools.list_available_libraries() self.assertTrue(mock_get_library_summaries.called)
class LibraryToolsServiceTest(MixedSplitTestCase): """ Tests for library service. """ def setUp(self): super().setUp() self.tools = LibraryToolsService(self.store, self.user_id) def test_list_available_libraries(self): """ Test listing of libraries. """ _ = LibraryFactory.create(modulestore=self.store) all_libraries = self.tools.list_available_libraries() self.assertTrue(all_libraries) self.assertEqual(len(all_libraries), 1) @patch( 'xmodule.modulestore.split_mongo.split.SplitMongoModuleStore.get_library_summaries' ) def test_list_available_libraries_fetch(self, mock_get_library_summaries): """ Test that library list is compiled using light weight library summary objects. """ _ = self.tools.list_available_libraries() self.assertTrue(mock_get_library_summaries.called)
class ContentLibraryToolsTest(MixedSplitTestCase, ContentLibrariesRestApiTest): """ Tests for LibraryToolsService which interact with blockstore-based content libraries """ def setUp(self): super().setUp() self.tools = LibraryToolsService(self.store, self.user.id) def test_import_from_blockstore(self): # Create a blockstore content library library = self._create_library(slug="testlib1_import", title="A Test Library", description="Testing XBlocks") # Create a unit block with an HTML block in it. unit_block_id = self._add_block_to_library(library["id"], "unit", "unit1")["id"] html_block_id = self._add_block_to_library(library["id"], "html", "html1", parent_block=unit_block_id)["id"] html_block = load_block(UsageKey.from_string(html_block_id), self.user) # Add assets and content to the HTML block self._set_library_block_asset(html_block_id, "test.txt", b"data", expect_response=200) self._set_library_block_olx(html_block_id, '<html><a href="/static/test.txt">Hello world</a></html>') # Create a modulestore course course = CourseFactory.create(modulestore=self.store, user_id=self.user.id) CourseInstructorRole(course.id).add_users(self.user) # Add Source from library block to the course sourced_block = self.make_block("library_sourced", course, user_id=self.user.id) # Import the unit block from the library to the course self.tools.import_from_blockstore(sourced_block, unit_block_id) # Verify imported block with its children self.assertEqual(len(sourced_block.children), 1) self.assertEqual(sourced_block.children[0].category, 'unit') imported_unit_block = self.store.get_item(sourced_block.children[0]) self.assertEqual(len(imported_unit_block.children), 1) self.assertEqual(imported_unit_block.children[0].category, 'html') imported_html_block = self.store.get_item(imported_unit_block.children[0]) self.assertIn('Hello world', imported_html_block.data) # Check that assets were imported and static paths were modified after importing assets = library_api.get_library_block_static_asset_files(html_block.scope_ids.usage_id) self.assertEqual(len(assets), 1) self.assertIn(assets[0].url, imported_html_block.data) # Check that reimporting updates the target block self._set_library_block_olx(html_block_id, '<html><a href="/static/test.txt">Foo bar</a></html>') self.tools.import_from_blockstore(sourced_block, unit_block_id) self.assertEqual(len(sourced_block.children), 1) imported_unit_block = self.store.get_item(sourced_block.children[0]) self.assertEqual(len(imported_unit_block.children), 1) imported_html_block = self.store.get_item(imported_unit_block.children[0]) self.assertNotIn('Hello world', imported_html_block.data) self.assertIn('Foo bar', imported_html_block.data)
def __init__(self, **kwargs): request_cache_dict = DEFAULT_REQUEST_CACHE.data store = modulestore() services = kwargs.setdefault('services', {}) user = kwargs.get('user') if user and user.is_authenticated: services['completion'] = CompletionService( user=user, context_key=kwargs.get('course_id')) services['fs'] = xblock.reference.plugins.FSService() services['i18n'] = ModuleI18nService services['library_tools'] = LibraryToolsService( store, user_id=user.id if user else None) 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) services['teams'] = TeamsService() services['teams_configuration'] = TeamsConfigurationService() services['call_to_action'] = CallToActionService() super(LmsModuleSystem, self).__init__(**kwargs)
def _preview_module_system(request, descriptor, field_data): """ Returns a ModuleSystem for the specified descriptor that is specialized for rendering module previews. request: The active django request descriptor: An XModuleDescriptor """ course_id = descriptor.location.course_key display_name_only = (descriptor.category == 'static_tab') wrappers = [ # This wrapper wraps the module in the template specified above partial(wrap_xblock, 'PreviewRuntime', display_name_only=display_name_only, usage_id_serializer=unicode, request_token=request_token(request)), # This wrapper replaces urls in the output that start with /static # with the correct course-specific url for the static content partial(replace_static_urls, None, course_id=course_id), _studio_wrap_xblock, ] descriptor.runtime._services[ 'studio_user_permissions'] = StudioPermissionsService(request) # pylint: disable=protected-access return PreviewModuleSystem( static_url=settings.STATIC_URL, # TODO (cpennington): Do we want to track how instructors are using the preview problems? track_function=lambda event_type, event: None, filestore=descriptor.runtime.resources_fs, get_module=partial(_load_preview_module, request), render_template=render_from_lms, debug=True, replace_urls=partial(static_replace.replace_static_urls, data_directory=None, course_id=course_id), user=request.user, can_execute_unsafe_code=(lambda: can_execute_unsafe_code(course_id)), get_python_lib_zip=( lambda: get_python_lib_zip(contentstore, course_id)), mixins=settings.XBLOCK_MIXINS, course_id=course_id, anonymous_student_id='student', # Set up functions to modify the fragment produced by student_view wrappers=wrappers, error_descriptor_class=ErrorDescriptor, get_user_role=lambda: get_user_role(request.user, course_id), descriptor_runtime=descriptor.runtime, services={ "i18n": ModuleI18nService(), "field-data": field_data, "library_tools": LibraryToolsService(modulestore()), "user": DjangoXBlockUserService(request.user), }, )
def setUpClass(cls): super(LibraryTestCase, cls).setUpClass() cls.library = LibraryFactory.create(modulestore=cls.store) creator = UserFactory() cls.library_blocks = [ ItemFactory.create( category="html", parent=cls.library, parent_location=cls.library.location, publish_item=False, user_id=creator.id, modulestore=cls.store, ) for _ in range(3) ] cls.libtools = LibraryToolsService(cls.store) cls.store.update_item(cls.library, creator.id) with cls.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, None): cls.course = CourseFactory.create(modulestore=cls.store) cls.course_key = cls.course.id with cls.store.bulk_operations(cls.course_key, emit_signals=False): cls.chapter = ItemFactory.create( parent=cls.course, parent_location=cls.course.location, category="chapter", modulestore=cls.store, publish_item=False, ) cls.sequential = ItemFactory.create( parent=cls.chapter, parent_location=cls.chapter.location, category='sequential', modulestore=cls.store, publish_item=False, ) cls.vertical = ItemFactory.create( parent=cls.sequential, parent_location=cls.sequential.location, category='vertical', modulestore=cls.store, publish_item=False, ) cls.lc_block = ItemFactory.create( category="library_content", parent=cls.vertical, parent_location=cls.vertical.location, max_count=2, source_library_id=str(cls.library.location.library_key), modulestore=cls.store, ) # copy children from library to content block (LibaryContentDescriptor.tools.update_children?) cls.store.update_item(cls.course, creator.id) cls.store.update_item(cls.lc_block, creator.id)
def __init__(self, **kwargs): services = kwargs.setdefault('services', {}) services['user_tags'] = UserTagsService(self) services['partitions'] = LmsPartitionService( user=kwargs.get('user'), course_id=kwargs.get('course_id'), track_function=kwargs.get('track_function', None), ) services['library_tools'] = LibraryToolsService(modulestore()) services['fs'] = xblock.reference.plugins.FSService() 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 services = kwargs.setdefault('services', {}) services['fs'] = xblock.reference.plugins.FSService() services['i18n'] = ModuleI18nService services['library_tools'] = LibraryToolsService(modulestore()) services['partitions'] = PartitionService( course_id=kwargs.get('course_id'), cache=request_cache_dict) store = modulestore() 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 _bind_course_module(self, module): """ Bind a module (part of self.course) so we can access student-specific data. """ module_system = get_test_system(course_id=module.location.course_key) module_system.descriptor_runtime = module.runtime._descriptor_system # pylint: disable=protected-access module_system._services['library_tools'] = LibraryToolsService(self.store) # pylint: disable=protected-access def get_module(descriptor): """Mocks module_system get_module function""" sub_module_system = get_test_system(course_id=module.location.course_key) sub_module_system.get_module = get_module sub_module_system.descriptor_runtime = descriptor._runtime # pylint: disable=protected-access descriptor.bind_for_student(sub_module_system, self.user.id) return descriptor module_system.get_module = get_module module.xmodule_runtime = module_system
def setUp(self): super().setUp() self.tools = LibraryToolsService(self.store, self.user_id) self.library = LibraryFactory.create(modulestore=self.store) self.lib_blocks = [ self.make_block("html", self.library, data=f"Hello world from block {i}") for i in range(1, 5) ] self.course = CourseFactory.create(modulestore=self.store) self.chapter = self.make_block("chapter", self.course) self.sequential = self.make_block("sequential", self.chapter) self.vertical = self.make_block("vertical", self.sequential) self.lc_block = self.make_block( "library_content", self.vertical, max_count=1, source_library_id=str(self.library.location.library_key) )
def setUp(self): super(LibraryContentTest, self).setUp() self.tools = LibraryToolsService(self.store) self.library = LibraryFactory.create(modulestore=self.store) self.lib_blocks = [ self.make_block("html", self.library, data="Hello world from block {}".format(i)) for i in range(1, 5) ] self.course = CourseFactory.create(modulestore=self.store) self.chapter = self.make_block("chapter", self.course) self.sequential = self.make_block("sequential", self.chapter) self.vertical = self.make_block("vertical", self.sequential) self.lc_block = self.make_block( "library_content", self.vertical, max_count=1, source_libraries=[LibraryVersionReference(self.library.location.library_key)] )
def __init__(self, modulestore, course_entry, default_class, module_data, lazy, **kwargs): """ Computes the settings inheritance and sets up the cache. modulestore: the module store that can be used to retrieve additional modules course_entry: the originally fetched enveloped course_structure w/ branch and course id info. Callers to _load_item provide an override but that function ignores the provided structure and only looks at the branch and course id module_data: a dict mapping Location -> json that was cached from the underlying modulestore """ # needed by capa_problem (as runtime.filestore via this.resources_fs) if course_entry.course_key.course: root = modulestore.fs_root / course_entry.course_key.org / course_entry.course_key.course / course_entry.course_key.run else: root = modulestore.fs_root / str(course_entry.structure['_id']) root.makedirs_p() # create directory if it doesn't exist id_manager = SplitMongoIdManager(self) kwargs.setdefault('id_reader', id_manager) kwargs.setdefault('id_generator', id_manager) super(CachingDescriptorSystem, self).__init__(field_data=None, load_item=self._load_item, resources_fs=OSFS(root), **kwargs) self.modulestore = modulestore self.course_entry = course_entry # set course_id attribute to avoid problems with subsystems that expect # it here. (grading, for example) self.course_id = course_entry.course_key self.lazy = lazy self.module_data = module_data self.default_class = default_class self.local_modules = {} self._services['library_tools'] = LibraryToolsService(modulestore, user_id=None)
def setUp(self): super(LibraryContentTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.tools = LibraryToolsService(self.store, self.user_id) self.library = LibraryFactory.create(modulestore=self.store) self.lib_blocks = [ self.make_block("html", self.library, data="Hello world from block {}".format(i)) for i in range(1, 5) ] self.course = CourseFactory.create(modulestore=self.store) self.chapter = self.make_block("chapter", self.course) self.sequential = self.make_block("sequential", self.chapter) self.vertical = self.make_block("vertical", self.sequential) self.lc_block = self.make_block("library_content", self.vertical, max_count=1, source_library_id=six.text_type( self.library.location.library_key))
def setUp(self): super().setUp() self.tools = LibraryToolsService(self.store, self.user.id)
def setUp(self): super(LibraryToolsServiceTest, self).setUp() self.tools = LibraryToolsService(self.store)
def _update_and_import_module( module, store, user_id, source_course_id, dest_course_id, do_import_static=True, runtime=None): """ Update all the module reference fields to the destination course id, then import the module into the destination course. """ logging.debug(u'processing import of module %s...', unicode(module.location)) def _update_module_references(module, source_course_id, dest_course_id): """ Move the module to a new course. """ def _convert_ref_fields_to_new_namespace(reference): # pylint: disable=invalid-name """ Convert a reference to the new namespace, but only if the original namespace matched the original course. Otherwise, returns the input value. """ assert isinstance(reference, UsageKey) if source_course_id == reference.course_key: return reference.map_into_course(dest_course_id) else: return reference fields = {} for field_name, field in module.fields.iteritems(): if field.scope != Scope.parent and field.is_set_on(module): if isinstance(field, Reference): value = field.read_from(module) if value is None: fields[field_name] = None else: fields[field_name] = _convert_ref_fields_to_new_namespace(field.read_from(module)) elif isinstance(field, ReferenceList): references = field.read_from(module) fields[field_name] = [_convert_ref_fields_to_new_namespace(reference) for reference in references] elif isinstance(field, ReferenceValueDict): reference_dict = field.read_from(module) fields[field_name] = { key: _convert_ref_fields_to_new_namespace(reference) for key, reference in reference_dict.iteritems() } elif field_name == 'xml_attributes': value = field.read_from(module) # remove any export/import only xml_attributes # which are used to wire together draft imports if 'parent_url' in value: del value['parent_url'] if 'parent_sequential_url' in value: del value['parent_sequential_url'] if 'index_in_children_list' in value: del value['index_in_children_list'] fields[field_name] = value else: fields[field_name] = field.read_from(module) return fields if do_import_static and 'data' in module.fields and isinstance(module.fields['data'], xblock.fields.String): # we want to convert all 'non-portable' links in the module_data # (if it is a string) to portable strings (e.g. /static/) module.data = rewrite_nonportable_content_links( source_course_id, dest_course_id, module.data ) fields = _update_module_references(module, source_course_id, dest_course_id) asides = module.get_asides() if isinstance(module, XModuleMixin) else None block = store.import_xblock( user_id, dest_course_id, module.location.category, module.location.block_id, fields, runtime, asides=asides ) # TODO: Move this code once the following condition is met. # Get to the point where XML import is happening inside the # modulestore that is eventually going to store the data. # Ticket: https://openedx.atlassian.net/browse/PLAT-1046 if block.location.category == 'library_content': # if library exists, update source_library_version and children # according to this existing library and library content block. if store.get_library(block.source_library_key): LibraryToolsService(store).update_children( block, user_id, version=block.source_library_version ) return block