Exemple #1
0
    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)
Exemple #2
0
    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))
Exemple #3
0
    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)
Exemple #4
0
    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
Exemple #5
0
    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)
Exemple #6
0
    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
Exemple #7
0
 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')))
Exemple #8
0
    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)
Exemple #9
0
 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')))
Exemple #10
0
        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())
Exemple #11
0
    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)
Exemple #12
0
    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)
Exemple #13
0
        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())
Exemple #14
0
    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)
        )
Exemple #15
0
    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)
Exemple #16
0
 def get_draft():
     return wrap_draft(super(DraftModuleStore, self).get_item(as_draft(usage_key), depth=depth))
Exemple #17
0
 def get_draft():
     return wrap_draft(
         super(DraftModuleStore, self).get_item(
             as_draft(usage_key),
             depth=depth,
             using_descriptor_system=using_descriptor_system))
Exemple #18
0
 def has_draft():
     return super(DraftModuleStore, self).has_item(as_draft(usage_key))  # lint-amnesty, pylint: disable=super-with-arguments
Exemple #19
0
 def has_draft():
     return super(DraftModuleStore, self).has_item(as_draft(usage_key))
Exemple #20
0
 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')
     ))
Exemple #21
0
 def get_draft():
     return wrap_draft(
         super(DraftModuleStore, self).get_item(as_draft(usage_key),
                                                depth=depth))
Exemple #22
0
 def get_item(location):
     return self.draft_store._find_one(as_draft(location))
Exemple #23
0
 def get_draft():
     return wrap_draft(super(DraftModuleStore, self).get_item(
         as_draft(usage_key), depth=depth, using_descriptor_system=using_descriptor_system
     ))
Exemple #24
0
 def has_draft():
     return super(DraftModuleStore, self).has_item(as_draft(usage_key))
Exemple #25
0
 def get_item(location):
     return self.draft_store._find_one(as_draft(location))