From 135a7ca3f63dc5b56ad0326c88d4406f69fd89ac Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Sat, 7 Sep 2024 13:36:30 -0500 Subject: [PATCH 01/11] fix: Bug when indexing collections --- openedx/core/djangoapps/content/search/api.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py index 9fb49b24b6d1..d29b80fafbcd 100644 --- a/openedx/core/djangoapps/content/search/api.py +++ b/openedx/core/djangoapps/content/search/api.py @@ -20,6 +20,7 @@ from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.locator import LibraryLocatorV2 from openedx_learning.api import authoring as authoring_api +from openedx_learning.api.authoring_models import Collection from common.djangoapps.student.roles import GlobalStaff from rest_framework.request import Request from common.djangoapps.student.role_helpers import get_course_roles @@ -298,7 +299,8 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None: # Get the list of collections status_cb("Counting collections...") - num_collections = authoring_api.get_collections().count() + collections = Collection.objects.filter(enabled=True).select_related("learning_package").order_by('pk') + num_collections = collections.count() # Some counters so we can track our progress as indexing progresses: num_contexts = num_courses + num_libraries + num_collections @@ -459,7 +461,7 @@ def index_collection_batch(batch, num_contexts_done) -> int: return num_contexts_done # To reduce memory usage on large instances, split up the Collections into pages of 100 collections: - paginator = Paginator(authoring_api.get_collections(enabled=True), 100) + paginator = Paginator(collections, 100) for p in paginator.page_range: num_contexts_done = index_collection_batch(paginator.page(p).object_list, num_contexts_done) From 590fc0e4446962672fbb2cb723ecf44ca99c14c7 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Sat, 7 Sep 2024 13:38:17 -0500 Subject: [PATCH 02/11] feat: Add REST-API to create a collection --- .../core/djangoapps/content_libraries/api.py | 2 + .../content_libraries/serializers.py | 20 ++++++++ .../core/djangoapps/content_libraries/urls.py | 2 + .../djangoapps/content_libraries/views.py | 47 +++++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py index 17bea80b3a96..81cee834aa48 100644 --- a/openedx/core/djangoapps/content_libraries/api.py +++ b/openedx/core/djangoapps/content_libraries/api.py @@ -152,6 +152,7 @@ class ContentLibraryMetadata: key = attr.ib(type=LibraryLocatorV2) title = attr.ib("") description = attr.ib("") + learning_package_id = attr.ib(default=None, type=int) num_blocks = attr.ib(0) version = attr.ib(0) type = attr.ib(default=COMPLEX) @@ -392,6 +393,7 @@ def get_library(library_key): return ContentLibraryMetadata( key=library_key, title=learning_package.title, + learning_package_id = learning_package.id, type=ref.type, description=ref.learning_package.description, num_blocks=num_blocks, diff --git a/openedx/core/djangoapps/content_libraries/serializers.py b/openedx/core/djangoapps/content_libraries/serializers.py index 497eda81475b..e78d626eb4c9 100644 --- a/openedx/core/djangoapps/content_libraries/serializers.py +++ b/openedx/core/djangoapps/content_libraries/serializers.py @@ -245,3 +245,23 @@ class ContentLibraryBlockImportTaskCreateSerializer(serializers.Serializer): """ course_key = CourseKeyField() + + +class LibraryCollectionCreationSerializer(serializers.Serializer): + """ + Serializer to create a new library collection. + """ + + title = serializers.CharField() + description = serializers.CharField() + +class LibraryCollectionMetadataSerializer(serializers.Serializer): + """ + Serializer for Library Collection Metadata. + """ + + id = serializers.CharField(read_only=True) + # Rename collection.key to "slug" because "key" is a reserved prop name in React + slug = serializers.CharField(read_only=True) + title = serializers.CharField() + description = serializers.CharField(allow_blank=True) diff --git a/openedx/core/djangoapps/content_libraries/urls.py b/openedx/core/djangoapps/content_libraries/urls.py index 6e450df63522..198e3a0a6c49 100644 --- a/openedx/core/djangoapps/content_libraries/urls.py +++ b/openedx/core/djangoapps/content_libraries/urls.py @@ -45,6 +45,8 @@ path('import_blocks/', include(import_blocks_router.urls)), # Paste contents of clipboard into library path('paste_clipboard/', views.LibraryPasteClipboardView.as_view()), + # list of library collections / create a library collection + path('collections/', views.LibraryCollectionsRootView.as_view()), ])), path('blocks//', include([ # Get metadata about a specific XBlock in this library, or delete the block: diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py index bde8142d3fcc..647da7d024cb 100644 --- a/openedx/core/djangoapps/content_libraries/views.py +++ b/openedx/core/djangoapps/content_libraries/views.py @@ -80,6 +80,7 @@ from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.csrf import csrf_exempt from django.views.generic.base import TemplateResponseMixin, View +from django.utils.text import slugify from pylti1p3.contrib.django import DjangoCacheDataStorage, DjangoDbToolConf, DjangoMessageLaunch, DjangoOIDCLogin from pylti1p3.exception import LtiException, OIDCException @@ -88,6 +89,7 @@ from organizations.api import ensure_organization from organizations.exceptions import InvalidOrganizationException from organizations.models import Organization +from openedx_learning.api import authoring as authoring_api from rest_framework import status from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError from rest_framework.generics import GenericAPIView @@ -113,6 +115,8 @@ LibraryXBlockStaticFilesSerializer, ContentLibraryAddPermissionByEmailSerializer, LibraryPasteClipboardSerializer, + LibraryCollectionCreationSerializer, + LibraryCollectionMetadataSerializer, ) import openedx.core.djangoapps.site_configuration.helpers as configuration_helpers from openedx.core.lib.api.view_utils import view_auth_classes @@ -154,6 +158,10 @@ def wrapped_fn(*args, **kwargs): return wrapped_fn +# Library 1.3 Views +# ============= + + class LibraryApiPaginationDocs: """ API docs for query params related to paginating ContentLibraryMetadata objects. @@ -829,6 +837,45 @@ def retrieve(self, request, lib_key_str, pk=None): return Response(ContentLibraryBlockImportTaskSerializer(import_task).data) +# Library Collections Views +# ============= + +@method_decorator(non_atomic_requests, name="dispatch") +@view_auth_classes() +class LibraryCollectionsRootView(GenericAPIView): + """ + Views to list and create library collections. + """ + + # TODO Implement list collections + + def post(self, request, lib_key_str): + """ + Create a new collection library. + """ + library_key = LibraryLocatorV2.from_string(lib_key_str) + api.require_permission_for_library_key(library_key, request.user, permissions.CAN_EDIT_THIS_CONTENT_LIBRARY) + serializer = LibraryCollectionCreationSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + library = api.get_library(library_key) + title = serializer.validated_data['title'] + + key = slugify(title) + + # TODO verify if key is unique + + result = authoring_api.create_collection( + learning_package_id=library.learning_package_id, + key=key, + title=title, + description=serializer.validated_data['description'], + created_by=request.user.id, + ) + + return Response(LibraryCollectionMetadataSerializer(result).data) + + # LTI 1.3 Views # ============= From 10f3e49f8f008811716d6caffc8c8acff45fc420 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Sat, 7 Sep 2024 14:22:08 -0500 Subject: [PATCH 03/11] feat: Verify key uniques in creating collection view --- .../content_libraries/serializers.py | 2 +- .../djangoapps/content_libraries/views.py | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/openedx/core/djangoapps/content_libraries/serializers.py b/openedx/core/djangoapps/content_libraries/serializers.py index e78d626eb4c9..9b7f1d1141cf 100644 --- a/openedx/core/djangoapps/content_libraries/serializers.py +++ b/openedx/core/djangoapps/content_libraries/serializers.py @@ -253,7 +253,7 @@ class LibraryCollectionCreationSerializer(serializers.Serializer): """ title = serializers.CharField() - description = serializers.CharField() + description = serializers.CharField(allow_blank=True) class LibraryCollectionMetadataSerializer(serializers.Serializer): """ diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py index 647da7d024cb..b55b434cb971 100644 --- a/openedx/core/djangoapps/content_libraries/views.py +++ b/openedx/core/djangoapps/content_libraries/views.py @@ -72,6 +72,7 @@ from django.contrib.auth import authenticate, get_user_model, login from django.contrib.auth.models import Group from django.db.transaction import atomic, non_atomic_requests +from django.db.utils import IntegrityError from django.http import Http404, HttpResponseBadRequest, JsonResponse from django.shortcuts import get_object_or_404 from django.urls import reverse @@ -863,15 +864,16 @@ def post(self, request, lib_key_str): key = slugify(title) - # TODO verify if key is unique - - result = authoring_api.create_collection( - learning_package_id=library.learning_package_id, - key=key, - title=title, - description=serializer.validated_data['description'], - created_by=request.user.id, - ) + try: + result = authoring_api.create_collection( + learning_package_id=library.learning_package_id, + key=key, + title=title, + description=serializer.validated_data['description'], + created_by=request.user.id, + ) + except IntegrityError: + return Response(status=status.HTTP_409_CONFLICT) return Response(LibraryCollectionMetadataSerializer(result).data) From 121ed6c9db8d826683902d64bcd884219c404ca4 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 9 Sep 2024 10:36:42 -0500 Subject: [PATCH 04/11] style: Fix lint on the code --- openedx/core/djangoapps/content/search/api.py | 1 - openedx/core/djangoapps/content_libraries/api.py | 2 +- openedx/core/djangoapps/content_libraries/serializers.py | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py index d29b80fafbcd..7392e1ddd2de 100644 --- a/openedx/core/djangoapps/content/search/api.py +++ b/openedx/core/djangoapps/content/search/api.py @@ -19,7 +19,6 @@ from meilisearch.models.task import TaskInfo from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.locator import LibraryLocatorV2 -from openedx_learning.api import authoring as authoring_api from openedx_learning.api.authoring_models import Collection from common.djangoapps.student.roles import GlobalStaff from rest_framework.request import Request diff --git a/openedx/core/djangoapps/content_libraries/api.py b/openedx/core/djangoapps/content_libraries/api.py index 81cee834aa48..cea92a0d2d02 100644 --- a/openedx/core/djangoapps/content_libraries/api.py +++ b/openedx/core/djangoapps/content_libraries/api.py @@ -393,7 +393,7 @@ def get_library(library_key): return ContentLibraryMetadata( key=library_key, title=learning_package.title, - learning_package_id = learning_package.id, + learning_package_id=learning_package.id, type=ref.type, description=ref.learning_package.description, num_blocks=num_blocks, diff --git a/openedx/core/djangoapps/content_libraries/serializers.py b/openedx/core/djangoapps/content_libraries/serializers.py index 9b7f1d1141cf..dfd28564f919 100644 --- a/openedx/core/djangoapps/content_libraries/serializers.py +++ b/openedx/core/djangoapps/content_libraries/serializers.py @@ -255,11 +255,13 @@ class LibraryCollectionCreationSerializer(serializers.Serializer): title = serializers.CharField() description = serializers.CharField(allow_blank=True) + class LibraryCollectionMetadataSerializer(serializers.Serializer): """ Serializer for Library Collection Metadata. """ + # TODO Set this "id" with the LibraryCollectionKey id = serializers.CharField(read_only=True) # Rename collection.key to "slug" because "key" is a reserved prop name in React slug = serializers.CharField(read_only=True) From 0f173e9bb35e9f08d2dbabd23df923bb852d00b9 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 9 Sep 2024 11:15:42 -0500 Subject: [PATCH 05/11] chore: Using new version of open-edx learning from branch --- requirements/constraints.txt | 2 +- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/doc.txt | 2 +- requirements/edx/kernel.in | 2 +- requirements/edx/testing.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 4321377fcb1d..6ebfe818fcf7 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -93,7 +93,7 @@ libsass==0.10.0 click==8.1.6 # pinning this version to avoid updates while the library is being developed -openedx-learning==0.11.2 +openedx-learning==0.11.4 # 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 6d2307072e0b..e050b4c315bd 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -824,7 +824,7 @@ openedx-filters==1.9.0 # -r requirements/edx/kernel.in # lti-consumer-xblock # ora2 -openedx-learning==0.11.2 +openedx-learning @ git+https://github.com/open-craft/openedx-learning.git@jill/collection-key # via # -c requirements/edx/../constraints.txt # -r requirements/edx/kernel.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 85d4993917e8..94ae329ac120 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -1373,7 +1373,7 @@ openedx-filters==1.9.0 # -r requirements/edx/testing.txt # lti-consumer-xblock # ora2 -openedx-learning==0.11.2 +openedx-learning @ git+https://github.com/open-craft/openedx-learning.git@jill/collection-key # via # -c requirements/edx/../constraints.txt # -r requirements/edx/doc.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 21160db584b7..f97536a7591d 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -983,7 +983,7 @@ openedx-filters==1.9.0 # -r requirements/edx/base.txt # lti-consumer-xblock # ora2 -openedx-learning==0.11.2 +openedx-learning @ git+https://github.com/open-craft/openedx-learning.git@jill/collection-key # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt diff --git a/requirements/edx/kernel.in b/requirements/edx/kernel.in index a5b510742ac7..8e423708336f 100644 --- a/requirements/edx/kernel.in +++ b/requirements/edx/kernel.in @@ -119,7 +119,7 @@ openedx-calc # Library supporting mathematical calculatio openedx-django-require openedx-events # Open edX Events from Hooks Extension Framework (OEP-50) openedx-filters # Open edX Filters from Hooks Extension Framework (OEP-50) -openedx-learning # Open edX Learning core (experimental) +git+https://github.com/open-craft/openedx-learning.git@jill/collection-key#egg=openedx-learning # Open edX Learning core (experimental) openedx-mongodbproxy openedx-django-wiki path diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 4771b0c21fe8..c0c6a29dc997 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -1034,7 +1034,7 @@ openedx-filters==1.9.0 # -r requirements/edx/base.txt # lti-consumer-xblock # ora2 -openedx-learning==0.11.2 +openedx-learning @ git+https://github.com/open-craft/openedx-learning.git@jill/collection-key # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt From bf367e8b50aa9713bc184c778138eed5a612b276 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 9 Sep 2024 11:21:03 -0500 Subject: [PATCH 06/11] style: Nit on one comment --- openedx/core/djangoapps/content_libraries/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py index b55b434cb971..fde0c304687e 100644 --- a/openedx/core/djangoapps/content_libraries/views.py +++ b/openedx/core/djangoapps/content_libraries/views.py @@ -159,7 +159,7 @@ def wrapped_fn(*args, **kwargs): return wrapped_fn -# Library 1.3 Views +# Library Views # ============= From 57bb313cda317eb5f03ae16896f94a716f86d406 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 9 Sep 2024 11:44:10 -0500 Subject: [PATCH 07/11] test: for REST API to create collections --- .../tests/test_content_libraries.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) 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 95b7309b3cd1..e6bf68c06e36 100644 --- a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py +++ b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py @@ -24,6 +24,7 @@ from openedx_events.tests.utils import OpenEdxEventsTestMixin from openedx.core.djangoapps.content_libraries.tests.base import ( ContentLibrariesRestApiTest, + URL_LIB_DETAIL, URL_BLOCK_METADATA_URL, URL_BLOCK_RENDER_VIEW, URL_BLOCK_GET_HANDLER_URL, @@ -1063,3 +1064,27 @@ def test_not_found_fails_correctly(self): self.assertEqual(response.json(), { 'detail': f"XBlock {valid_not_found_key} does not exist, or you don't have permission to view it.", }) + + +class LibraryCollectionsTestCase(ContentLibrariesRestApiTest): + + def test_create_collection(self): + """ + Test collection creation + + Tests with some non-ASCII chars in title, description. + """ + url = URL_LIB_DETAIL + 'collections/' + lib = self._create_library( + slug="téstlꜟط", title="A Tést Lꜟطrary", description="Just Téstꜟng", license_type=CC_4_BY, + ) + collection = self._api("post", url.format(lib_key=lib["id"]), { + 'title': 'A Tést Lꜟطrary Collection', + 'description': 'Just Téstꜟng', + }, 200) + expected_data = { + 'id': '1', + 'title': 'A Tést Lꜟطrary Collection', + 'description': 'Just Téstꜟng' + } + self.assertDictContainsEntries(collection, expected_data) From 44144c4b443b5063d31a52a56eb2a1a46e054c78 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 9 Sep 2024 15:15:38 -0500 Subject: [PATCH 08/11] refactor: Create correlative key in create collection rest api view --- .../djangoapps/content_libraries/views.py | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py index fde0c304687e..24c3e25323e6 100644 --- a/openedx/core/djangoapps/content_libraries/views.py +++ b/openedx/core/djangoapps/content_libraries/views.py @@ -864,16 +864,23 @@ def post(self, request, lib_key_str): key = slugify(title) - try: - result = authoring_api.create_collection( - learning_package_id=library.learning_package_id, - key=key, - title=title, - description=serializer.validated_data['description'], - created_by=request.user.id, - ) - except IntegrityError: - return Response(status=status.HTTP_409_CONFLICT) + attempt = 0 + result = None + + # It's possible that the key is not unique in the database + # So to avoid that, we add a correlative number in the key + while not result: + modified_key = key if attempt == 0 else key + str(attempt) + try: + result = authoring_api.create_collection( + learning_package_id=library.learning_package_id, + key=modified_key, + title=title, + description=serializer.validated_data['description'], + created_by=request.user.id, + ) + except IntegrityError: + attempt += 1 return Response(LibraryCollectionMetadataSerializer(result).data) From 817e3b41fed220d7cc05f7626c70ef974a372bdd Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 9 Sep 2024 15:39:28 -0500 Subject: [PATCH 09/11] fix: Adding slug to create collection response --- .../content_libraries/serializers.py | 2 +- .../tests/test_content_libraries.py | 28 +++++++++++++++++++ .../djangoapps/content_libraries/views.py | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/openedx/core/djangoapps/content_libraries/serializers.py b/openedx/core/djangoapps/content_libraries/serializers.py index dfd28564f919..42941143f111 100644 --- a/openedx/core/djangoapps/content_libraries/serializers.py +++ b/openedx/core/djangoapps/content_libraries/serializers.py @@ -264,6 +264,6 @@ class LibraryCollectionMetadataSerializer(serializers.Serializer): # TODO Set this "id" with the LibraryCollectionKey id = serializers.CharField(read_only=True) # Rename collection.key to "slug" because "key" is a reserved prop name in React - slug = serializers.CharField(read_only=True) + slug = serializers.CharField(source="key", read_only=True) title = serializers.CharField() description = serializers.CharField(allow_blank=True) 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 e6bf68c06e36..c8d433dadc7f 100644 --- a/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py +++ b/openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py @@ -1084,7 +1084,35 @@ def test_create_collection(self): }, 200) expected_data = { 'id': '1', + 'slug': 'a-test-lrary-collection', 'title': 'A Tést Lꜟطrary Collection', 'description': 'Just Téstꜟng' } self.assertDictContainsEntries(collection, expected_data) + + def test_create_collection_same_key(self): + """ + Test collection creation with same key + """ + url = URL_LIB_DETAIL + 'collections/' + lib = self._create_library( + slug="téstlꜟط", title="A Tést Lꜟطrary", description="Just Téstꜟng", license_type=CC_4_BY, + ) + + self._api("post", url.format(lib_key=lib["id"]), { + 'title': 'Test Collection', + 'description': 'Just a Test', + }, 200) + + for i in range(0, 100): + collection = self._api("post", url.format(lib_key=lib["id"]), { + 'title': 'Test Collection', + 'description': 'Just a Test', + }, 200) + expected_data = { + 'id': collection['id'], + 'slug': f'test-collection-{i + 1}', + 'title': 'Test Collection', + 'description': 'Just a Test' + } + self.assertDictContainsEntries(collection, expected_data) diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py index 24c3e25323e6..3a7500133492 100644 --- a/openedx/core/djangoapps/content_libraries/views.py +++ b/openedx/core/djangoapps/content_libraries/views.py @@ -870,7 +870,7 @@ def post(self, request, lib_key_str): # It's possible that the key is not unique in the database # So to avoid that, we add a correlative number in the key while not result: - modified_key = key if attempt == 0 else key + str(attempt) + modified_key = key if attempt == 0 else key + '-' + str(attempt) try: result = authoring_api.create_collection( learning_package_id=library.learning_package_id, From b7378ab3359e5395cf66969579a6498e05a25972 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 9 Sep 2024 17:06:39 -0500 Subject: [PATCH 10/11] feat: Update openedx_events and add LIBRARY_COLLECTION_CREATED event on create collection API view --- openedx/core/djangoapps/content_libraries/views.py | 9 +++++++++ requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/doc.txt | 2 +- requirements/edx/kernel.in | 2 +- requirements/edx/testing.txt | 2 +- 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/openedx/core/djangoapps/content_libraries/views.py b/openedx/core/djangoapps/content_libraries/views.py index 3a7500133492..ce9427f70e96 100644 --- a/openedx/core/djangoapps/content_libraries/views.py +++ b/openedx/core/djangoapps/content_libraries/views.py @@ -98,6 +98,8 @@ from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet +from openedx_events.content_authoring.data import LibraryCollectionData +from openedx_events.content_authoring.signals import LIBRARY_COLLECTION_CREATED from openedx.core.djangoapps.content_libraries import api, permissions from openedx.core.djangoapps.content_libraries.serializers import ( @@ -882,6 +884,13 @@ def post(self, request, lib_key_str): except IntegrityError: attempt += 1 + LIBRARY_COLLECTION_CREATED.send_event( + library_collection=LibraryCollectionData( + library_key=library_key, + collection_key=result.id, + ) + ) + return Response(LibraryCollectionMetadataSerializer(result).data) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index fdcccada2ebe..e163df903a98 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -811,7 +811,7 @@ openedx-django-require==2.1.0 # via -r requirements/edx/kernel.in openedx-django-wiki==2.1.0 # via -r requirements/edx/kernel.in -openedx-events==9.12.0 +openedx-events==9.14.0 # via # -r requirements/edx/kernel.in # edx-enterprise diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index c06478c4834c..fcd594bb041e 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -1358,7 +1358,7 @@ openedx-django-wiki==2.1.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -openedx-events==9.12.0 +openedx-events==9.14.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index d1696973663a..4b7188901f92 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -970,7 +970,7 @@ openedx-django-require==2.1.0 # via -r requirements/edx/base.txt openedx-django-wiki==2.1.0 # via -r requirements/edx/base.txt -openedx-events==9.12.0 +openedx-events==9.14.0 # via # -r requirements/edx/base.txt # edx-enterprise diff --git a/requirements/edx/kernel.in b/requirements/edx/kernel.in index 8e423708336f..5734e07bfb9b 100644 --- a/requirements/edx/kernel.in +++ b/requirements/edx/kernel.in @@ -117,7 +117,7 @@ olxcleaner openedx-atlas # CLI tool to manage translations openedx-calc # Library supporting mathematical calculations for Open edX openedx-django-require -openedx-events # Open edX Events from Hooks Extension Framework (OEP-50) +openedx-events>=9.14.0 # Open edX Events from Hooks Extension Framework (OEP-50) openedx-filters # Open edX Filters from Hooks Extension Framework (OEP-50) git+https://github.com/open-craft/openedx-learning.git@jill/collection-key#egg=openedx-learning # Open edX Learning core (experimental) openedx-mongodbproxy diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index d0e3f8c4e748..64fd32561b47 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -1021,7 +1021,7 @@ openedx-django-require==2.1.0 # via -r requirements/edx/base.txt openedx-django-wiki==2.1.0 # via -r requirements/edx/base.txt -openedx-events==9.12.0 +openedx-events==9.14.0 # via # -r requirements/edx/base.txt # edx-enterprise From 3180f760c756373a88954493c8a1cef8e4794761 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Tue, 10 Sep 2024 09:05:31 -0500 Subject: [PATCH 11/11] revert: fixes in rebuild index --- openedx/core/djangoapps/content/search/api.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py index 7392e1ddd2de..9fb49b24b6d1 100644 --- a/openedx/core/djangoapps/content/search/api.py +++ b/openedx/core/djangoapps/content/search/api.py @@ -19,7 +19,7 @@ from meilisearch.models.task import TaskInfo from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.locator import LibraryLocatorV2 -from openedx_learning.api.authoring_models import Collection +from openedx_learning.api import authoring as authoring_api from common.djangoapps.student.roles import GlobalStaff from rest_framework.request import Request from common.djangoapps.student.role_helpers import get_course_roles @@ -298,8 +298,7 @@ def rebuild_index(status_cb: Callable[[str], None] | None = None) -> None: # Get the list of collections status_cb("Counting collections...") - collections = Collection.objects.filter(enabled=True).select_related("learning_package").order_by('pk') - num_collections = collections.count() + num_collections = authoring_api.get_collections().count() # Some counters so we can track our progress as indexing progresses: num_contexts = num_courses + num_libraries + num_collections @@ -460,7 +459,7 @@ def index_collection_batch(batch, num_contexts_done) -> int: return num_contexts_done # To reduce memory usage on large instances, split up the Collections into pages of 100 collections: - paginator = Paginator(collections, 100) + paginator = Paginator(authoring_api.get_collections(enabled=True), 100) for p in paginator.page_range: num_contexts_done = index_collection_batch(paginator.page(p).object_list, num_contexts_done)