Add command line command to create course in either split or mongo
PLAT-189
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Django management command to create a course in a specific modulestore
|
||||
"""
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.contrib.auth.models import User
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from contentstore.views.course import create_new_course_in_store
|
||||
from contentstore.management.commands.utils import user_from_str
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
Create a course in a specific modulestore.
|
||||
"""
|
||||
|
||||
# can this query modulestore for the list of write accessible stores or does that violate command pattern?
|
||||
help = "Create a course in one of {}".format([ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split])
|
||||
args = "modulestore user org course run"
|
||||
|
||||
def parse_args(self, *args):
|
||||
"""
|
||||
Return a tuple of passed in values for (modulestore, user, org, course, run).
|
||||
"""
|
||||
if len(args) != 5:
|
||||
raise CommandError(
|
||||
"create_course requires 5 arguments: "
|
||||
"a modulestore, user, org, course, run. Modulestore is one of {}".format(
|
||||
[ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split]
|
||||
)
|
||||
)
|
||||
|
||||
if args[0] not in [ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split]:
|
||||
raise CommandError(
|
||||
"Modulestore (first arg) must be one of {}".format(
|
||||
[ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split]
|
||||
)
|
||||
)
|
||||
storetype = args[0]
|
||||
|
||||
try:
|
||||
user = user_from_str(args[1])
|
||||
except User.DoesNotExist:
|
||||
raise CommandError("No user {} found: expected args are ".format(args[1], self.args))
|
||||
|
||||
org = args[2]
|
||||
course = args[3]
|
||||
run = args[4]
|
||||
|
||||
return storetype, user, org, course, run
|
||||
|
||||
def handle(self, *args, **options):
|
||||
storetype, user, org, course, run = self.parse_args(*args)
|
||||
new_course = create_new_course_in_store(storetype, user, org, course, run, {})
|
||||
self.stdout.write(u"Created {}".format(unicode(new_course.id)))
|
||||
@@ -10,21 +10,7 @@ from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
|
||||
|
||||
def user_from_str(identifier):
|
||||
"""
|
||||
Return a user identified by the given string. The string could be an email
|
||||
address, or a stringified integer corresponding to the ID of the user in
|
||||
the database. If no user could be found, a User.DoesNotExist exception
|
||||
will be raised.
|
||||
"""
|
||||
try:
|
||||
user_id = int(identifier)
|
||||
except ValueError:
|
||||
return User.objects.get(email=identifier)
|
||||
|
||||
return User.objects.get(id=user_id)
|
||||
from contentstore.management.commands.utils import user_from_str
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""
|
||||
Unittests for creating a course in an chosen modulestore
|
||||
"""
|
||||
import unittest
|
||||
import ddt
|
||||
from django.core.management import CommandError, call_command
|
||||
|
||||
from contentstore.management.commands.create_course import Command
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
|
||||
class TestArgParsing(unittest.TestCase):
|
||||
"""
|
||||
Tests for parsing arguments for the `create_course` management command
|
||||
"""
|
||||
def setUp(self):
|
||||
self.command = Command()
|
||||
|
||||
def test_no_args(self):
|
||||
errstring = "create_course requires 5 arguments"
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
self.command.handle('create_course')
|
||||
|
||||
def test_invalid_store(self):
|
||||
with self.assertRaises(CommandError):
|
||||
self.command.handle("foo", "user@foo.org", "org", "course", "run")
|
||||
|
||||
def test_xml_store(self):
|
||||
with self.assertRaises(CommandError):
|
||||
self.command.handle(ModuleStoreEnum.Type.xml, "user@foo.org", "org", "course", "run")
|
||||
|
||||
def test_nonexistent_user_id(self):
|
||||
errstring = "No user 99 found"
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
self.command.handle("split", "99", "org", "course", "run")
|
||||
|
||||
def test_nonexistent_user_email(self):
|
||||
errstring = "No user fake@example.com found"
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
self.command.handle("mongo", "fake@example.com", "org", "course", "run")
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestCreateCourse(ModuleStoreTestCase):
|
||||
"""
|
||||
Unit tests for creating a course in either old mongo or split mongo via command line
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCreateCourse, self).setUp(create_user=True)
|
||||
|
||||
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
|
||||
def test_all_stores_user_email(self, store):
|
||||
call_command(
|
||||
"create_course",
|
||||
store,
|
||||
str(self.user.email),
|
||||
"org", "course", "run"
|
||||
)
|
||||
new_key = modulestore().make_course_key("org", "course", "run")
|
||||
self.assertTrue(
|
||||
modulestore().has_course(new_key),
|
||||
"Could not find course in {}".format(store)
|
||||
)
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(store, modulestore()._get_modulestore_for_courseid(new_key).get_modulestore_type())
|
||||
19
cms/djangoapps/contentstore/management/commands/utils.py
Normal file
19
cms/djangoapps/contentstore/management/commands/utils.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""
|
||||
Common methods for cms commands to use
|
||||
"""
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
def user_from_str(identifier):
|
||||
"""
|
||||
Return a user identified by the given string. The string could be an email
|
||||
address, or a stringified integer corresponding to the ID of the user in
|
||||
the database. If no user could be found, a User.DoesNotExist exception
|
||||
will be raised.
|
||||
"""
|
||||
try:
|
||||
user_id = int(identifier)
|
||||
except ValueError:
|
||||
return User.objects.get(email=identifier)
|
||||
|
||||
return User.objects.get(id=user_id)
|
||||
@@ -564,6 +564,22 @@ def _create_new_course(request, org, number, run, fields):
|
||||
Returns the URL for the course overview page.
|
||||
Raises DuplicateCourseError if the course already exists
|
||||
"""
|
||||
store_for_new_course = (
|
||||
settings.FEATURES.get('DEFAULT_STORE_FOR_NEW_COURSE') or
|
||||
modulestore().default_modulestore.get_modulestore_type()
|
||||
)
|
||||
new_course = create_new_course_in_store(store_for_new_course, request.user, org, number, run, fields)
|
||||
return JsonResponse({
|
||||
'url': reverse_course_url('course_handler', new_course.id),
|
||||
'course_key': unicode(new_course.id),
|
||||
})
|
||||
|
||||
|
||||
def create_new_course_in_store(store, user, org, number, run, fields):
|
||||
"""
|
||||
Create course in store w/ handling instructor enrollment, permissions, and defaulting the wiki slug.
|
||||
Separated out b/c command line course creation uses this as well as the web interface.
|
||||
"""
|
||||
# Set a unique wiki_slug for newly created courses. To maintain active wiki_slugs for
|
||||
# existing xml courses this cannot be changed in CourseDescriptor.
|
||||
# # TODO get rid of defining wiki slug in this org/course/run specific way and reconcile
|
||||
@@ -572,31 +588,22 @@ def _create_new_course(request, org, number, run, fields):
|
||||
definition_data = {'wiki_slug': wiki_slug}
|
||||
fields.update(definition_data)
|
||||
|
||||
store = modulestore()
|
||||
store_for_new_course = (
|
||||
settings.FEATURES.get('DEFAULT_STORE_FOR_NEW_COURSE') or
|
||||
store.default_modulestore.get_modulestore_type()
|
||||
)
|
||||
with store.default_store(store_for_new_course):
|
||||
with modulestore().default_store(store):
|
||||
# Creating the course raises DuplicateCourseError if an existing course with this org/name is found
|
||||
new_course = store.create_course(
|
||||
new_course = modulestore().create_course(
|
||||
org,
|
||||
number,
|
||||
run,
|
||||
request.user.id,
|
||||
user.id,
|
||||
fields=fields,
|
||||
)
|
||||
|
||||
# Make sure user has instructor and staff access to the new course
|
||||
add_instructor(new_course.id, request.user, request.user)
|
||||
add_instructor(new_course.id, user, user)
|
||||
|
||||
# Initialize permissions for user in the new course
|
||||
initialize_permissions(new_course.id, request.user)
|
||||
|
||||
return JsonResponse({
|
||||
'url': reverse_course_url('course_handler', new_course.id),
|
||||
'course_key': unicode(new_course.id),
|
||||
})
|
||||
initialize_permissions(new_course.id, user)
|
||||
return new_course
|
||||
|
||||
|
||||
def _rerun_course(request, org, number, run, fields):
|
||||
|
||||
Reference in New Issue
Block a user