예제 #1
0
 def test_bundle_crud(self):
     """ Create, Fetch, Update, and Delete a Bundle """
     coll = api.create_collection("Test Collection")
     args = {
         "title": "Water 💧 Bundle",
         "slug": "h2o",
         "description": "Sploosh",
     }
     # Create:
     bundle = api.create_bundle(coll.uuid, **args)
     for attr, value in args.items():
         self.assertEqual(getattr(bundle, attr), value)
     self.assertIsInstance(bundle.uuid, UUID)
     # Fetch:
     bundle2 = api.get_bundle(bundle.uuid)
     self.assertEqual(bundle, bundle2)
     # Update:
     new_description = "Water Nation Bending Lessons"
     bundle3 = api.update_bundle(bundle.uuid, description=new_description)
     self.assertEqual(bundle3.description, new_description)
     bundle4 = api.get_bundle(bundle.uuid)
     self.assertEqual(bundle4.description, new_description)
     # Delete:
     api.delete_bundle(bundle.uuid)
     with self.assertRaises(api.BundleNotFound):
         api.get_bundle(bundle.uuid)
예제 #2
0
 def test_bundle_crud(self):
     """ Create, Fetch, Update, and Delete a Bundle """
     coll = api.create_collection("Test Collection")
     args = {
         "title": "Water 💧 Bundle",
         "slug": "h2o",
         "description": "Sploosh",
     }
     # Create:
     bundle = api.create_bundle(coll.uuid, **args)
     for attr, value in args.items():
         assert getattr(bundle, attr) == value
     assert isinstance(bundle.uuid, UUID)
     # Fetch:
     bundle2 = api.get_bundle(bundle.uuid)
     assert bundle == bundle2
     # Update:
     new_description = "Water Nation Bending Lessons"
     bundle3 = api.update_bundle(bundle.uuid, description=new_description)
     assert bundle3.description == new_description
     bundle4 = api.get_bundle(bundle.uuid)
     assert bundle4.description == new_description
     # Delete:
     api.delete_bundle(bundle.uuid)
     with pytest.raises(api.BundleNotFound):
         api.get_bundle(bundle.uuid)
예제 #3
0
def get_bundle_version_number(bundle_uuid, draft_name=None):
    """
    Get the current version number of the specified bundle/draft. If a draft is
    specified, the update timestamp is used in lieu of a version number.
    """
    cache_key = 'bundle_version:{}:{}'.format(bundle_uuid, draft_name or '')
    version = cache.get(cache_key)
    if version is not None:
        return version
    else:
        version = 0  # Default to 0 in case bundle/draft is empty or doesn't exist

    bundle_metadata = blockstore_api.get_bundle(bundle_uuid)
    if draft_name:
        draft_uuid = bundle_metadata.drafts.get(draft_name)  # pylint: disable=no-member
        if draft_uuid:
            draft_metadata = blockstore_api.get_draft(draft_uuid)
            # Convert the 'updated_at' datetime info an integer value with microsecond accuracy.
            updated_at_timestamp = (
                draft_metadata.updated_at -
                datetime(1970, 1, 1, tzinfo=UTC)).total_seconds()
            version = int(updated_at_timestamp * 1e6)
    # If we're not using a draft or the draft does not exist [anymore], fall
    # back to the bundle version, if any versions have been published:
    if version == 0 and bundle_metadata.latest_version:
        version = bundle_metadata.latest_version
    cache.set(cache_key, version, timeout=MAX_BLOCKSTORE_CACHE_DELAY)
    return version
예제 #4
0
def create_bundle_link(library_key, link_id, target_opaque_key, version=None):
    """
    Create a new link to the resource with the specified opaque key.

    For now, only LibraryLocatorV2 opaque keys are supported.
    """
    ref = ContentLibrary.objects.get_by_key(library_key)
    # Make sure this link ID/name is not already in use:
    links = blockstore_cache.get_bundle_draft_direct_links_cached(
        ref.bundle_uuid, DRAFT_NAME)
    if link_id in links:
        raise InvalidNameError("That link ID is already in use.")
    # Determine the target:
    if not isinstance(target_opaque_key, LibraryLocatorV2):
        raise TypeError(
            "For now, only LibraryLocatorV2 opaque keys are supported by create_bundle_link"
        )
    target_bundle_uuid = ContentLibrary.objects.get_by_key(
        target_opaque_key).bundle_uuid
    if version is None:
        version = get_bundle(target_bundle_uuid).latest_version
    # Create the new link:
    draft = get_or_create_bundle_draft(ref.bundle_uuid, DRAFT_NAME)
    set_draft_link(draft.uuid, link_id, target_bundle_uuid, version)
    # Clear the cache:
    LibraryBundle(library_key, ref.bundle_uuid,
                  draft_name=DRAFT_NAME).cache.clear()
    CONTENT_LIBRARY_UPDATED.send(sender=None, library_key=library_key)
예제 #5
0
def get_library(library_key):
    """
    Get the library with the specified key. Does not check permissions.
    returns a ContentLibraryMetadata instance.

    Raises ContentLibraryNotFound if the library doesn't exist.
    """
    assert isinstance(library_key, LibraryLocatorV2)
    ref = ContentLibrary.objects.get_by_key(library_key)
    bundle_metadata = get_bundle(ref.bundle_uuid)
    lib_bundle = LibraryBundle(library_key,
                               ref.bundle_uuid,
                               draft_name=DRAFT_NAME)
    num_blocks = len(lib_bundle.get_top_level_usages())
    last_published = lib_bundle.get_last_published_time()
    (has_unpublished_changes,
     has_unpublished_deletes) = lib_bundle.has_changes()
    return ContentLibraryMetadata(
        key=library_key,
        bundle_uuid=ref.bundle_uuid,
        title=bundle_metadata.title,
        description=bundle_metadata.description,
        num_blocks=num_blocks,
        version=bundle_metadata.latest_version,
        last_published=last_published,
        allow_public_learning=ref.allow_public_learning,
        allow_public_read=ref.allow_public_read,
        has_unpublished_changes=has_unpublished_changes,
        has_unpublished_deletes=has_unpublished_deletes,
    )
예제 #6
0
    def get_item_definition(cls, item):
        ref = ContentLibrary.objects.get_by_key(item)
        lib_bundle = LibraryBundle(item,
                                   ref.bundle_uuid,
                                   draft_name=DRAFT_NAME)
        num_blocks = len(lib_bundle.get_top_level_usages())
        last_published = lib_bundle.get_last_published_time()
        last_published_str = None
        if last_published:
            last_published_str = last_published.strftime('%Y-%m-%dT%H:%M:%SZ')
        (has_unpublished_changes,
         has_unpublished_deletes) = lib_bundle.has_changes()

        bundle_metadata = get_bundle(ref.bundle_uuid)

        # NOTE: Increment ContentLibraryIndexer.SCHEMA_VERSION if the following schema is updated to avoid dealing
        # with outdated indexes which might cause errors due to missing/invalid attributes.
        return {
            "schema_version": ContentLibraryIndexer.SCHEMA_VERSION,
            "id": str(item),
            "uuid": str(bundle_metadata.uuid),
            "title": bundle_metadata.title,
            "description": bundle_metadata.description,
            "num_blocks": num_blocks,
            "version": bundle_metadata.latest_version,
            "last_published": last_published_str,
            "has_unpublished_changes": has_unpublished_changes,
            "has_unpublished_deletes": has_unpublished_deletes,
            # only 'content' field is analyzed by elastisearch, and allows text-search
            "content": {
                "id": str(item),
                "title": bundle_metadata.title,
                "description": bundle_metadata.description,
            },
        }
예제 #7
0
def revert_changes(library_key):
    """
    Revert all pending changes to the specified library, restoring it to the
    last published version.
    """
    ref = ContentLibrary.objects.get_by_key(library_key)
    bundle = get_bundle(ref.bundle_uuid)
    if DRAFT_NAME in bundle.drafts:  # pylint: disable=unsupported-membership-test
        draft_uuid = bundle.drafts[DRAFT_NAME]  # pylint: disable=unsubscriptable-object
        delete_draft(draft_uuid)
    else:
        return  # If there is no draft, no action is needed.
    LibraryBundle(library_key, ref.bundle_uuid, draft_name=DRAFT_NAME).cache.clear()
예제 #8
0
def publish_changes(library_key):
    """
    Publish all pending changes to the specified library.
    """
    ref = ContentLibrary.objects.get_by_key(library_key)
    bundle = get_bundle(ref.bundle_uuid)
    if DRAFT_NAME in bundle.drafts:  # pylint: disable=unsupported-membership-test
        draft_uuid = bundle.drafts[DRAFT_NAME]  # pylint: disable=unsubscriptable-object
        commit_draft(draft_uuid)
    else:
        return  # If there is no draft, no action is needed.
    LibraryBundle(library_key, ref.bundle_uuid).cache.clear()
    LibraryBundle(library_key, ref.bundle_uuid, draft_name=DRAFT_NAME).cache.clear()
    CONTENT_LIBRARY_UPDATED.send(sender=None, library_key=library_key, update_blocks=True)
예제 #9
0
    def index_libraries(cls, library_keys):
        """
        Index the specified libraries. If they already exist, replace them with new ones.
        """
        searcher = SearchEngine.get_search_engine(cls.INDEX_NAME)

        library_dicts = []

        for library_key in library_keys:
            ref = ContentLibrary.objects.get_by_key(library_key)
            lib_bundle = LibraryBundle(library_key,
                                       ref.bundle_uuid,
                                       draft_name=DRAFT_NAME)
            num_blocks = len(lib_bundle.get_top_level_usages())
            last_published = lib_bundle.get_last_published_time()
            last_published_str = None
            if last_published:
                last_published_str = last_published.strftime(
                    '%Y-%m-%dT%H:%M:%SZ')
            (has_unpublished_changes,
             has_unpublished_deletes) = lib_bundle.has_changes()

            bundle_metadata = get_bundle(ref.bundle_uuid)

            # NOTE: Increment ContentLibraryIndexer.SCHEMA_VERSION if the following schema is updated to avoid dealing
            # with outdated indexes which might cause errors due to missing/invalid attributes.
            library_dict = {
                "schema_version": ContentLibraryIndexer.SCHEMA_VERSION,
                "id": str(library_key),
                "uuid": str(bundle_metadata.uuid),
                "title": bundle_metadata.title,
                "description": bundle_metadata.description,
                "num_blocks": num_blocks,
                "version": bundle_metadata.latest_version,
                "last_published": last_published_str,
                "has_unpublished_changes": has_unpublished_changes,
                "has_unpublished_deletes": has_unpublished_deletes,
            }
            library_dicts.append(library_dict)

        return searcher.index(cls.LIBRARY_DOCUMENT_TYPE, library_dicts)
예제 #10
0
def get_library(library_key):
    """
    Get the library with the specified key. Does not check permissions.
    returns a ContentLibraryMetadata instance.

    Raises ContentLibraryNotFound if the library doesn't exist.
    """
    assert isinstance(library_key, LibraryLocatorV2)
    ref = ContentLibrary.objects.get_by_key(library_key)
    bundle_metadata = get_bundle(ref.bundle_uuid)
    lib_bundle = LibraryBundle(library_key, ref.bundle_uuid, draft_name=DRAFT_NAME)
    (has_unpublished_changes, has_unpublished_deletes) = lib_bundle.has_changes()
    return ContentLibraryMetadata(
        key=library_key,
        bundle_uuid=ref.bundle_uuid,
        title=bundle_metadata.title,
        description=bundle_metadata.description,
        version=bundle_metadata.latest_version,
        has_unpublished_changes=has_unpublished_changes,
        has_unpublished_deletes=has_unpublished_deletes,
    )
예제 #11
0
파일: api.py 프로젝트: xzhuwu/edx-platform
def update_bundle_link(library_key, link_id, version=None, delete=False):
    """
    Update a bundle's link to point to the specified version of its target
    bundle. Use version=None to automatically point to the latest version.
    Use delete=True to delete the link.
    """
    ref = ContentLibrary.objects.get_by_key(library_key)
    draft = get_or_create_bundle_draft(ref.bundle_uuid, DRAFT_NAME)
    if delete:
        set_draft_link(draft.uuid, link_id, None, None)
    else:
        links = blockstore_cache.get_bundle_draft_direct_links_cached(ref.bundle_uuid, DRAFT_NAME)
        try:
            link = links[link_id]
        except KeyError:
            raise InvalidNameError("That link does not exist.")
        if version is None:
            version = get_bundle(link.bundle_uuid).latest_version
        set_draft_link(draft.uuid, link_id, link.bundle_uuid, version)
    # Clear the cache:
    LibraryBundle(library_key, ref.bundle_uuid, draft_name=DRAFT_NAME).cache.clear()
예제 #12
0
 def test_nonexistent_bundle(self):
     """ Request a bundle that doesn't exist -> BundleNotFound """
     with self.assertRaises(api.BundleNotFound):
         api.get_bundle(BAD_UUID)