86 lines
3.6 KiB
Python
86 lines
3.6 KiB
Python
"""
|
|
Management Command to delete course.
|
|
"""
|
|
|
|
|
|
from django.core.management.base import BaseCommand, CommandError
|
|
from opaque_keys import InvalidKeyError
|
|
from opaque_keys.edx.keys import CourseKey
|
|
|
|
from cms.djangoapps.contentstore.utils import delete_course
|
|
from xmodule.contentstore.django import contentstore
|
|
from xmodule.modulestore import ModuleStoreEnum
|
|
from xmodule.modulestore.django import modulestore
|
|
|
|
from .prompt import query_yes_no
|
|
|
|
|
|
class Command(BaseCommand):
|
|
"""
|
|
Delete a MongoDB backed course
|
|
|
|
Example usage:
|
|
$ ./manage.py cms delete_course 'course-v1:edX+DemoX+Demo_Course' --settings=devstack
|
|
$ ./manage.py cms delete_course 'course-v1:edX+DemoX+Demo_Course' --keep-instructors --settings=devstack
|
|
$ ./manage.py cms delete_course 'course-v1:edX+DemoX+Demo_Course' --remove-assets --settings=devstack
|
|
|
|
Note:
|
|
The keep-instructors option is useful for resolving issues that arise when a course run's ID is duplicated
|
|
in a case-insensitive manner. MongoDB is case-sensitive, but MySQL is case-insensitive. This results in
|
|
course-v1:edX+DemoX+1t2017 being treated differently in MongoDB from course-v1:edX+DemoX+1T2017 (capital 'T').
|
|
|
|
If you need to remove a duplicate that has resulted from casing issues, use the --keep-instructors flag
|
|
to ensure that permissions for the remaining course run are not deleted.
|
|
|
|
Use the remove-assets option to ensure all assets are deleted. This is especially relevant to users of the
|
|
split Mongo modulestore.
|
|
"""
|
|
help = 'Delete a MongoDB backed course'
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument(
|
|
'course_key',
|
|
help='ID of the course to delete.',
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--keep-instructors',
|
|
action='store_true',
|
|
default=False,
|
|
help='Do not remove permissions of users and groups for course',
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--remove-assets',
|
|
action='store_true',
|
|
help='Remove all assets associated with the course. '
|
|
'Be careful! These assets may be associated with another course',
|
|
)
|
|
|
|
def handle(self, *args, **options):
|
|
try:
|
|
# a course key may have unicode chars in it
|
|
try:
|
|
course_key = str(options['course_key'], 'utf8')
|
|
# May already be decoded to unicode if coming in through tests, this is ok.
|
|
except TypeError:
|
|
course_key = str(options['course_key'])
|
|
course_key = CourseKey.from_string(course_key)
|
|
except InvalidKeyError:
|
|
raise CommandError('Invalid course_key: {}'.format(options['course_key'])) # lint-amnesty, pylint: disable=raise-missing-from
|
|
|
|
if not modulestore().get_course(course_key):
|
|
raise CommandError('Course not found: {}'.format(options['course_key']))
|
|
|
|
print('Preparing to delete course %s from module store....' % options['course_key'])
|
|
|
|
if query_yes_no(f'Are you sure you want to delete course {course_key}?', default='no'):
|
|
if query_yes_no('Are you sure? This action cannot be undone!', default='no'):
|
|
delete_course(course_key, ModuleStoreEnum.UserID.mgmt_command, options['keep_instructors'])
|
|
|
|
if options['remove_assets']:
|
|
contentstore().delete_all_course_assets(course_key)
|
|
print(f'Deleted assets for course {course_key}') # lint-amnesty, pylint: disable=too-many-format-args
|
|
|
|
print(f'Deleted course {course_key}')
|