From 6deb4f8d0514f88d1c02407ee8554f89da5abd74 Mon Sep 17 00:00:00 2001 From: Daniel Wong Date: Mon, 27 Oct 2025 15:53:07 -0600 Subject: [PATCH] fix: add to search index when creating library from archive (#37526) Implement full re-index process when creating a library. --- openedx/core/djangoapps/content/search/api.py | 17 +++++++++- .../djangoapps/content/search/handlers.py | 16 +++++++++ .../core/djangoapps/content/search/tasks.py | 5 +-- .../content_libraries/api/blocks.py | 34 ++++++++++++++++++- .../content_libraries/api/libraries.py | 12 +++++-- requirements/constraints.txt | 2 +- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/doc.txt | 2 +- requirements/edx/testing.txt | 2 +- 10 files changed, 82 insertions(+), 12 deletions(-) diff --git a/openedx/core/djangoapps/content/search/api.py b/openedx/core/djangoapps/content/search/api.py index 5575abfd87..f80dec4e32 100644 --- a/openedx/core/djangoapps/content/search/api.py +++ b/openedx/core/djangoapps/content/search/api.py @@ -873,7 +873,7 @@ def upsert_library_container_index_doc(container_key: LibraryContainerLocator) - _update_index_docs([doc]) -def upsert_content_library_index_docs(library_key: LibraryLocatorV2) -> None: +def upsert_content_library_index_docs(library_key: LibraryLocatorV2, full_index: bool = False) -> None: """ Creates or updates the documents for the given Content Library in the search index """ @@ -883,6 +883,21 @@ def upsert_content_library_index_docs(library_key: LibraryLocatorV2) -> None: doc = searchable_doc_for_library_block(metadata) docs.append(doc) + if full_index: + # For a full re-index, we also need to update collections, and containers data: + for container in lib_api.get_library_containers(library_key): + container_key = lib_api.library_container_locator( + library_key, + container, + ) + doc = searchable_doc_for_container(container_key) + docs.append(doc) + + for collection in lib_api.get_library_collections(library_key): + collection_key = lib_api.library_collection_locator(library_key, collection.key) + doc = searchable_doc_for_collection(collection_key, collection=collection) + docs.append(doc) + _update_index_docs(docs) diff --git a/openedx/core/djangoapps/content/search/handlers.py b/openedx/core/djangoapps/content/search/handlers.py index 5e6505cbe7..38dac31535 100644 --- a/openedx/core/djangoapps/content/search/handlers.py +++ b/openedx/core/djangoapps/content/search/handlers.py @@ -19,6 +19,7 @@ from openedx_events.content_authoring.data import ( XBlockData, ) from openedx_events.content_authoring.signals import ( + CONTENT_LIBRARY_CREATED, CONTENT_LIBRARY_DELETED, CONTENT_LIBRARY_UPDATED, CONTENT_OBJECT_ASSOCIATIONS_CHANGED, @@ -187,6 +188,21 @@ def library_block_deleted(**kwargs) -> None: delete_library_block_index_doc.apply(args=[str(library_block_data.usage_key)]) +@receiver(CONTENT_LIBRARY_CREATED) +@only_if_meilisearch_enabled +def content_library_created_handler(**kwargs) -> None: + """ + Create the index for the content library + """ + content_library_data = kwargs.get("content_library", None) + if not content_library_data or not isinstance(content_library_data, ContentLibraryData): # pragma: no cover + log.error("Received null or incorrect data for event") + return + library_key = content_library_data.library_key + + update_content_library_index_docs.apply(args=[str(library_key), True]) + + @receiver(CONTENT_LIBRARY_UPDATED) @only_if_meilisearch_enabled def content_library_updated_handler(**kwargs) -> None: diff --git a/openedx/core/djangoapps/content/search/tasks.py b/openedx/core/djangoapps/content/search/tasks.py index 46d28636f5..8d18c2aae6 100644 --- a/openedx/core/djangoapps/content/search/tasks.py +++ b/openedx/core/djangoapps/content/search/tasks.py @@ -91,7 +91,7 @@ def delete_library_block_index_doc(usage_key_str: str) -> None: @shared_task(base=LoggedTask, autoretry_for=(MeilisearchError, ConnectionError)) @set_code_owner_attribute -def update_content_library_index_docs(library_key_str: str) -> None: +def update_content_library_index_docs(library_key_str: str, full_index: bool = False) -> None: """ Celery task to update the content index documents for all library blocks in a library """ @@ -99,7 +99,8 @@ def update_content_library_index_docs(library_key_str: str) -> None: log.info("Updating content index documents for library with id: %s", library_key) - api.upsert_content_library_index_docs(library_key) + # If full_index is True, also update collections and containers data + api.upsert_content_library_index_docs(library_key, full_index=full_index) @shared_task(base=LoggedTask, autoretry_for=(MeilisearchError, ConnectionError)) diff --git a/openedx/core/djangoapps/content_libraries/api/blocks.py b/openedx/core/djangoapps/content_libraries/api/blocks.py index 472269a875..af44d18745 100644 --- a/openedx/core/djangoapps/content_libraries/api/blocks.py +++ b/openedx/core/djangoapps/content_libraries/api/blocks.py @@ -37,7 +37,10 @@ from openedx_events.content_authoring.signals import ( LIBRARY_CONTAINER_UPDATED ) from openedx_learning.api import authoring as authoring_api -from openedx_learning.api.authoring_models import Component, ComponentVersion, LearningPackage, MediaType +from openedx_learning.api.authoring_models import ( + Component, ComponentVersion, LearningPackage, MediaType, + Container, Collection +) from xblock.core import XBlock from openedx.core.djangoapps.xblock.api import ( @@ -80,6 +83,8 @@ log = logging.getLogger(__name__) __all__ = [ # API methods "get_library_components", + "get_library_containers", + "get_library_collections", "get_library_block", "set_library_block_olx", "get_component_from_usage_key", @@ -121,6 +126,33 @@ def get_library_components( return components +def get_library_containers(library_key: LibraryLocatorV2) -> QuerySet[Container]: + """ + Get all containers in the given content library. + """ + lib = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined] + learning_package = lib.learning_package + assert learning_package is not None + containers: QuerySet[Container] = authoring_api.get_containers( + learning_package.id + ) + + return containers + + +def get_library_collections(library_key: LibraryLocatorV2) -> QuerySet[Collection]: + """ + Get all collections in the given content library. + """ + lib = ContentLibrary.objects.get_by_key(library_key) # type: ignore[attr-defined] + learning_package = lib.learning_package + assert learning_package is not None + collections = authoring_api.get_collections( + learning_package.id + ) + return collections + + def get_library_block(usage_key: LibraryUsageLocatorV2, include_collections=False) -> LibraryXBlockMetadata: """ Get metadata about (the draft version of) one specific XBlock in a library. diff --git a/openedx/core/djangoapps/content_libraries/api/libraries.py b/openedx/core/djangoapps/content_libraries/api/libraries.py index 11e9d25fb9..c67f8afc4c 100644 --- a/openedx/core/djangoapps/content_libraries/api/libraries.py +++ b/openedx/core/djangoapps/content_libraries/api/libraries.py @@ -53,12 +53,17 @@ from django.core.validators import validate_unicode_slug from django.db import IntegrityError, transaction from django.db.models import Q, QuerySet from django.utils.translation import gettext as _ -from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2 -from openedx_events.content_authoring.data import ContentLibraryData +from opaque_keys.edx.locator import ( + LibraryLocatorV2, + LibraryUsageLocatorV2, +) +from openedx_events.content_authoring.data import ( + ContentLibraryData, +) from openedx_events.content_authoring.signals import ( CONTENT_LIBRARY_CREATED, CONTENT_LIBRARY_DELETED, - CONTENT_LIBRARY_UPDATED + CONTENT_LIBRARY_UPDATED, ) from openedx_learning.api import authoring as authoring_api from openedx_learning.api.authoring_models import Component, LearningPackage @@ -407,6 +412,7 @@ def create_library( """ assert isinstance(org, Organization) validate_unicode_slug(slug) + is_learning_package_loaded = learning_package is not None try: with transaction.atomic(): ref = ContentLibrary.objects.create( diff --git a/requirements/constraints.txt b/requirements/constraints.txt index a5931c0690..49da088649 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -61,7 +61,7 @@ numpy<2.0.0 # Date: 2023-09-18 # pinning this version to avoid updates while the library is being developed # Issue for unpinning: https://github.com/openedx/edx-platform/issues/35269 -openedx-learning==0.29.0 +openedx-learning==0.29.1 # Date: 2023-11-29 # Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise. diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 3631b9e7f5..2d7a601405 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -841,7 +841,7 @@ openedx-filters==2.1.0 # ora2 openedx-forum==0.3.8 # via -r requirements/edx/kernel.in -openedx-learning==0.29.0 +openedx-learning==0.29.1 # via # -c requirements/constraints.txt # -r requirements/edx/kernel.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 19fcb3bd9d..df0f69dcaf 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -1393,7 +1393,7 @@ openedx-forum==0.3.8 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -openedx-learning==0.29.0 +openedx-learning==0.29.1 # via # -c requirements/constraints.txt # -r requirements/edx/doc.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 148230096c..cbcaa9ee22 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -1015,7 +1015,7 @@ openedx-filters==2.1.0 # ora2 openedx-forum==0.3.8 # via -r requirements/edx/base.txt -openedx-learning==0.29.0 +openedx-learning==0.29.1 # via # -c requirements/constraints.txt # -r requirements/edx/base.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 6d71050c39..3003e057bf 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -1059,7 +1059,7 @@ openedx-filters==2.1.0 # ora2 openedx-forum==0.3.8 # via -r requirements/edx/base.txt -openedx-learning==0.29.0 +openedx-learning==0.29.1 # via # -c requirements/constraints.txt # -r requirements/edx/base.txt