From 96184d2a87f6b386e458a1c0091521441b26a32d Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Wed, 5 Feb 2025 21:16:49 +0530 Subject: [PATCH] feat: use openedx event to create links on import --- .../contentstore/signals/handlers.py | 22 +++++++++++++++++-- .../tests/test_mixed_modulestore.py | 13 +++-------- xmodule/modulestore/xml_importer.py | 14 +++++++----- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/cms/djangoapps/contentstore/signals/handlers.py b/cms/djangoapps/contentstore/signals/handlers.py index 5bb9624e0899..5f33aabead11 100644 --- a/cms/djangoapps/contentstore/signals/handlers.py +++ b/cms/djangoapps/contentstore/signals/handlers.py @@ -12,9 +12,10 @@ from django.dispatch import receiver from edx_toggles.toggles import SettingToggle from opaque_keys.edx.keys import CourseKey -from openedx_events.content_authoring.data import CourseCatalogData, CourseScheduleData, XBlockData +from openedx_events.content_authoring.data import CourseCatalogData, CourseData, CourseScheduleData, XBlockData from openedx_events.content_authoring.signals import ( COURSE_CATALOG_INFO_CHANGED, + POST_COURSE_IMPORT, XBLOCK_CREATED, XBLOCK_DELETED, XBLOCK_UPDATED, @@ -37,7 +38,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError from ..models import PublishableEntityLink -from ..tasks import handle_create_or_update_xblock_upstream_link +from ..tasks import create_or_update_upstream_links, handle_create_or_update_xblock_upstream_link from .signals import GRADING_POLICY_CHANGED log = logging.getLogger(__name__) @@ -269,3 +270,20 @@ def delete_upstream_downstream_link_handler(**kwargs): PublishableEntityLink.objects.filter( downstream_usage_key=xblock_info.usage_key ).delete() + + +@receiver(POST_COURSE_IMPORT) +def handle_new_course_import(**kwargs): + """ + Automatically create upstream->downstream links for course in database on new import. + """ + course_data = kwargs.get("course", None) + if not course_data or not isinstance(course_data, CourseData): + log.error("Received null or incorrect data for event") + return + + create_or_update_upstream_links.delay( + str(course_data.course_key), + force=True, + replace=True + ) diff --git a/xmodule/modulestore/tests/test_mixed_modulestore.py b/xmodule/modulestore/tests/test_mixed_modulestore.py index dc6e3b020e79..0928ab253b9c 100644 --- a/xmodule/modulestore/tests/test_mixed_modulestore.py +++ b/xmodule/modulestore/tests/test_mixed_modulestore.py @@ -164,13 +164,6 @@ def setUp(self): self.course_locations = {} self.user_id = ModuleStoreEnum.UserID.test - # mock and ignore create_or_update_xblock_upstream_link task to avoid unnecessary - # errors as it is tested separately - create_or_update_xblock_upstream_link_patch = patch( - 'cms.djangoapps.contentstore.signals.handlers.handle_create_or_update_xblock_upstream_link' - ) - create_or_update_xblock_upstream_link_patch.start() - self.addCleanup(create_or_update_xblock_upstream_link_patch.stop) def _check_connection(self): """ @@ -1106,7 +1099,7 @@ def test_has_changes_missing_child(self, default_ms, default_branch): # check CONTENT_TAGGING_AUTO CourseWaffleFlag # Find: active_versions, 2 structures (published & draft), definition (unnecessary) # Sends: updated draft and published structures and active_versions - @ddt.data((ModuleStoreEnum.Type.split, 6, 2, 3)) + @ddt.data((ModuleStoreEnum.Type.split, 5, 2, 3)) @ddt.unpack def test_delete_item(self, default_ms, num_mysql, max_find, max_send): """ @@ -1129,7 +1122,7 @@ def test_delete_item(self, default_ms, num_mysql, max_find, max_send): # check CONTENT_TAGGING_AUTO CourseWaffleFlag # find: draft and published structures, definition (unnecessary) # sends: update published (why?), draft, and active_versions - @ddt.data((ModuleStoreEnum.Type.split, 6, 3, 3)) + @ddt.data((ModuleStoreEnum.Type.split, 5, 3, 3)) @ddt.unpack def test_delete_private_vertical(self, default_ms, num_mysql, max_find, max_send): """ @@ -1179,7 +1172,7 @@ def test_delete_private_vertical(self, default_ms, num_mysql, max_find, max_send # check CONTENT_TAGGING_AUTO CourseWaffleFlag # find: structure (cached) # send: update structure and active_versions - @ddt.data((ModuleStoreEnum.Type.split, 6, 1, 2)) + @ddt.data((ModuleStoreEnum.Type.split, 5, 1, 2)) @ddt.unpack def test_delete_draft_vertical(self, default_ms, num_mysql, max_find, max_send): """ diff --git a/xmodule/modulestore/xml_importer.py b/xmodule/modulestore/xml_importer.py index bf15a7f42f26..29cc6883b335 100644 --- a/xmodule/modulestore/xml_importer.py +++ b/xmodule/modulestore/xml_importer.py @@ -35,6 +35,8 @@ from lxml import etree from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.locator import LibraryLocator +from openedx_events.content_authoring.data import CourseData +from openedx_events.content_authoring.signals import POST_COURSE_IMPORT from path import Path as path from xblock.core import XBlockMixin from xblock.fields import Reference, ReferenceList, ReferenceValueDict, Scope @@ -728,11 +730,13 @@ def post_course_import(self, dest_id): """ Trigger celery task to create upstream links for newly imported blocks. """ - # Avoid circular import - from cms.djangoapps.contentstore.tasks import create_or_update_upstream_links - - time_now = datetime.now(tz=timezone.utc) - create_or_update_upstream_links.delay(str(dest_id), force=True, replace=True, created=time_now) + # .. event_implemented_name: XBLOCK_UPDATED + POST_COURSE_IMPORT.send_event( + time=datetime.now(timezone.utc), + course=CourseData( + course_key=dest_id + ) + ) class LibraryImportManager(ImportManager):