def test_migrate_published_info(self): """ Tests that blocks that were storing published_date and published_by through CMSBlockMixin are loaded correctly """ # Insert the test block directly into the module store location = Location("edX", "migration", "2012_Fall", "html", "test_html") published_date = datetime(1970, 1, 1, tzinfo=UTC) published_by = 123 self.draft_store._update_single_item( as_draft(location), { "definition.data": {}, "metadata": { # published_date was previously stored as a list of time components, not a datetime "published_date": list(published_date.timetuple()), "published_by": published_by, }, }, allow_not_found=True, ) # Retrieve the block and verify its fields component = self.draft_store.get_item(location) self.assertEqual(component.published_on, published_date) self.assertEqual(component.published_by, published_by)
def create_xmodule(self, location, definition_data=None, metadata=None, runtime=None, fields={}, **kwargs): """ Create the new xmodule but don't save it. Returns the new module with a draft locator if the category allows drafts. If the category does not allow drafts, just creates a published module. :param location: a Location--must have a category :param definition_data: can be empty. The initial definition_data for the kvs :param metadata: can be empty, the initial metadata for the kvs :param runtime: if you already have an xmodule from the course, the xmodule.runtime value :param fields: a dictionary of field names and values for the new xmodule """ self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) if location.category not in DIRECT_ONLY_CATEGORIES: location = as_draft(location) return wrap_draft( super(DraftModuleStore, self).create_xmodule(location, definition_data, metadata, runtime, fields))
def test_migrate_published_info(self): """ Tests that blocks that were storing published_date and published_by through CMSBlockMixin are loaded correctly """ # Insert the test block directly into the module store location = Location('edX', 'migration', '2012_Fall', 'html', 'test_html') published_date = datetime(1970, 1, 1, tzinfo=UTC) published_by = 123 self.draft_store._update_single_item( as_draft(location), { 'definition.data': {}, 'metadata': { # published_date was previously stored as a list of time components, not a datetime 'published_date': list(published_date.timetuple()), 'published_by': published_by, }, }, ) # Retrieve the block and verify its fields component = self.draft_store.get_item(location) self.assertEqual(component.published_date, published_date) self.assertEqual(component.published_by, published_by)
def _query_children_for_cache_children(self, course_key, items): # first get non-draft in a round-trip to_process_non_drafts = super(DraftModuleStore, self)._query_children_for_cache_children(course_key, items) to_process_dict = {} for non_draft in to_process_non_drafts: to_process_dict[BlockUsageLocator._from_deprecated_son(non_draft["_id"], course_key.run)] = non_draft if self.get_branch_setting() == ModuleStoreEnum.Branch.draft_preferred: # now query all draft content in another round-trip query = [] for item in items: item_usage_key = UsageKey.from_string(item).map_into_course(course_key) if item_usage_key.block_type not in DIRECT_ONLY_CATEGORIES: query.append(as_draft(item_usage_key).to_deprecated_son()) if query: query = {'_id': {'$in': query}} to_process_drafts = list(self.collection.find(query)) # now we have to go through all drafts and replace the non-draft # with the draft. This is because the semantics of the DraftStore is to # always return the draft - if available for draft in to_process_drafts: draft_loc = BlockUsageLocator._from_deprecated_son(draft["_id"], course_key.run) draft_as_non_draft_loc = as_published(draft_loc) # does non-draft exist in the collection # if so, replace it if draft_as_non_draft_loc in to_process_dict: to_process_dict[draft_as_non_draft_loc] = draft # convert the dict - which is used for look ups - back into a list queried_children = to_process_dict.values() return queried_children
def update_item(self, xblock, user_id, allow_not_found=False, force=False, isPublish=False): """ See superclass doc. In addition to the superclass's behavior, this method converts the unit to draft if it's not direct-only and not already draft. """ self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) # if the xblock is direct-only, update the PUBLISHED version if xblock.location.category in DIRECT_ONLY_CATEGORIES: return super(DraftModuleStore, self).update_item(xblock, user_id, allow_not_found) draft_loc = as_draft(xblock.location) if not super(DraftModuleStore, self).has_item(draft_loc): try: # ignore any descendants which are already draft self._convert_to_draft(xblock.location, user_id, ignore_if_draft=True) except ItemNotFoundError as exception: # ignore the exception only if allow_not_found is True and # the item that wasn't found is the one that was passed in # we make this extra location check so we do not hide errors when converting any children to draft if not (allow_not_found and exception.args[0] == xblock.location): raise xblock.location = draft_loc super(DraftModuleStore, self).update_item(xblock, user_id, allow_not_found, isPublish=isPublish) return wrap_draft(xblock)
def _query_children_for_cache_children(self, course_key, items): # first get non-draft in a round-trip to_process_non_drafts = super(DraftModuleStore, self)._query_children_for_cache_children(course_key, items) to_process_dict = {} for non_draft in to_process_non_drafts: to_process_dict[Location._from_deprecated_son(non_draft["_id"], course_key.run)] = non_draft if self.branch_setting_func() == ModuleStoreEnum.Branch.draft_preferred: # now query all draft content in another round-trip query = [] for item in items: item_usage_key = course_key.make_usage_key_from_deprecated_string(item) if item_usage_key.category not in DIRECT_ONLY_CATEGORIES: query.append(as_draft(item_usage_key).to_deprecated_son()) if query: query = {'_id': {'$in': query}} to_process_drafts = list(self.collection.find(query)) # now we have to go through all drafts and replace the non-draft # with the draft. This is because the semantics of the DraftStore is to # always return the draft - if available for draft in to_process_drafts: draft_loc = Location._from_deprecated_son(draft["_id"], course_key.run) draft_as_non_draft_loc = as_published(draft_loc) # does non-draft exist in the collection # if so, replace it if draft_as_non_draft_loc in to_process_dict: to_process_dict[draft_as_non_draft_loc] = draft # convert the dict - which is used for look ups - back into a list queried_children = to_process_dict.values() return queried_children
def get_draft(): return wrap_draft( super(DraftModuleStore, self).get_item( # lint-amnesty, pylint: disable=super-with-arguments as_draft(usage_key), depth=depth, using_descriptor_system=using_descriptor_system, for_parent=kwargs.get('for_parent')))
def test_migrate_published_info(self): """ Tests that blocks that were storing published_date and published_by through CMSBlockMixin are loaded correctly """ # Insert the test block directly into the module store location = Location('edX', 'migration', '2012_Fall', 'html', 'test_html') published_date = datetime(1970, 1, 1, tzinfo=UTC) published_by = 123 self.draft_store._update_single_item( as_draft(location), { 'definition.data': {}, 'metadata': { # published_date was previously stored as a list of time components, not a datetime 'published_date': list(published_date.timetuple()), 'published_by': published_by, }, }, allow_not_found=True, ) # Retrieve the block and verify its fields component = self.draft_store.get_item(location) self.assertEqual(component.published_on, published_date) self.assertEqual(component.published_by, published_by)
def get_draft(): return wrap_draft( super(DraftModuleStore, self).get_item( as_draft(usage_key), depth=depth, using_descriptor_system=using_descriptor_system, for_parent=kwargs.get('for_parent')))
def _internal_depth_first(item_location, is_root): """ Depth first publishing from the given location """ try: # handle child does not exist w/o killing publish item = self.get_item(item_location) except ItemNotFoundError: log.warning('Cannot find: %s', item_location) return # publish the children first if item.has_children: for child_loc in item.children: _internal_depth_first(child_loc, False) if item_location.block_type in DIRECT_ONLY_CATEGORIES or not getattr( item, 'is_draft', False): # ignore noop attempt to publish something that can't be or isn't currently draft return # try to find the originally PUBLISHED version, if it exists try: original_published = super(DraftModuleStore, self).get_item(item_location) # lint-amnesty, pylint: disable=super-with-arguments except ItemNotFoundError: original_published = None # if the category of this item allows having children if item.has_children: if original_published is not None: # see if previously published children were deleted. 2 reasons for children lists to differ: # Case 1: child deleted # Case 2: child moved for orig_child in original_published.children: if orig_child not in item.children: published_parent = self.get_parent_location( orig_child) if published_parent == item_location: # Case 1: child was deleted in draft parent item # So, delete published version of the child now that we're publishing the draft parent self._delete_subtree(orig_child, [as_published]) else: # Case 2: child was moved to a new draft parent item # So, do not delete the child. It will be published when the new parent is published. pass # update the published (not draft) item (ignoring that item is "draft"). The published # may not exist; (if original_published is None); so, allow_not_found super(DraftModuleStore, self).update_item( # lint-amnesty, pylint: disable=super-with-arguments item, user_id, isPublish=True, is_publish_root=is_root, allow_not_found=True) to_be_deleted.append(as_draft(item_location).to_deprecated_son())
def test_export_course_with_peer_component(self): """ Test export course when link_to_location is given in peer grading interface settings. """ name = "export_peer_component" locations = self._create_test_tree(name) # Insert the test block directly into the module store problem_location = Location('edX', 'tree{}'.format(name), name, 'combinedopenended', 'test_peer_problem') self.draft_store.create_child( self.dummy_user, locations["child"], problem_location.block_type, block_id=problem_location.block_id ) interface_location = Location('edX', 'tree{}'.format(name), name, 'peergrading', 'test_peer_interface') self.draft_store.create_child( self.dummy_user, locations["child"], interface_location.block_type, block_id=interface_location.block_id ) self.draft_store._update_single_item( as_draft(interface_location), { 'definition.data': {}, 'metadata': { 'link_to_location': unicode(problem_location), 'use_for_single_location': True, }, }, ) component = self.draft_store.get_item(interface_location) self.assertEqual(unicode(component.link_to_location), unicode(problem_location)) root_dir = path(mkdtemp()) # export_course_to_xml should work. try: export_course_to_xml( self.draft_store, self.content_store, interface_location.course_key, root_dir, 'test_export' ) finally: shutil.rmtree(root_dir)
def _internal_depth_first(item_location, is_root): """ Depth first publishing from the given location """ try: # handle child does not exist w/o killing publish item = self.get_item(item_location) except ItemNotFoundError: log.warning('Cannot find: %s', item_location) return # publish the children first if item.has_children: for child_loc in item.children: _internal_depth_first(child_loc, False) if item_location.category in DIRECT_ONLY_CATEGORIES or not getattr(item, 'is_draft', False): # ignore noop attempt to publish something that can't be or isn't currently draft return # try to find the originally PUBLISHED version, if it exists try: original_published = super(DraftModuleStore, self).get_item(item_location) except ItemNotFoundError: original_published = None # if the category of this item allows having children if item.has_children: if original_published is not None: # see if previously published children were deleted. 2 reasons for children lists to differ: # Case 1: child deleted # Case 2: child moved for orig_child in original_published.children: if orig_child not in item.children: published_parent = self.get_parent_location(orig_child) if published_parent == item_location: # Case 1: child was deleted in draft parent item # So, delete published version of the child now that we're publishing the draft parent self._delete_subtree(orig_child, [as_published]) else: # Case 2: child was moved to a new draft parent item # So, do not delete the child. It will be published when the new parent is published. pass # update the published (not draft) item (ignoring that item is "draft"). The published # may not exist; (if original_published is None); so, allow_not_found super(DraftModuleStore, self).update_item( item, user_id, isPublish=True, is_publish_root=is_root, allow_not_found=True ) to_be_deleted.append(as_draft(item_location).to_deprecated_son())
def create_xmodule(self, location, definition_data=None, metadata=None, runtime=None, fields={}, **kwargs): """ Create the new xmodule but don't save it. Returns the new module with a draft locator if the category allows drafts. If the category does not allow drafts, just creates a published module. :param location: a Location--must have a category :param definition_data: can be empty. The initial definition_data for the kvs :param metadata: can be empty, the initial metadata for the kvs :param runtime: if you already have an xmodule from the course, the xmodule.runtime value :param fields: a dictionary of field names and values for the new xmodule """ self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) if location.category not in DIRECT_ONLY_CATEGORIES: location = as_draft(location) return wrap_draft( super(DraftModuleStore, self).create_xmodule(location, definition_data, metadata, runtime, fields) )
def update_item(self, xblock, user_id, allow_not_found=False, force=False, isPublish=False, **kwargs): """ See superclass doc. In addition to the superclass's behavior, this method converts the unit to draft if it's not direct-only and not already draft. """ self._verify_branch_setting(ModuleStoreEnum.Branch.draft_preferred) # if the xblock is direct-only, update the PUBLISHED version if xblock.location.category in DIRECT_ONLY_CATEGORIES: return super(DraftModuleStore, self).update_item(xblock, user_id, allow_not_found) draft_loc = as_draft(xblock.location) if not super(DraftModuleStore, self).has_item(draft_loc): try: # ignore any descendants which are already draft self._convert_to_draft(xblock.location, user_id, ignore_if_draft=True) except ItemNotFoundError as exception: # ignore the exception only if allow_not_found is True and # the item that wasn't found is the one that was passed in # we make this extra location check so we do not hide errors when converting any children to draft if not (allow_not_found and exception.args[0] == xblock.location): raise xblock.location = draft_loc super(DraftModuleStore, self).update_item(xblock, user_id, allow_not_found, isPublish=isPublish) return wrap_draft(xblock)
def get_draft(): return wrap_draft(super(DraftModuleStore, self).get_item(as_draft(usage_key), depth=depth))
def get_draft(): return wrap_draft( super(DraftModuleStore, self).get_item( as_draft(usage_key), depth=depth, using_descriptor_system=using_descriptor_system))
def has_draft(): return super(DraftModuleStore, self).has_item(as_draft(usage_key)) # lint-amnesty, pylint: disable=super-with-arguments
def has_draft(): return super(DraftModuleStore, self).has_item(as_draft(usage_key))
def get_draft(): return wrap_draft(super(DraftModuleStore, self).get_item( as_draft(usage_key), depth=depth, using_descriptor_system=using_descriptor_system, for_parent=kwargs.get('for_parent') ))
def get_draft(): return wrap_draft( super(DraftModuleStore, self).get_item(as_draft(usage_key), depth=depth))
def get_item(location): return self.draft_store._find_one(as_draft(location))
def get_draft(): return wrap_draft(super(DraftModuleStore, self).get_item( as_draft(usage_key), depth=depth, using_descriptor_system=using_descriptor_system ))