-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add library copy management command (#32598)
This PR introduces the "copy" management command, which copies v1 libraries into v2 libraries.
- Loading branch information
1 parent
6b19eab
commit 00b1ce2
Showing
4 changed files
with
303 additions
and
9 deletions.
There are no files selected for viewing
125 changes: 125 additions & 0 deletions
125
cms/djangoapps/contentstore/management/commands/copy_libraries_from_v1_to_v2.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
"""A Command to Copy or uncopy V1 Content Libraries entires to be stored as v2 content libraries.""" | ||
|
||
import logging | ||
from textwrap import dedent | ||
|
||
from django.core.management import BaseCommand, CommandError | ||
|
||
from opaque_keys.edx.keys import CourseKey | ||
from opaque_keys.edx.locator import LibraryLocator | ||
|
||
from xmodule.modulestore.django import modulestore | ||
|
||
|
||
from celery import group | ||
|
||
from cms.djangoapps.contentstore.tasks import create_v2_library_from_v1_library, delete_v2_library_from_v1_library | ||
|
||
from .prompt import query_yes_no | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
class Command(BaseCommand): | ||
""" | ||
Copy or uncopy V1 Content Libraries (default all) entires to be stored as v2 content libraries. | ||
First Specify the uuid for the collection to store the content libraries in. | ||
Specfiy --all for all libraries, library ids for specific libraries, | ||
and -- file followed by the path for a list of libraries from a file. | ||
Example usage: | ||
$ ./manage.py cms copy_libraries_from_v1_to_v2 'collection_uuid' --all | ||
$ ./manage.py cms copy_libraries_from_v1_to_v2 | ||
library-v1:edX+DemoX+Demo_Library' 'library-v1:edX+DemoX+Better_Library' -c 'collection_uuid' | ||
$ ./manage.py cms copy_libraries_from_v1_to_v2 --all --uncopy | ||
$ ./manage.py cms copy_libraries_from_v1_to_v2 'library-v1:edX+DemoX+Better_Library' --uncopy | ||
$ ./manage.py cms copy_libraries_from_v1_to_v2 | ||
'11111111-2111-4111-8111-111111111111' | ||
'./list_of--library-locators- --file | ||
Note: | ||
This Command Also produces an "output file" which contains the mapping of locators and the status of the copy. | ||
""" | ||
|
||
help = dedent(__doc__) | ||
CONFIRMATION_PROMPT = "Reindexing all libraries might be a time consuming operation. Do you want to continue?" | ||
|
||
def add_arguments(self, parser): | ||
"""arguements for command""" | ||
|
||
parser.add_argument( | ||
'-collection_uuid', | ||
'-c', | ||
nargs=1, | ||
type=str, | ||
help='the uuid for the collection to create the content library in.' | ||
) | ||
parser.add_argument( | ||
'library_ids', | ||
nargs='*', | ||
help='a space-seperated list of v1 library ids to copy' | ||
) | ||
parser.add_argument( | ||
'--all', | ||
action='store_true', | ||
dest='all', | ||
help='Copy all libraries' | ||
) | ||
parser.add_argument( | ||
'--uncopy', | ||
action='store_true', | ||
dest='uncopy', | ||
help='Delete libraries specified' | ||
) | ||
|
||
parser.add_argument( | ||
'output_csv', | ||
nargs='?', | ||
default=None, | ||
help='a file path to write the tasks output to. Without this the result is simply logged.' | ||
) | ||
|
||
def _parse_library_key(self, raw_value): | ||
""" Parses library key from string """ | ||
result = CourseKey.from_string(raw_value) | ||
|
||
if not isinstance(result, LibraryLocator): | ||
raise CommandError(f"Argument {raw_value} is not a library key") | ||
return result | ||
|
||
def handle(self, *args, **options): # lint-amnesty, pylint: disable=unused-argument | ||
"""Parse args and generate tasks for copying content.""" | ||
print(options) | ||
|
||
if (not options['library_ids'] and not options['all']) or (options['library_ids'] and options['all']): | ||
raise CommandError("copy_libraries_from_v1_to_v2 requires one or more <library_id>s or the --all flag.") | ||
|
||
if (not options['library_ids'] and not options['all']) or (options['library_ids'] and options['all']): | ||
raise CommandError("copy_libraries_from_v1_to_v2 requires one or more <library_id>s or the --all flag.") | ||
|
||
if options['all']: | ||
store = modulestore() | ||
if query_yes_no(self.CONFIRMATION_PROMPT, default="no"): | ||
v1_library_keys = [ | ||
library.location.library_key.replace(branch=None) for library in store.get_libraries() | ||
] | ||
else: | ||
return | ||
else: | ||
v1_library_keys = list(map(self._parse_library_key, options['library_ids'])) | ||
|
||
create_library_task_group = group([ | ||
delete_v2_library_from_v1_library.s(str(v1_library_key), options['collection_uuid'][0]) | ||
if options['uncopy'] | ||
else create_v2_library_from_v1_library.s(str(v1_library_key), options['collection_uuid'][0]) | ||
for v1_library_key in v1_library_keys | ||
]) | ||
|
||
group_result = create_library_task_group.apply_async().get() | ||
if options['output_csv']: | ||
with open(options['output_csv'][0], 'w', encoding='utf-8', newline='') as output_writer: | ||
output_writer.writerow("v1_library_id", "v2_library_id", "status", "error_msg") | ||
for result in group_result: | ||
output_writer.write(result.keys()) | ||
log.info(group_result) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters