From e7402fca2ae6fc6dd9e82d0ec6d512314691534e Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Thu, 16 May 2024 13:08:02 -0400 Subject: [PATCH] refactor: update to use Learning Core's new public API This also bumps our openedx-learning dependency to 0.10.0 (the first version with the new openedx_learning.api package). --- cms/envs/common.py | 6 +-- lms/envs/common.py | 6 +-- .../core/djangoapps/content_libraries/api.py | 48 +++++++++---------- .../content_libraries/library_context.py | 4 +- .../djangoapps/content_libraries/models.py | 2 +- openedx/core/djangoapps/xblock/api.py | 13 ++--- .../xblock/runtime/learning_core_runtime.py | 14 +++--- requirements/constraints.txt | 2 +- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/doc.txt | 2 +- requirements/edx/testing.txt | 2 +- setup.cfg | 12 +++++ 13 files changed, 59 insertions(+), 56 deletions(-) diff --git a/cms/envs/common.py b/cms/envs/common.py index 1a830a868706..ee4aa6eb0818 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1881,9 +1881,9 @@ 'openedx_events', # Learning Core Apps, used by v2 content libraries (content_libraries app) - "openedx_learning.core.components", - "openedx_learning.core.contents", - "openedx_learning.core.publishing", + "openedx_learning.apps.authoring.components", + "openedx_learning.apps.authoring.contents", + "openedx_learning.apps.authoring.publishing", ] diff --git a/lms/envs/common.py b/lms/envs/common.py index da2bfed626e0..3f18c96acf92 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3385,9 +3385,9 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring 'openedx_events', # Learning Core Apps, used by v2 content libraries (content_libraries app) - "openedx_learning.core.components", - "openedx_learning.core.contents", - "openedx_learning.core.publishing", + "openedx_learning.apps.authoring.components", + "openedx_learning.apps.authoring.contents", + "openedx_learning.apps.authoring.publishing", ] diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py index 3c47e0d2605f..82d648eaeba1 100644 --- a/openedx/core/djangoapps/content_libraries/api.py +++ b/openedx/core/djangoapps/content_libraries/api.py @@ -85,10 +85,8 @@ LIBRARY_BLOCK_DELETED, LIBRARY_BLOCK_UPDATED, ) -from openedx_learning.core.publishing import api as publishing_api -from openedx_learning.core.contents import api as contents_api -from openedx_learning.core.components import api as components_api -from openedx_learning.core.components.models import Component +from openedx_learning.api import authoring as authoring_api +from openedx_learning.api.authoring_models import Component, MediaType from organizations.models import Organization from xblock.core import XBlock from xblock.exceptions import XBlockNotFoundError @@ -327,18 +325,18 @@ def get_library(library_key): """ ref = ContentLibrary.objects.get_by_key(library_key) learning_package = ref.learning_package - num_blocks = publishing_api.get_all_drafts(learning_package.id).count() - last_publish_log = publishing_api.get_last_publish(learning_package.id) - has_unpublished_changes = publishing_api.get_entities_with_unpublished_changes(learning_package.id) \ - .exists() + num_blocks = authoring_api.get_all_drafts(learning_package.id).count() + last_publish_log = authoring_api.get_last_publish(learning_package.id) + has_unpublished_changes = authoring_api.get_entities_with_unpublished_changes(learning_package.id) \ + .exists() # TODO: I'm doing this one to match already-existing behavior, but this is # something that we should remove. It exists to accomodate some complexities # with how Blockstore staged changes, but Learning Core works differently, # and has_unpublished_changes should be sufficient. # Ref: https://github.com/openedx/edx-platform/issues/34283 - has_unpublished_deletes = publishing_api.get_entities_with_unpublished_deletes(learning_package.id) \ - .exists() + has_unpublished_deletes = authoring_api.get_entities_with_unpublished_deletes(learning_package.id) \ + .exists() # Learning Core doesn't really have a notion of a global version number,but # we can sort of approximate it by using the primary key of the last publish @@ -415,7 +413,7 @@ def create_library( allow_public_read=allow_public_read, license=library_license, ) - learning_package = publishing_api.create_learning_package( + learning_package = authoring_api.create_learning_package( key=str(ref.library_key), title=title, description=description, @@ -556,7 +554,7 @@ def update_library( content_lib.save() if learning_pkg_changed: - publishing_api.update_learning_package( + authoring_api.update_learning_package( content_lib.learning_package_id, title=title, description=description, @@ -614,7 +612,7 @@ def get_library_components(library_key, text_search=None, block_types=None) -> Q """ lib = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined] learning_package = lib.learning_package - components = components_api.get_components( + components = authoring_api.get_components( learning_package.id, draft=True, namespace='xblock.v1', @@ -693,13 +691,13 @@ def set_library_block_olx(usage_key, new_olx_str): now = datetime.now(tz=timezone.utc) with transaction.atomic(): - new_content = contents_api.get_or_create_text_content( + new_content = authoring_api.get_or_create_text_content( component.learning_package_id, get_or_create_olx_media_type(usage_key.block_type).id, text=new_olx_str, created=now, ) - components_api.create_next_version( + authoring_api.create_next_version( component.pk, title=new_title, content_to_replace={ @@ -736,7 +734,7 @@ def create_library_block(library_key, block_type, definition_id): ) # If adding a component would take us over our max, return an error. - component_count = publishing_api.get_all_drafts(ref.learning_package.id).count() + component_count = authoring_api.get_all_drafts(ref.learning_package.id).count() if component_count + 1 > settings.MAX_BLOCKS_PER_CONTENT_LIBRARY: raise BlockLimitReachedError( _("Library cannot have more than {} Components").format( @@ -785,14 +783,14 @@ def _component_exists(usage_key: UsageKeyV2) -> bool: return True -def get_or_create_olx_media_type(block_type: str) -> contents_api.MediaType: +def get_or_create_olx_media_type(block_type: str) -> MediaType: """ Get or create a MediaType for the block type. Learning Core stores all Content with a Media Type (a.k.a. MIME type). For OLX, we use the "application/vnd.*" convention, per RFC 6838. """ - return contents_api.get_or_create_media_type( + return authoring_api.get_or_create_media_type( f"application/vnd.openedx.xblock.v1.{block_type}+xml" ) @@ -819,10 +817,10 @@ def _create_component_for_block(content_lib, usage_key): learning_package = content_lib.learning_package with transaction.atomic(): - component_type = components_api.get_or_create_component_type( + component_type = authoring_api.get_or_create_component_type( "xblock.v1", usage_key.block_type ) - component, component_version = components_api.create_component_and_version( + component, component_version = authoring_api.create_component_and_version( learning_package.id, component_type=component_type, local_key=usage_key.block_id, @@ -830,13 +828,13 @@ def _create_component_for_block(content_lib, usage_key): created=now, created_by=None, ) - content = contents_api.get_or_create_text_content( + content = authoring_api.get_or_create_text_content( learning_package.id, get_or_create_olx_media_type(usage_key.block_type).id, text=xml_text, created=now, ) - components_api.create_component_version_content( + authoring_api.create_component_version_content( component_version.pk, content.id, key="block.xml", @@ -849,7 +847,7 @@ def delete_library_block(usage_key, remove_from_parent=True): Delete the specified block from this library (soft delete). """ component = get_component_from_usage_key(usage_key) - publishing_api.soft_delete_draft(component.pk) + authoring_api.soft_delete_draft(component.pk) LIBRARY_BLOCK_DELETED.send_event( library_block=LibraryBlockData( @@ -938,7 +936,7 @@ def publish_changes(library_key): """ learning_package = ContentLibrary.objects.get_by_key(library_key).learning_package - publishing_api.publish_all_drafts(learning_package.id) + authoring_api.publish_all_drafts(learning_package.id) CONTENT_LIBRARY_UPDATED.send_event( content_library=ContentLibraryData( @@ -954,7 +952,7 @@ def revert_changes(library_key): last published version. """ learning_package = ContentLibrary.objects.get_by_key(library_key).learning_package - publishing_api.reset_drafts_to_published(learning_package.id) + authoring_api.reset_drafts_to_published(learning_package.id) CONTENT_LIBRARY_UPDATED.send_event( content_library=ContentLibraryData( diff --git a/openedx/core/djangoapps/content_libraries/library_context.py b/openedx/core/djangoapps/content_libraries/library_context.py index 93de022474b0..2607c18df7e4 100644 --- a/openedx/core/djangoapps/content_libraries/library_context.py +++ b/openedx/core/djangoapps/content_libraries/library_context.py @@ -10,7 +10,7 @@ from openedx.core.djangoapps.content_libraries.models import ContentLibrary from openedx.core.djangoapps.xblock.api import LearningContext -from openedx_learning.core.components import api as components_api +from openedx_learning.api import authoring as authoring_api log = logging.getLogger(__name__) @@ -87,7 +87,7 @@ def block_exists(self, usage_key): if learning_package is None: return False - return components_api.component_exists_by_key( + return authoring_api.component_exists_by_key( learning_package.id, namespace='xblock.v1', type_name=usage_key.block_type, diff --git a/openedx/core/djangoapps/content_libraries/models.py b/openedx/core/djangoapps/content_libraries/models.py index 58a9f6b6863f..c1dea39e0613 100644 --- a/openedx/core/djangoapps/content_libraries/models.py +++ b/openedx/core/djangoapps/content_libraries/models.py @@ -57,7 +57,7 @@ LIBRARY_TYPES, COMPLEX, LICENSE_OPTIONS, ALL_RIGHTS_RESERVED, ) -from openedx_learning.core.publishing.models import LearningPackage +from openedx_learning.api.authoring_models import LearningPackage from organizations.models import Organization # lint-amnesty, pylint: disable=wrong-import-order from .apps import ContentLibrariesConfig diff --git a/openedx/core/djangoapps/xblock/api.py b/openedx/core/djangoapps/xblock/api.py index f54464de9f4e..80a4bd57eab8 100644 --- a/openedx/core/djangoapps/xblock/api.py +++ b/openedx/core/djangoapps/xblock/api.py @@ -15,12 +15,10 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from openedx_learning.core.components import api as components_api -from openedx_learning.core.components.models import Component -from openedx_learning.core.publishing import api as publishing_api +from openedx_learning.api import authoring as authoring_api +from openedx_learning.api.authoring_models import Component from opaque_keys.edx.keys import UsageKeyV2 from opaque_keys.edx.locator import BundleDefinitionLocator, LibraryUsageLocatorV2 - from rest_framework.exceptions import NotFound from xblock.core import XBlock from xblock.exceptions import NoSuchViewError @@ -28,13 +26,10 @@ from openedx.core.djangoapps.xblock.apps import get_xblock_app_config from openedx.core.djangoapps.xblock.learning_context.manager import get_learning_context_impl - from openedx.core.djangoapps.xblock.runtime.learning_core_runtime import ( LearningCoreFieldData, LearningCoreXBlockRuntime, ) - - from openedx.core.djangoapps.xblock.runtime.runtime import XBlockRuntimeSystem as _XBlockRuntimeSystem from .utils import get_secure_token_for_xblock_handler, get_xblock_id_for_anonymous_user @@ -192,10 +187,10 @@ def get_component_from_usage_key(usage_key: UsageKeyV2) -> Component: This is a lower-level function that will return a Component even if there is no current draft version of that Component (because it's been soft-deleted). """ - learning_package = publishing_api.get_learning_package_by_key( + learning_package = authoring_api.get_learning_package_by_key( str(usage_key.context_key) ) - return components_api.get_component_by_key( + return authoring_api.get_component_by_key( learning_package.id, namespace='xblock.v1', type_name=usage_key.block_type, diff --git a/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py b/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py index dc5a85de3a02..26aa7af60f0b 100644 --- a/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py +++ b/openedx/core/djangoapps/xblock/runtime/learning_core_runtime.py @@ -10,9 +10,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.db.transaction import atomic -from openedx_learning.core.components import api as components_api -from openedx_learning.core.contents import api as contents_api -from openedx_learning.core.publishing import api as publishing_api +from openedx_learning.api import authoring as authoring_api from lxml import etree @@ -239,16 +237,16 @@ def save_block(self, block): usage_key = block.scope_ids.usage_id with atomic(): component = self._get_component_from_usage_key(usage_key) - block_media_type = contents_api.get_or_create_media_type( + block_media_type = authoring_api.get_or_create_media_type( f"application/vnd.openedx.xblock.v1.{usage_key.block_type}+xml" ) - content = contents_api.get_or_create_text_content( + content = authoring_api.get_or_create_text_content( component.learning_package_id, block_media_type.id, text=serialized.olx_str, created=now, ) - components_api.create_next_version( + authoring_api.create_next_version( component.pk, title=block.display_name, content_to_replace={ @@ -267,9 +265,9 @@ def _get_component_from_usage_key(self, usage_key): TODO: This is the third place where we're implementing this. Figure out where the definitive place should be and have everything else call that. """ - learning_package = publishing_api.get_learning_package_by_key(str(usage_key.lib_key)) + learning_package = authoring_api.get_learning_package_by_key(str(usage_key.lib_key)) try: - component = components_api.get_component_by_key( + component = authoring_api.get_component_by_key( learning_package.id, namespace='xblock.v1', type_name=usage_key.block_type, diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ee231e31c5c5..c2fc9b2ddac1 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -97,7 +97,7 @@ libsass==0.10.0 click==8.1.6 # pinning this version to avoid updates while the library is being developed -openedx-learning==0.9.4 +openedx-learning==0.10.0 # Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise. openai<=0.28.1 diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 0790c316c5cb..66d3d0f13d41 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -775,7 +775,7 @@ openedx-filters==1.8.1 # -r requirements/edx/kernel.in # lti-consumer-xblock # ora2 -openedx-learning==0.9.4 +openedx-learning==0.10.0 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/kernel.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 989c67531020..13b959fa4e48 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -1331,7 +1331,7 @@ openedx-filters==1.8.1 # -r requirements/edx/testing.txt # lti-consumer-xblock # ora2 -openedx-learning==0.9.4 +openedx-learning==0.10.0 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/doc.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 0345deeff1b3..f968e6954ea8 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -906,7 +906,7 @@ openedx-filters==1.8.1 # -r requirements/edx/base.txt # lti-consumer-xblock # ora2 -openedx-learning==0.9.4 +openedx-learning==0.10.0 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index d62c210dc7f5..13e13fe49b28 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -991,7 +991,7 @@ openedx-filters==1.8.1 # -r requirements/edx/base.txt # lti-consumer-xblock # ora2 -openedx-learning==0.9.4 +openedx-learning==0.10.0 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt diff --git a/setup.cfg b/setup.cfg index fe776ec580a4..e3ad0543698e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -62,6 +62,7 @@ root_packages = lms cms openedx + openedx_learning include_external_packages = True contract_types = # Our custom contract which checks that we're only importing from 'api.py' @@ -185,3 +186,14 @@ allowed_modules = # Only imports from api.py are allowed elsewhere in the code # See https://open-edx-proposals.readthedocs.io/en/latest/best-practices/oep-0049-django-app-patterns.html#api-py api + +[importlinter:contract:3] +name = Do not import apps from openedx-learning (only import from openedx_learning.api.* and openedx_learning.lib.*). +type = forbidden +source_modules = + cms + lms + openedx +forbidden_modules = + openedx_learning.apps +allow_indirect_imports = True