Course outline is empty after provisioning
rgraber opened this issue · 16 comments
Due to issues in using old DB dumps for provisioning, course outlines only show the top-level section headers in the LMS for the demo course imported during provisioning.
We have a workaround for this issue (making changes to the course in Studio will cause the course to republish and the subsections to appear), but we would like to ensure that the demo course works correctly after provisioning right out of the box.
Acceptance Critieria:
- After a fresh provision of a course, viewing the course home should display the subsections.
Technical Notes:
- From my investigation, it seems like the issue is caused by the course outline being empty, so all the subsections are filtered out: https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/course_home_api/outline/views.py#L286
- Trying to generate a new outline using https://github.com/openedx/edx-platform/blob/master/cms/djangoapps/contentstore/management/commands/backfill_course_outlines.py doesn't seem to work, unfortunately.
- I don't fully understand why, but the outline created for the demo course seems to have the wrong usage keys or maybe the demo course gets wrong usage keys too, but there's a mismatch even though the course outline has been created.
New suggestion: take a mongo dump and load that in as well so that we don't have to manually import the demo course.
To recreate, just run make dev.provision.lms
.
The problem can be seen on the api endpoint: http://localhost:18000/api/course_home/outline/course-v1:edX+DemoX+Demo_Course The top-level sections will appear, but the subsections will not. The expected subsections can be viewed in Studio.
Follow-up checklist (for Arch-BOM usage)
- Is the issue flaky or consistent?
- Does it affect multiple people or multiple types of systems?
- Update the devstack troubleshooting documentation page if necessary
- Do we need a new troubleshooting section?
- Did a troubleshooting section already exist, but it wasn't easy to find given the symptoms?
- If a recurring issue, should we ticket an automated resolution in place of the doc?
If I change the provisioning scripts to not load the old DB, and instead just wipe any existing one and recreate it empty (DROP DATABASE IF EXISTS edxapp; CREATE DATABASE edxapp DEFAULT CHARACTER SET utf8;
), then after running ./update-dbs-init-sql-scripts.sh
load the demo course... the problem persists. Only if I then edit a section title does the course start behaving properly.
This means that there's something wrong just with the way we're importing the course.
FWIW, you can probably kludge this quickly by putting something like this at the end of the import script:
# In the imports:
from xmodule.modulestore.django import SignalHandler
# in the code somewhere after the content has made it into the modulestore:
SignalHandler.course_published.send_robust(self, course_key=course_key)
I have a hacky approach that works (the correct modulestore class needs to be passed to that signal), but I'm not sure how to turn it into something less terrible...
I currently see the signal getting called twice. Here are the two places, as a diff of stack traces for lack of a better alternative:
--- <unnamed>
+++ <unnamed>
@@ -2,16 +2,12 @@
-> course_items = import_course_from_xml(
/edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py(775)import_course_from_xml()
-> return list(manager.run_imports())
- /edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py(550)run_imports()
--> dest_id, runtime = self.get_dest_id(courselike_key)
- /edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py(618)get_dest_id()
--> new_course = self.store.create_course(
- /edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py(84)inner()
--> retval = func(field_decorator=strip_key_collection, *args, **kwargs)
- /edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py(667)create_course()
--> course = store.create_course(org, course, run, user_id, **kwargs)
- /edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split_draft.py(56)create_course()
--> return item
+ /edx/app/edxapp/edx-platform/xmodule/modulestore/xml_importer.py(566)run_imports()
+-> self.import_children(source_courselike, courselike, courselike_key, dest_id)
+ /usr/lib/python3.8/contextlib.py(120)__exit__()
+-> next(self.gen)
+ /edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py(1043)bulk_operations()
+-> yield
/usr/lib/python3.8/contextlib.py(120)__exit__()
-> next(self.gen)
/edx/app/edxapp/edx-platform/xmodule/modulestore/__init__.py(190)bulk_operations()
So this signal is sent first as a result of get_dest_id
, and then later after import_children
is called. The set of receivers called is identical for the two, and all return None rather than an error. The signal is not sent as a result of import_drafts
, but perhaps that's expected.
I tried adding the below code before the yield courselike
in order to send the signal, but I just got a bunch of "course not found" errors.
from xmodule.modulestore.django import SignalHandler
from xmodule.modulestore.split_mongo.split_draft import DraftVersioningModuleStore
SignalHandler.course_published.send_robust(DraftVersioningModuleStore, course_key=courselike_key)
...I don't really understand the modulestore stuff, but I realized that it was looking in the drafts side (I saw CourseLocator('edX', 'DemoX', 'Demo_Course', 'draft-branch', None)
), and I guess the course is actually published by this point somehow! (Not sure where that happens.) So I swapped out the modulestore class for xmodule.modulestore.split_mongo.split.SplitMongoModuleStore
and that seems to work.
For reference, the errors I was getting:
Log output, including errors
``` 2023-07-25 16:18:19,070 ERROR 45 [cms.djangoapps.contentstore.tasks] [user None] [ip None] tasks.py:271 - ('Course {} does not exist', 'edX/DemoX/Demo_Course') Traceback (most recent call last): File "/edx/app/edxapp/edx-platform/cms/djangoapps/contentstore/tasks.py", line 267, in update_special_exams_and_publish register_exams_handler(course_key) File "/edx/app/edxapp/edx-platform/cms/djangoapps/contentstore/proctoring.py", line 44, in register_special_exams raise ItemNotFoundError("Course {} does not exist", str(course_key)) # lint-amnesty, pylint: disable=raising-format-tuple xmodule.modulestore.exceptions.ItemNotFoundError: ('Course {} does not exist', 'edX/DemoX/Demo_Course') 2023-07-25 16:18:19,073 INFO 45 [cms.djangoapps.contentstore.tasks] [user None] [ip None] tasks.py:273 - Publishing course edX/DemoX/Demo_Course 2023-07-25 16:18:19,074 INFO 45 [celery.app.trace] [user None] [ip None] trace.py:131 - Task cms.djangoapps.contentstore.tasks.update_special_exams_and_publish[53c4aa67-00ef-4f2b-a490-336884395f5c] succeeded in 0.02532108800369315s: None 2023-07-25 16:18:19,078 INFO 45 [openedx.core.djangoapps.discussions.tasks] [user None] [ip None] tasks.py:52 - Updating discussion settings for course: edX/DemoX/Demo_Course 2023-07-25 16:18:19,088 WARNING 45 [edx_toggles.toggles.internal.waffle.flag] [user None] [ip None] flag.py:79 - Flag 'discussions.enable_new_structure_discussions' accessed without a request, which is likely in the context of a celery task. 2023-07-25 16:18:19,100 INFO 45 [openedx.core.djangoapps.discussions.handlers] [user None] [ip None] handlers.py:56 - Updating existing discussion topic links for edX/DemoX/Demo_Course 2023-07-25 16:18:19,104 INFO 45 [openedx.core.djangoapps.discussions.handlers] [user None] [ip None] handlers.py:79 - Creating new discussion topic links for edX/DemoX/Demo_Course 2023-07-25 16:18:19,107 INFO 45 [openedx.core.djangoapps.discussions.handlers] [user None] [ip None] handlers.py:96 - Course edX/DemoX/Demo_Course doesn't have discussion configuration model yet. Creating a new one. 2023-07-25 16:18:19,241 INFO 45 [celery.app.trace] [user None] [ip None] trace.py:131 - Task openedx.core.djangoapps.discussions.tasks.update_discussions_settings_from_course_task[fd1fc44e-a472-4c63-894d-a7b933d38d04] succeeded in 0.16360070300288498s: None 2023-07-25 16:18:19,243 WARNING 45 [edx_toggles.toggles.internal.waffle.flag] [user None] [ip None] flag.py:79 - Flag 'cms.export_course_metadata' accessed without a request, which is likely in the context of a celery task. 2023-07-25 16:18:19,286 INFO 45 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:299 - Attempting to load CourseOverview for course edX/DemoX/Demo_Course from modulestore. 2023-07-25 16:18:19,299 INFO 45 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:360 - Could not create CourseOverview for non-existent course: edX/DemoX/Demo_Course Error calling _listen_for_course_publish in Signal.send_robust() () Traceback (most recent call last): File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 212, in send_robust response = receiver(signal=self, sender=sender, **named) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/course_overviews/signals.py", line 40, in _listen_for_course_publish updated_course_overview = CourseOverview.load_from_module_store(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/course_overviews/models.py", line 364, in load_from_module_store raise cls.DoesNotExist() openedx.core.djangoapps.content.course_overviews.models.CourseOverview.DoesNotExist 2023-07-25 16:18:19,300 ERROR 45 [django.dispatch] [user None] [ip None] dispatcher.py:214 - Error calling _listen_for_course_publish in Signal.send_robust() () Traceback (most recent call last): File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 212, in send_robust response = receiver(signal=self, sender=sender, **named) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/course_overviews/signals.py", line 40, in _listen_for_course_publish updated_course_overview = CourseOverview.load_from_module_store(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/course_overviews/models.py", line 364, in load_from_module_store raise cls.DoesNotExist() openedx.core.djangoapps.content.course_overviews.models.CourseOverview.DoesNotExist 2023-07-25 16:18:19,329 ERROR 45 [edx.celery.task] [user None] [ip None] tasks.py:118 - update_course_in_cache_v2 encountered expected error, retrying. Traceback (most recent call last): File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 107, in _call_and_retry_if_needed api_method(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/api.py", line 32, in update_course_in_cache return get_block_structure_manager(course_key).update_collected_if_needed() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 113, in update_collected_if_needed self._update_collected() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 121, in _update_collected block_structure = BlockStructureFactory.create_from_modulestore( File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/factory.py", line 56, in create_from_modulestore root_xblock = modulestore.get_item(root_block_usage_key, depth=None, lazy=False) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 84, in inner retval = func(field_decorator=strip_key_collection, *args, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 250, in get_item return store.get_item(usage_key, depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split_draft.py", line 283, in get_item return super().get_item(usage_key, depth=depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split.py", line 1178, in get_item raise ItemNotFoundError(usage_key) xmodule.modulestore.exceptions.ItemNotFoundError: i4x://edX/DemoX/course/course@draft-branch 2023-07-25 16:18:19,346 INFO 45 [celery.app.trace] [user None] [ip None] trace.py:131 - Task openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2[d338a00b-2164-4d36-ae2b-136696d1e0bd] retry: Retry in 30s: ItemNotFoundError(BlockUsageLocator(CourseLocator('edX', 'DemoX', 'Demo_Course', 'draft-branch', None), 'course', 'course')) 2023-07-25 16:18:19,364 ERROR 45 [edx.celery.task] [user None] [ip None] tasks.py:118 - update_course_in_cache_v2 encountered expected error, retrying. Traceback (most recent call last): File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 107, in _call_and_retry_if_needed api_method(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/api.py", line 32, in update_course_in_cache return get_block_structure_manager(course_key).update_collected_if_needed() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 113, in update_collected_if_needed self._update_collected() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 121, in _update_collected block_structure = BlockStructureFactory.create_from_modulestore( File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/factory.py", line 56, in create_from_modulestore root_xblock = modulestore.get_item(root_block_usage_key, depth=None, lazy=False) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 84, in inner retval = func(field_decorator=strip_key_collection, *args, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 250, in get_item return store.get_item(usage_key, depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split_draft.py", line 283, in get_item return super().get_item(usage_key, depth=depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split.py", line 1178, in get_item raise ItemNotFoundError(usage_key) xmodule.modulestore.exceptions.ItemNotFoundError: i4x://edX/DemoX/course/course@draft-branch 2023-07-25 16:18:19,369 INFO 45 [celery.app.trace] [user None] [ip None] trace.py:131 - Task openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2[d338a00b-2164-4d36-ae2b-136696d1e0bd] retry: Retry in 30s: ItemNotFoundError(BlockUsageLocator(CourseLocator('edX', 'DemoX', 'Demo_Course', 'draft-branch', None), 'course', 'course')) 2023-07-25 16:18:19,392 ERROR 45 [edx.celery.task] [user None] [ip None] tasks.py:118 - update_course_in_cache_v2 encountered expected error, retrying. Traceback (most recent call last): File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 107, in _call_and_retry_if_needed api_method(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/api.py", line 32, in update_course_in_cache return get_block_structure_manager(course_key).update_collected_if_needed() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 113, in update_collected_if_needed self._update_collected() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 121, in _update_collected block_structure = BlockStructureFactory.create_from_modulestore( File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/factory.py", line 56, in create_from_modulestore root_xblock = modulestore.get_item(root_block_usage_key, depth=None, lazy=False) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 84, in inner retval = func(field_decorator=strip_key_collection, *args, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 250, in get_item return store.get_item(usage_key, depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split_draft.py", line 283, in get_item return super().get_item(usage_key, depth=depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split.py", line 1178, in get_item raise ItemNotFoundError(usage_key) xmodule.modulestore.exceptions.ItemNotFoundError: i4x://edX/DemoX/course/course@draft-branch 2023-07-25 16:18:19,398 INFO 45 [celery.app.trace] [user None] [ip None] trace.py:131 - Task openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2[d338a00b-2164-4d36-ae2b-136696d1e0bd] retry: Retry in 30s: ItemNotFoundError(BlockUsageLocator(CourseLocator('edX', 'DemoX', 'Demo_Course', 'draft-branch', None), 'course', 'course')) 2023-07-25 16:18:19,422 ERROR 45 [edx.celery.task] [user None] [ip None] tasks.py:118 - update_course_in_cache_v2 encountered expected error, retrying. Traceback (most recent call last): File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 107, in _call_and_retry_if_needed api_method(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/api.py", line 32, in update_course_in_cache return get_block_structure_manager(course_key).update_collected_if_needed() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 113, in update_collected_if_needed self._update_collected() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 121, in _update_collected block_structure = BlockStructureFactory.create_from_modulestore( File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/factory.py", line 56, in create_from_modulestore root_xblock = modulestore.get_item(root_block_usage_key, depth=None, lazy=False) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 84, in inner retval = func(field_decorator=strip_key_collection, *args, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 250, in get_item return store.get_item(usage_key, depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split_draft.py", line 283, in get_item return super().get_item(usage_key, depth=depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split.py", line 1178, in get_item raise ItemNotFoundError(usage_key) xmodule.modulestore.exceptions.ItemNotFoundError: i4x://edX/DemoX/course/course@draft-branch 2023-07-25 16:18:19,427 INFO 45 [celery.app.trace] [user None] [ip None] trace.py:131 - Task openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2[d338a00b-2164-4d36-ae2b-136696d1e0bd] retry: Retry in 30s: ItemNotFoundError(BlockUsageLocator(CourseLocator('edX', 'DemoX', 'Demo_Course', 'draft-branch', None), 'course', 'course')) 2023-07-25 16:18:19,450 ERROR 45 [edx.celery.task] [user None] [ip None] tasks.py:118 - update_course_in_cache_v2 encountered expected error, retrying. Traceback (most recent call last): File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 107, in _call_and_retry_if_needed api_method(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/api.py", line 32, in update_course_in_cache return get_block_structure_manager(course_key).update_collected_if_needed() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 113, in update_collected_if_needed self._update_collected() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 121, in _update_collected block_structure = BlockStructureFactory.create_from_modulestore( File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/factory.py", line 56, in create_from_modulestore root_xblock = modulestore.get_item(root_block_usage_key, depth=None, lazy=False) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 84, in inner retval = func(field_decorator=strip_key_collection, *args, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 250, in get_item return store.get_item(usage_key, depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split_draft.py", line 283, in get_item return super().get_item(usage_key, depth=depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split.py", line 1178, in get_item raise ItemNotFoundError(usage_key) xmodule.modulestore.exceptions.ItemNotFoundError: i4x://edX/DemoX/course/course@draft-branch 2023-07-25 16:18:19,455 INFO 45 [celery.app.trace] [user None] [ip None] trace.py:131 - Task openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2[d338a00b-2164-4d36-ae2b-136696d1e0bd] retry: Retry in 30s: ItemNotFoundError(BlockUsageLocator(CourseLocator('edX', 'DemoX', 'Demo_Course', 'draft-branch', None), 'course', 'course')) 2023-07-25 16:18:19,474 ERROR 45 [edx.celery.task] [user None] [ip None] tasks.py:118 - update_course_in_cache_v2 encountered expected error, retrying. Traceback (most recent call last): File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 107, in _call_and_retry_if_needed api_method(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/api.py", line 32, in update_course_in_cache return get_block_structure_manager(course_key).update_collected_if_needed() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 113, in update_collected_if_needed self._update_collected() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 121, in _update_collected block_structure = BlockStructureFactory.create_from_modulestore( File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/factory.py", line 56, in create_from_modulestore root_xblock = modulestore.get_item(root_block_usage_key, depth=None, lazy=False) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 84, in inner retval = func(field_decorator=strip_key_collection, *args, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 250, in get_item return store.get_item(usage_key, depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split_draft.py", line 283, in get_item return super().get_item(usage_key, depth=depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split.py", line 1178, in get_item raise ItemNotFoundError(usage_key) xmodule.modulestore.exceptions.ItemNotFoundError: i4x://edX/DemoX/course/course@draft-branch 2023-07-25 16:18:19,481 ERROR 45 [celery.app.trace] [user None] [ip None] trace.py:270 - Task openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2[d338a00b-2164-4d36-ae2b-136696d1e0bd] raised unexpected: ItemNotFoundError(BlockUsageLocator(CourseLocator('edX', 'DemoX', 'Demo_Course', 'draft-branch', None), 'course', 'course')) Traceback (most recent call last): File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/celery/app/trace.py", line 477, in trace_task R = retval = fun(*args, **kwargs) File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/edx_django_utils/monitoring/internal/code_owner/utils.py", line 193, in new_function return wrapped_function(*args, **kwargs) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 49, in update_course_in_cache_v2 _update_course_in_cache(self, **kwargs) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 67, in _update_course_in_cache _call_and_retry_if_needed(self, api.update_course_in_cache, **kwargs) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 119, in _call_and_retry_if_needed raise self.retry(kwargs=kwargs, exc=exc) File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/celery/app/task.py", line 736, in retry raise_with_context(exc) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/tasks.py", line 107, in _call_and_retry_if_needed api_method(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/api.py", line 32, in update_course_in_cache return get_block_structure_manager(course_key).update_collected_if_needed() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 113, in update_collected_if_needed self._update_collected() File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/manager.py", line 121, in _update_collected block_structure = BlockStructureFactory.create_from_modulestore( File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/block_structure/factory.py", line 56, in create_from_modulestore root_xblock = modulestore.get_item(root_block_usage_key, depth=None, lazy=False) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 84, in inner retval = func(field_decorator=strip_key_collection, *args, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/mixed.py", line 250, in get_item return store.get_item(usage_key, depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split_draft.py", line 283, in get_item return super().get_item(usage_key, depth=depth, **kwargs) File "/edx/app/edxapp/edx-platform/xmodule/modulestore/split_mongo/split.py", line 1178, in get_item raise ItemNotFoundError(usage_key) xmodule.modulestore.exceptions.ItemNotFoundError: i4x://edX/DemoX/course/course@draft-branch 2023-07-25 16:18:19,496 INFO 45 [openedx.core.djangoapps.course_date_signals.handlers] [user None] [ip None] handlers.py:175 - No course found for key edX/DemoX/Demo_Course to extract dates from 2023-07-25 16:18:19,501 INFO 45 [edx.celery.task] [user None] [ip None] tasks.py:162 - Starting XBlockCaches update for course_key: edX/DemoX/Demo_Course 2023-07-25 16:18:19,514 ERROR 45 [celery.app.trace] [user None] [ip None] trace.py:270 - Task openedx.core.djangoapps.bookmarks.tasks.update_xblocks_cache[f53648a8-410d-400f-a6f2-ce5447ee2123] raised unexpected: AttributeError("'NoneType' object has no attribute 'has_children'") Traceback (most recent call last): File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/celery/app/trace.py", line 477, in trace_task R = retval = fun(*args, **kwargs) File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/edx_django_utils/monitoring/internal/code_owner/utils.py", line 193, in new_function return wrapped_function(*args, **kwargs) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/bookmarks/tasks.py", line 163, in update_xblocks_cache _update_xblocks_cache(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/bookmarks/tasks.py", line 115, in _update_xblocks_cache blocks_data = _calculate_course_xblocks_data(course_key) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/bookmarks/tasks.py", line 35, in _calculate_course_xblocks_data children = current_block.get_children() if current_block.has_children else [] AttributeError: 'NoneType' object has no attribute 'has_children' 2023-07-25 16:18:19,519 INFO 45 [openedx.core.djangoapps.course_apps.tasks] [user None] [ip None] tasks.py:41 - Caching course apps status for course with id: edX/DemoX/Demo_Course 2023-07-25 16:18:19,545 INFO 45 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:299 - Attempting to load CourseOverview for course edX/DemoX/Demo_Course from modulestore. 2023-07-25 16:18:19,556 INFO 45 [openedx.core.djangoapps.content.course_overviews.models] [user None] [ip None] models.py:360 - Could not create CourseOverview for non-existent course: edX/DemoX/Demo_Course 2023-07-25 16:18:19,560 ERROR 45 [celery.app.trace] [user None] [ip None] trace.py:270 - Task openedx.core.djangoapps.course_apps.tasks.update_course_apps_status[62208f9b-89d9-40f4-9525-4ce6a5f9487b] raised unexpected: DoesNotExist() Traceback (most recent call last): File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/celery/app/trace.py", line 477, in trace_task R = retval = fun(*args, **kwargs) File "/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/edx_django_utils/monitoring/internal/code_owner/utils.py", line 193, in new_function return wrapped_function(*args, **kwargs) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/course_apps/tasks.py", line 43, in update_course_apps_status is_enabled = course_app.is_enabled(course_key=course_key) File "/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/plugins.py", line 145, in is_enabled return CourseOverview.get_from_id(course_key).show_calculator File "/edx/app/edxapp/edx-platform/openedx/core/lib/cache_utils.py", line 74, in decorator result = wrapped(*args, **kwargs) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/course_overviews/models.py", line 422, in get_from_id return course_overview or cls.load_from_module_store(course_id) File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/content/course_overviews/models.py", line 364, in load_from_module_store raise cls.DoesNotExist() openedx.core.djangoapps.content.course_overviews.models.CourseOverview.DoesNotExist 2023-07-25 16:18:19,649 INFO 45 [celery.app.trace] [user None] [ip None] trace.py:131 - Task lms.djangoapps.discussion.tasks.update_discussions_map[746b1aee-cc1a-4e2c-8918-b02095b4858a] succeeded in 0.0819977339997422s: None 2023-07-25 16:18:19,650 INFO 45 [celery_utils.logged_task] [user None] [ip None] logged_task.py:25 - Task lms.djangoapps.discussion.tasks.update_discussions_map[746b1aee-cc1a-4e2c-8918-b02095b4858a] submitted with arguments [{'course_id': 'edX/DemoX/Demo_Course'}], None ```OK, that only worked with stale state, and I haven't been able to replicate it. But it's close!
This works:
from opaque_keys.edx.locator import CourseLocator
from xmodule.modulestore.django import SignalHandler
course_key = CourseLocator('edX', 'DemoX', 'Demo_Course', None, None)
SignalHandler.course_published.send_robust(type(self.store), course_key=course_key)
...but using the existing courselike_key
does not! As far as I can tell, the difference is that courselike_key
has deprecated=True
. Not sure why, and not sure why it's a problem.
In many parts of the codebase, "published" is synonymous with "updated", regardless of whether it's the draft or published branch. It's often the case that things listening for the course published signal are actually reading it twice (once for the draft and once for published branches), but they're always reading the content from the published branch. So it's wasting the work half the time, but it does that because previous attempts to get it to Do The Right Thing have resulted in weird regressions that were hard to untangle.
Does the existing courselike_key
have versioning information attached to it by any chance? That sort of thing caused issues for me before.
Also, DraftVersioningModuleStore
is a subclass of SplitMongoModuleStore
, and should still work.
deprecated=True
in a course key means it's an Old Mongo style course key (with IDs like MITx/6.002x/Spring), which maps to the DraftModuleStore
and that's no longer supported. It should never work, and you shouldn't have to test for it. That whole modulestore is supposed to die a fiery death, but that PR's been held up because of some questions about possible compatibility breakage. In any case, please don't add anything that creates or manipulates courses of that format.
courselike_key
doesn't have branch or version_guid. As far as I can tell (by iterating over the properties!) the only difference is the deprecated property. I think this maybe is because the import script is using XMLModuleStore to create the course key, and that store explicitly sets deprecated=True. (Not sure about any of this.)
OK, got it! courselike_key
is passed in to the importer, but dest_id
is a version of the key that has deprecated=False. So, this "fixes" it:
from xmodule.modulestore.django import SignalHandler
SignalHandler.course_published.send_robust(type(self.store), course_key=dest_id)
I've created openedx/edx-platform#32843 to use this hack, but the tests fail. More details there.
Another detail: When a course is imported via the UI, this problem does not occur.
Ticket in 2U private issue tracker: https://2u-internal.atlassian.net/browse/TNL-10930
Closing in favor of the TNL ticket