diff --git a/cms/djangoapps/contentstore/management/commands/fix_not_found.py b/cms/djangoapps/contentstore/management/commands/fix_not_found.py new file mode 100644 index 0000000000..392a72aa24 --- /dev/null +++ b/cms/djangoapps/contentstore/management/commands/fix_not_found.py @@ -0,0 +1,27 @@ +""" +Script for fixing the item not found errors in a course +""" +from django.core.management.base import BaseCommand, CommandError +from opaque_keys.edx.keys import CourseKey +from xmodule.modulestore.django import modulestore +from xmodule.modulestore import ModuleStoreEnum + +# To run from command line: ./manage.py cms fix_not_found course-v1:org+course+run + + +class Command(BaseCommand): + """Fix a course's item not found errors""" + help = "Fix a course's ItemNotFound errors" + + def handle(self, *args, **options): + "Execute the command" + if len(args) != 1: + raise CommandError("requires 1 argument: ") + + course_key = CourseKey.from_string(args[0]) + # for now only support on split mongo + owning_store = modulestore()._get_modulestore_for_courseid(course_key) + if hasattr(owning_store, 'fix_not_found'): + owning_store.fix_not_found(course_key, ModuleStoreEnum.UserID.mgmt_command) + else: + raise CommandError("The owning modulestore does not support this command.") diff --git a/common/lib/xmodule/xmodule/modulestore/split_migrator.py b/common/lib/xmodule/xmodule/modulestore/split_migrator.py index 8788e0679d..97191258ad 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_migrator.py +++ b/common/lib/xmodule/xmodule/modulestore/split_migrator.py @@ -119,7 +119,7 @@ class SplitMigrator(object): # clean up orphans in published version: in old mongo, parents pointed to the union of their published and draft # children which meant some pointers were to non-existent locations in 'direct' - self.split_modulestore.internal_clean_children(course_version_locator) + self.split_modulestore.fix_not_found(course_version_locator, user_id) def _add_draft_modules_to_course(self, published_course_usage_key, source_course_key, user_id, **kwargs): """ diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py index 5cb62bcf57..3874d8d872 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py @@ -2418,22 +2418,26 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): # update the index entry if appropriate self._update_head(dest_course_key, index_entry, dest_course_key.branch, new_structure['_id']) - def internal_clean_children(self, course_locator): + def fix_not_found(self, course_locator, user_id): """ Only intended for rather low level methods to use. Goes through the children attrs of - each block removing any whose block_id is not a member of the course. Does not generate - a new version of the course but overwrites the existing one. + each block removing any whose block_id is not a member of the course. :param course_locator: the course to clean """ original_structure = self._lookup_course(course_locator).structure - for block in original_structure['blocks'].itervalues(): + index_entry = self._get_index_if_valid(course_locator) + new_structure = self.version_structure(course_locator, original_structure, user_id) + for block in new_structure['blocks'].itervalues(): if 'fields' in block and 'children' in block['fields']: block['fields']["children"] = [ block_id for block_id in block['fields']["children"] - if block_id in original_structure['blocks'] + if block_id in new_structure['blocks'] ] - self.update_structure(course_locator, original_structure) + self.update_structure(course_locator, new_structure) + if index_entry is not None: + # update the index entry if appropriate + self._update_head(course_locator, index_entry, course_locator.branch, new_structure['_id']) def convert_references_to_keys(self, course_key, xblock_class, jsonfields, blocks): """ diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py index 11b23295a4..77699fdd59 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py @@ -251,6 +251,16 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli course_key = self._map_revision_to_branch(course_key) return super(DraftVersioningModuleStore, self).get_orphans(course_key, **kwargs) + def fix_not_found(self, course_key, user_id): + """ + Fix any children which point to non-existent blocks in the course's published and draft branches + """ + for branch in [ModuleStoreEnum.RevisionOption.published_only, ModuleStoreEnum.RevisionOption.draft_only]: + super(DraftVersioningModuleStore, self).fix_not_found( + self._map_revision_to_branch(course_key, branch), + user_id + ) + def has_changes(self, xblock): """ Checks if the given block has unpublished changes