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 ...@@ -10,21 +10,7 @@ from opaque_keys.edx.keys import CourseKey
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore import ModuleStoreEnum
from contentstore.management.commands.utils import user_from_str
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)
class Command(BaseCommand): 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): ...@@ -564,6 +564,22 @@ def _create_new_course(request, org, number, run, fields):
Returns the URL for the course overview page. Returns the URL for the course overview page.
Raises DuplicateCourseError if the course already exists 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 # Set a unique wiki_slug for newly created courses. To maintain active wiki_slugs for
# existing xml courses this cannot be changed in CourseDescriptor. # 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 # # 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): ...@@ -572,31 +588,22 @@ def _create_new_course(request, org, number, run, fields):
definition_data = {'wiki_slug': wiki_slug} definition_data = {'wiki_slug': wiki_slug}
fields.update(definition_data) fields.update(definition_data)
store = modulestore() with modulestore().default_store(store):
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):
# Creating the course raises DuplicateCourseError if an existing course with this org/name is found # 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, org,
number, number,
run, run,
request.user.id, user.id,
fields=fields, fields=fields,
) )
# Make sure user has instructor and staff access to the new course # 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 for user in the new course
initialize_permissions(new_course.id, request.user) initialize_permissions(new_course.id, user)
return new_course
return JsonResponse({
'url': reverse_course_url('course_handler', new_course.id),
'course_key': unicode(new_course.id),
})
def _rerun_course(request, org, number, run, fields): 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