diff --git a/cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py b/cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py index a818da81d1..68771ceaec 100644 --- a/cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py +++ b/cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py @@ -408,7 +408,6 @@ class ClipboardPasteFromV2LibraryTestCase(ModuleStoreTestCase): self.store = modulestore() self.library = library_api.create_library( - library_type=library_api.COMPLEX, org=Organization.objects.create(name="Test Org", short_name="CL-TEST"), slug="lib", title="Library", diff --git a/openedx/core/djangoapps/content/search/tests/test_api.py b/openedx/core/djangoapps/content/search/tests/test_api.py index c89d84490e..990226f343 100644 --- a/openedx/core/djangoapps/content/search/tests/test_api.py +++ b/openedx/core/djangoapps/content/search/tests/test_api.py @@ -115,7 +115,6 @@ class TestSearchApi(ModuleStoreTestCase): # Create a content library: self.library = library_api.create_library( - library_type=library_api.COMPLEX, org=OrganizationFactory.create(short_name="org1"), slug="lib", title="Library", diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py index 58b99a390f..fdfe2ff0ef 100644 --- a/openedx/core/djangoapps/content_libraries/api.py +++ b/openedx/core/djangoapps/content_libraries/api.py @@ -114,7 +114,7 @@ from openedx.core.lib.xblock_serializer.api import serialize_modulestore_block_f from xmodule.modulestore.django import modulestore from . import permissions, tasks -from .constants import ALL_RIGHTS_RESERVED, COMPLEX +from .constants import ALL_RIGHTS_RESERVED from .models import ContentLibrary, ContentLibraryPermission, ContentLibraryBlockImportTask log = logging.getLogger(__name__) @@ -176,7 +176,6 @@ class ContentLibraryMetadata: description = attr.ib("") num_blocks = attr.ib(0) version = attr.ib(0) - type = attr.ib(default=COMPLEX) last_published = attr.ib(default=None, type=datetime) last_draft_created = attr.ib(default=None, type=datetime) last_draft_created_by = attr.ib(default=None, type=datetime) @@ -306,15 +305,13 @@ class LibraryXBlockType: # ============ -def get_libraries_for_user(user, org=None, library_type=None, text_search=None, order=None): +def get_libraries_for_user(user, org=None, text_search=None, order=None): """ Return content libraries that the user has permission to view. """ filter_kwargs = {} if org: filter_kwargs['org__short_name'] = org - if library_type: - filter_kwargs['type'] = library_type qs = ContentLibrary.objects.filter(**filter_kwargs) \ .select_related('learning_package', 'org') \ .order_by('org__short_name', 'slug') @@ -361,7 +358,6 @@ def get_metadata(queryset, text_search=None): ContentLibraryMetadata( key=lib.library_key, title=lib.learning_package.title if lib.learning_package else "", - type=lib.type, description="", version=0, allow_public_learning=lib.allow_public_learning, @@ -446,7 +442,6 @@ def get_library(library_key): return ContentLibraryMetadata( key=library_key, title=learning_package.title, - type=ref.type, description=ref.learning_package.description, num_blocks=num_blocks, version=version, @@ -474,7 +469,6 @@ def create_library( allow_public_learning=False, allow_public_read=False, library_license=ALL_RIGHTS_RESERVED, - library_type=COMPLEX, ): """ Create a new content library. @@ -491,8 +485,6 @@ def create_library( allow_public_read: Allow anyone to view blocks (including source) in Studio? - library_type: Deprecated parameter, not really used. Set to COMPLEX. - Returns a ContentLibraryMetadata instance. """ assert isinstance(org, Organization) @@ -502,7 +494,6 @@ def create_library( ref = ContentLibrary.objects.create( org=org, slug=slug, - type=library_type, allow_public_learning=allow_public_learning, allow_public_read=allow_public_read, license=library_license, @@ -526,7 +517,6 @@ def create_library( return ContentLibraryMetadata( key=ref.library_key, title=title, - type=library_type, description=description, num_blocks=0, version=0, @@ -611,7 +601,6 @@ def update_library( description=None, allow_public_learning=None, allow_public_read=None, - library_type=None, library_license=None, ): """ @@ -621,7 +610,7 @@ def update_library( A value of None means "don't change". """ lib_obj_fields = [ - allow_public_learning, allow_public_read, library_type, library_license + allow_public_learning, allow_public_read, library_license ] lib_obj_changed = any(field is not None for field in lib_obj_fields) learning_pkg_changed = any(field is not None for field in [title, description]) @@ -640,10 +629,6 @@ def update_library( content_lib.allow_public_learning = allow_public_learning if allow_public_read is not None: content_lib.allow_public_read = allow_public_read - if library_type is not None: - # TODO: Get rid of this field entirely, and remove library_type - # from any functions that take it as an argument. - content_lib.library_type = library_type if library_license is not None: content_lib.library_license = library_license content_lib.save() @@ -856,13 +841,6 @@ def validate_can_add_block_to_library( """ assert isinstance(library_key, LibraryLocatorV2) content_library = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined] - if content_library.type != COMPLEX: - if block_type != content_library.type: - raise IncompatibleTypesError( - _('Block type "{block_type}" is not compatible with library type "{library_type}".').format( - block_type=block_type, library_type=content_library.type, - ) - ) # If adding a component would take us over our max, return an error. component_count = authoring_api.get_all_drafts(content_library.learning_package.id).count() @@ -1288,10 +1266,7 @@ def get_allowed_block_types(library_key): # pylint: disable=unused-argument # TODO: return support status and template options # See cms/djangoapps/contentstore/views/component.py block_types = sorted(name for name, class_ in XBlock.load_classes()) - lib = get_library(library_key) - if lib.type != COMPLEX: - # Problem and Video libraries only permit XBlocks of the same name. - block_types = (name for name in block_types if name == lib.type) + info = [] for block_type in block_types: # TODO: unify the contentstore helper with the xblock.api version of diff --git a/openedx/core/djangoapps/content_libraries/constants.py b/openedx/core/djangoapps/content_libraries/constants.py index 9505d52d1c..a01e0fbdda 100644 --- a/openedx/core/djangoapps/content_libraries/constants.py +++ b/openedx/core/djangoapps/content_libraries/constants.py @@ -2,16 +2,6 @@ from django.utils.translation import gettext_lazy as _ -VIDEO = 'video' -COMPLEX = 'complex' -PROBLEM = 'problem' - -LIBRARY_TYPES = ( - (VIDEO, _('Video')), - (COMPLEX, _('Complex')), - (PROBLEM, _('Problem')), -) - # These are all the licenses we support so far. ALL_RIGHTS_RESERVED = '' CC_4_BY = 'CC:4.0:BY' diff --git a/openedx/core/djangoapps/content_libraries/migrations/0011_remove_contentlibrary_bundle_uuid_and_more.py b/openedx/core/djangoapps/content_libraries/migrations/0011_remove_contentlibrary_bundle_uuid_and_more.py new file mode 100644 index 0000000000..0c634c6488 --- /dev/null +++ b/openedx/core/djangoapps/content_libraries/migrations/0011_remove_contentlibrary_bundle_uuid_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.16 on 2024-10-24 20:21 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('content_libraries', '0010_contentlibrary_learning_package_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='contentlibrary', + name='bundle_uuid', + ), + migrations.RemoveField( + model_name='contentlibrary', + name='type', + ), + ] diff --git a/openedx/core/djangoapps/content_libraries/models.py b/openedx/core/djangoapps/content_libraries/models.py index c1dea39e06..4a210223cc 100644 --- a/openedx/core/djangoapps/content_libraries/models.py +++ b/openedx/core/djangoapps/content_libraries/models.py @@ -54,8 +54,7 @@ from pylti1p3.grade import Grade from opaque_keys.edx.django.models import UsageKeyField from openedx.core.djangoapps.content_libraries.constants import ( - LIBRARY_TYPES, COMPLEX, LICENSE_OPTIONS, - ALL_RIGHTS_RESERVED, + LICENSE_OPTIONS, ALL_RIGHTS_RESERVED, ) from openedx_learning.api.authoring_models import LearningPackage from organizations.models import Organization # lint-amnesty, pylint: disable=wrong-import-order @@ -101,18 +100,6 @@ class ContentLibrary(models.Model): org = models.ForeignKey(Organization, on_delete=models.PROTECT, null=False) slug = models.SlugField(allow_unicode=True) - # We no longer use the ``bundle_uuid`` and ``type`` fields, but we'll leave - # them in the model until after the Redwood release, just in case someone - # out there was using v2 libraries. We don't expect this, since it wasn't in - # a usable state, but there's always a chance someone managed to do it and - # is still using it. By keeping the schema backwards compatible, the thought - # is that they would update to the latest version, notice their libraries - # aren't working correctly, and still have the ability to recover their data - # if the code was rolled back. - # TODO: Remove these fields after the Redwood release is cut. - bundle_uuid = models.UUIDField(unique=True, null=True, default=None) - type = models.CharField(max_length=25, default=COMPLEX, choices=LIBRARY_TYPES) - license = models.CharField(max_length=25, default=ALL_RIGHTS_RESERVED, choices=LICENSE_OPTIONS) learning_package = models.OneToOneField( LearningPackage, diff --git a/openedx/core/djangoapps/content_libraries/serializers.py b/openedx/core/djangoapps/content_libraries/serializers.py index b19d27bed3..d639ed63af 100644 --- a/openedx/core/djangoapps/content_libraries/serializers.py +++ b/openedx/core/djangoapps/content_libraries/serializers.py @@ -11,8 +11,6 @@ from opaque_keys import InvalidKeyError from openedx_learning.api.authoring_models import Collection from openedx.core.djangoapps.content_libraries.constants import ( - LIBRARY_TYPES, - COMPLEX, ALL_RIGHTS_RESERVED, LICENSE_OPTIONS, ) @@ -37,10 +35,8 @@ class ContentLibraryMetadataSerializer(serializers.Serializer): # begins with 'lib:'. (The numeric ID of the ContentLibrary object in MySQL # is not exposed via this API.) id = serializers.CharField(source="key", read_only=True) - type = serializers.ChoiceField(choices=LIBRARY_TYPES, default=COMPLEX) org = serializers.SlugField(source="key.org") slug = serializers.CharField(source="key.slug", validators=(validate_unicode_slug, )) - bundle_uuid = serializers.UUIDField(format='hex_verbose', read_only=True) title = serializers.CharField() description = serializers.CharField(allow_blank=True) num_blocks = serializers.IntegerField(read_only=True) @@ -86,7 +82,6 @@ class ContentLibraryUpdateSerializer(serializers.Serializer): description = serializers.CharField() allow_public_learning = serializers.BooleanField() allow_public_read = serializers.BooleanField() - type = serializers.ChoiceField(choices=LIBRARY_TYPES) license = serializers.ChoiceField(choices=LICENSE_OPTIONS) @@ -118,7 +113,7 @@ class ContentLibraryPermissionSerializer(ContentLibraryPermissionLevelSerializer group_name = serializers.CharField(source="group.name", allow_null=True, allow_blank=False, default=None) -class BaseFilterSerializer(serializers.Serializer): +class ContentLibraryFilterSerializer(serializers.Serializer): """ Base serializer for filtering listings on the content library APIs. """ @@ -127,13 +122,6 @@ class BaseFilterSerializer(serializers.Serializer): order = serializers.CharField(default=None, required=False) -class ContentLibraryFilterSerializer(BaseFilterSerializer): - """ - Serializer for filtering library listings. - """ - type = serializers.ChoiceField(choices=LIBRARY_TYPES, default=None, required=False) - - class CollectionMetadataSerializer(serializers.Serializer): """ Serializer for CollectionMetadata diff --git a/openedx/core/djangoapps/content_libraries/tests/base.py b/openedx/core/djangoapps/content_libraries/tests/base.py index 33d5477da6..7f1664e6c7 100644 --- a/openedx/core/djangoapps/content_libraries/tests/base.py +++ b/openedx/core/djangoapps/content_libraries/tests/base.py @@ -9,7 +9,7 @@ from organizations.models import Organization from rest_framework.test import APITransactionTestCase, APIClient from common.djangoapps.student.tests.factories import UserFactory -from openedx.core.djangoapps.content_libraries.constants import COMPLEX, ALL_RIGHTS_RESERVED +from openedx.core.djangoapps.content_libraries.constants import ALL_RIGHTS_RESERVED from openedx.core.djangolib.testing.utils import skip_unless_cms # Define the URLs here - don't use reverse() because we want to detect @@ -124,7 +124,7 @@ class ContentLibrariesRestApiTest(APITransactionTestCase): self.client = old_client # pylint: disable=attribute-defined-outside-init def _create_library( - self, slug, title, description="", org=None, library_type=COMPLEX, + self, slug, title, description="", org=None, license_type=ALL_RIGHTS_RESERVED, expect_response=200, ): """ Create a library """ @@ -135,7 +135,6 @@ class ContentLibrariesRestApiTest(APITransactionTestCase): "slug": slug, "title": title, "description": description, - "type": library_type, "license": license_type, }, expect_response) diff --git a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py index 8977464dd4..b74d16b678 100644 --- a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py +++ b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py @@ -25,7 +25,7 @@ from organizations.models import Organization from rest_framework.test import APITestCase from common.djangoapps.student.tests.factories import UserFactory -from openedx.core.djangoapps.content_libraries.constants import CC_4_BY, COMPLEX, PROBLEM, VIDEO +from openedx.core.djangoapps.content_libraries.constants import CC_4_BY from openedx.core.djangoapps.content_libraries.tests.base import ( URL_BLOCK_GET_HANDLER_URL, URL_BLOCK_METADATA_URL, @@ -100,7 +100,6 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix "title": "A Tést Lꜟطrary", "description": "Just Téstꜟng", "version": 0, - "type": COMPLEX, "license": CC_4_BY, "has_unpublished_changes": False, "has_unpublished_deletes": False, @@ -199,13 +198,13 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix Test the filters in the list libraries API """ self._create_library( - slug="test-lib-filter-1", title="Fob", description="Bar", library_type=VIDEO, + slug="test-lib-filter-1", title="Fob", description="Bar", ) self._create_library( slug="test-lib-filter-2", title="Library-Title-2", description="Bar-2", ) self._create_library( - slug="l3", title="Library-Title-3", description="Description", library_type=VIDEO, + slug="l3", title="Library-Title-3", description="Description", ) Organization.objects.get_or_create( @@ -215,7 +214,6 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix self._create_library( slug="l4", title="Library-Title-4", description="Library-Description", org='org-test', - library_type=VIDEO, ) self._create_library( slug="l5", title="Library-Title-5", description="Library-Description", @@ -225,14 +223,11 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix assert len(self._list_libraries()) == 5 assert len(self._list_libraries({'org': 'org-test'})) == 2 assert len(self._list_libraries({'text_search': 'test-lib-filter'})) == 2 - assert len(self._list_libraries({'text_search': 'test-lib-filter', 'type': VIDEO})) == 1 assert len(self._list_libraries({'text_search': 'library-title'})) == 4 - assert len(self._list_libraries({'text_search': 'library-title', 'type': VIDEO})) == 2 assert len(self._list_libraries({'text_search': 'bar'})) == 2 assert len(self._list_libraries({'text_search': 'org-test'})) == 2 assert len(self._list_libraries({'org': 'org-test', 'text_search': 'library-title-4'})) == 1 - assert len(self._list_libraries({'type': VIDEO})) == 3 self.assertOrderEqual( self._list_libraries({'order': 'title'}), @@ -496,27 +491,6 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix assert len(self._get_library_blocks(lib['id'], {'block_type': 'problem'})['results']) == 3 assert len(self._get_library_blocks(lib['id'], {'block_type': 'squirrel'})['results']) == 0 - @ddt.data( - ('video-problem', VIDEO, 'problem', 400), - ('video-video', VIDEO, 'video', 200), - ('problem-problem', PROBLEM, 'problem', 200), - ('problem-video', PROBLEM, 'video', 400), - ('complex-video', COMPLEX, 'video', 200), - ('complex-problem', COMPLEX, 'problem', 200), - ) - @ddt.unpack - def test_library_blocks_type_constrained(self, slug, library_type, block_type, expect_response): - """ - Test that type-constrained libraries enforce their constraint when adding an XBlock. - """ - lib = self._create_library( - slug=slug, title="A Test Library", description="Testing XBlocks", library_type=library_type, - ) - lib_id = lib["id"] - - # Add a 'problem' XBlock to the library: - self._add_block_to_library(lib_id, block_type, 'test-block', expect_response=expect_response) - def test_library_not_found(self): """Test that requests fail with 404 when the library does not exist""" valid_not_found_key = 'lb:valid:key:video:1' @@ -755,24 +729,6 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix # Second block should throw error self._add_block_to_library(lib_id, "problem", "problem1", expect_response=400) - @ddt.data( - ('complex-types', COMPLEX, False), - ('video-types', VIDEO, True), - ('problem-types', PROBLEM, True), - ) - @ddt.unpack - def test_block_types(self, slug, library_type, constrained): - """ - Test that the permitted block types listing for a library change based on type. - """ - lib = self._create_library(slug=slug, title='Test Block Types', library_type=library_type) - types = self._get_library_block_types(lib['id']) - if constrained: - assert len(types) == 1 - assert types[0]['block_type'] == library_type - else: - assert len(types) > 1 - def test_content_library_create_event(self): """ Check that CONTENT_LIBRARY_CREATED event is sent when a content library is created. diff --git a/openedx/core/djangoapps/content_libraries/tests/test_models.py b/openedx/core/djangoapps/content_libraries/tests/test_models.py index 81a5a8fa32..eded47a305 100644 --- a/openedx/core/djangoapps/content_libraries/tests/test_models.py +++ b/openedx/core/djangoapps/content_libraries/tests/test_models.py @@ -15,7 +15,6 @@ from organizations.models import Organization from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2 from ..models import ALL_RIGHTS_RESERVED -from ..models import COMPLEX from ..models import ContentLibrary from ..models import LtiGradedResource from ..models import LtiProfile @@ -35,7 +34,6 @@ class ContentLibraryTest(TestCase): return ContentLibrary.objects.create( org=org, slug='foobar', - type=COMPLEX, allow_public_learning=False, allow_public_read=False, license=ALL_RIGHTS_RESERVED, diff --git a/openedx/core/djangoapps/content_libraries/tests/test_runtime.py b/openedx/core/djangoapps/content_libraries/tests/test_runtime.py index f79808a7ec..3b505d3111 100644 --- a/openedx/core/djangoapps/content_libraries/tests/test_runtime.py +++ b/openedx/core/djangoapps/content_libraries/tests/test_runtime.py @@ -21,7 +21,7 @@ from openedx.core.djangoapps.content_libraries.tests.base import ( URL_BLOCK_FIELDS_URL, ) from openedx.core.djangoapps.content_libraries.tests.user_state_block import UserStateTestBlock -from openedx.core.djangoapps.content_libraries.constants import COMPLEX, ALL_RIGHTS_RESERVED +from openedx.core.djangoapps.content_libraries.constants import ALL_RIGHTS_RESERVED from openedx.core.djangoapps.dark_lang.models import DarkLangConfig from openedx.core.djangoapps.xblock import api as xblock_api from openedx.core.djangolib.testing.utils import skip_unless_lms, skip_unless_cms @@ -50,7 +50,6 @@ class ContentLibraryContentTestMixin: _, slug = self.id().rsplit('.', 1) with transaction.atomic(): self.library = library_api.create_library( - library_type=COMPLEX, org=self.organization, slug=slugify(slug), title=(f"{slug} Test Lib"), diff --git a/openedx/core/djangoapps/content_libraries/tests/test_views_lti.py b/openedx/core/djangoapps/content_libraries/tests/test_views_lti.py index a25d02761d..b1ee61ca50 100644 --- a/openedx/core/djangoapps/content_libraries/tests/test_views_lti.py +++ b/openedx/core/djangoapps/content_libraries/tests/test_views_lti.py @@ -5,8 +5,6 @@ Tests for LTI views. from django.conf import settings from django.test import TestCase, override_settings -from openedx.core.djangoapps.content_libraries.constants import PROBLEM - from .base import ( ContentLibrariesRestApiTest, URL_LIB_LTI_JWKS, @@ -60,10 +58,10 @@ class LibraryBlockLtiUrlViewTestMixin: """ library = self._create_library( - slug="libgg", title="A Test Library", description="Testing library", library_type=PROBLEM, + slug="libgg", title="A Test Library", description="Testing library", ) - block = self._add_block_to_library(library['id'], PROBLEM, PROBLEM) + block = self._add_block_to_library(library['id'], 'problem', 'problem') usage_key = str(block['id']) url = f'/api/libraries/v2/blocks/{usage_key}/lti/' diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py index 9357d54ca7..6325d9c4ae 100644 --- a/openedx/core/djangoapps/content_libraries/views.py +++ b/openedx/core/djangoapps/content_libraries/views.py @@ -227,14 +227,12 @@ class LibraryRootView(GenericAPIView): serializer = ContentLibraryFilterSerializer(data=request.query_params) serializer.is_valid(raise_exception=True) org = serializer.validated_data['org'] - library_type = serializer.validated_data['type'] text_search = serializer.validated_data['text_search'] order = serializer.validated_data['order'] queryset = api.get_libraries_for_user( request.user, org=org, - library_type=library_type, text_search=text_search, order=order, ) @@ -259,7 +257,6 @@ class LibraryRootView(GenericAPIView): data = dict(serializer.validated_data) # Converting this over because using the reserved names 'type' and 'license' would shadow the built-in # definitions elsewhere. - data['library_type'] = data.pop('type') data['library_license'] = data.pop('license') key_data = data.pop("key") # Move "slug" out of the "key.slug" pseudo-field that the serializer added: @@ -313,8 +310,6 @@ class LibraryDetailsView(APIView): serializer.is_valid(raise_exception=True) data = dict(serializer.validated_data) # Prevent ourselves from shadowing global names. - if 'type' in data: - data['library_type'] = data.pop('type') if 'license' in data: data['library_license'] = data.pop('license') try: