Commit a4d13dc4 by Don Mitchell

Add command line command to create course in either split or mongo

PLAT-189
parent 7496041f
"""
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):
......
"""
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())
"""
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):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment