Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
229c6858
Commit
229c6858
authored
Aug 04, 2017
by
Steven Zheng
Committed by
GitHub
Aug 04, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15697 from edx/LEARNER-2101
Add advanced settings to generate_courses command
parents
55561990
d04c03e6
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
330 additions
and
144 deletions
+330
-144
cms/djangoapps/contentstore/management/commands/generate_courses.py
+160
-0
cms/djangoapps/contentstore/management/commands/generate_test_course.py
+0
-55
cms/djangoapps/contentstore/management/commands/tests/test_generate_courses.py
+170
-0
cms/djangoapps/contentstore/management/commands/tests/test_generate_test_course.py
+0
-89
No files found.
cms/djangoapps/contentstore/management/commands/generate_courses.py
0 → 100644
View file @
229c6858
"""
Django management command to generate a test course from a course config json
"""
import
json
import
logging
from
django.contrib.auth.models
import
User
from
django.core.management.base
import
BaseCommand
,
CommandError
from
contentstore.management.commands.utils
import
user_from_str
from
contentstore.views.course
import
create_new_course_in_store
from
openedx.core.djangoapps.credit.models
import
CreditProvider
from
xmodule.course_module
import
CourseFields
from
xmodule.fields
import
Date
from
xmodule.modulestore.exceptions
import
DuplicateCourseError
from
xmodule.tabs
import
CourseTabList
logger
=
logging
.
getLogger
(
__name__
)
class
Command
(
BaseCommand
):
""" Generate a basic course """
help
=
'Generate courses on studio from a json list of courses'
def
add_arguments
(
self
,
parser
):
parser
.
add_argument
(
'courses_json'
,
)
def
handle
(
self
,
*
args
,
**
options
):
try
:
courses
=
json
.
loads
(
options
[
"courses_json"
])[
"courses"
]
except
ValueError
:
raise
CommandError
(
"Invalid JSON object"
)
except
KeyError
:
raise
CommandError
(
"JSON object is missing courses list"
)
for
course_settings
in
courses
:
# Validate course
if
not
self
.
_course_is_valid
(
course_settings
):
logger
.
warning
(
"Can't create course, proceeding to next course"
)
continue
# Retrieve settings
org
=
course_settings
[
"organization"
]
num
=
course_settings
[
"number"
]
run
=
course_settings
[
"run"
]
user_email
=
course_settings
[
"user"
]
try
:
user
=
user_from_str
(
user_email
)
except
User
.
DoesNotExist
:
logger
.
warning
(
user_email
+
" user does not exist"
)
logger
.
warning
(
"Can't create course, proceeding to next course"
)
continue
fields
=
self
.
_process_course_fields
(
course_settings
[
"fields"
])
# Create the course
try
:
new_course
=
create_new_course_in_store
(
"split"
,
user
,
org
,
num
,
run
,
fields
)
logger
.
info
(
"Created {}"
.
format
(
unicode
(
new_course
.
id
)))
except
DuplicateCourseError
:
logger
.
warning
(
"Course already exists for
%
s,
%
s,
%
s"
,
org
,
num
,
run
)
# Configure credit provider
if
(
"enrollment"
in
course_settings
)
and
(
"credit_provider"
in
course_settings
[
"enrollment"
]):
credit_provider
=
course_settings
[
"enrollment"
][
"credit_provider"
]
if
credit_provider
is
not
None
:
CreditProvider
.
objects
.
get_or_create
(
provider_id
=
credit_provider
,
display_name
=
credit_provider
)
def
_process_course_fields
(
self
,
fields
):
""" Returns a validated list of course fields """
all_fields
=
CourseFields
.
__dict__
.
keys
()
non_course_fields
=
[
"__doc__"
,
"__module__"
,
"__weakref__"
,
"__dict__"
]
for
field
in
non_course_fields
:
all_fields
.
remove
(
field
)
# Non-primitive course fields
date_fields
=
[
"certificate_available_date"
,
"announcement"
,
"enrollment_start"
,
"enrollment_end"
,
"start"
,
"end"
]
course_tab_list_fields
=
[
"tabs"
]
for
field
in
dict
(
fields
):
if
field
not
in
all_fields
:
# field does not exist as a CourseField
del
fields
[
field
]
logger
.
info
(
field
+
"is not a valid CourseField"
)
elif
fields
[
field
]
is
None
:
# field is unset
del
fields
[
field
]
elif
field
in
date_fields
:
# Generate Date object from the json value
try
:
date_json
=
fields
[
field
]
fields
[
field
]
=
Date
()
.
from_json
(
date_json
)
logger
.
info
(
field
+
" has been set to "
+
date_json
)
except
Exception
:
# pylint: disable=broad-except
logger
.
info
(
"The date string could not be parsed for "
+
field
)
del
fields
[
field
]
elif
field
in
course_tab_list_fields
:
# Generate CourseTabList object from the json value
try
:
course_tab_list_json
=
fields
[
field
]
fields
[
field
]
=
CourseTabList
()
.
from_json
(
course_tab_list_json
)
logger
.
info
(
field
+
" has been set to "
+
course_tab_list_json
)
except
Exception
:
# pylint: disable=broad-except
logger
.
info
(
"The course tab list string could not be parsed for "
+
field
)
del
fields
[
field
]
else
:
# CourseField is valid and has been set
logger
.
info
(
field
+
" has been set to "
+
str
(
fields
[
field
]))
for
field
in
all_fields
:
if
field
not
in
fields
:
logger
.
info
(
field
+
" has not been set"
)
return
fields
def
_course_is_valid
(
self
,
course
):
""" Returns true if the course contains required settings """
is_valid
=
True
# Check course settings
required_course_settings
=
[
"organization"
,
"number"
,
"run"
,
"fields"
,
"user"
]
for
setting
in
required_course_settings
:
if
setting
not
in
course
:
logger
.
warning
(
"Course json is missing "
+
setting
)
is_valid
=
False
# Check fields settings
required_field_settings
=
[
"display_name"
]
if
"fields"
in
course
:
for
setting
in
required_field_settings
:
if
setting
not
in
course
[
"fields"
]:
logger
.
warning
(
"Fields json is missing "
+
setting
)
is_valid
=
False
return
is_valid
cms/djangoapps/contentstore/management/commands/generate_test_course.py
deleted
100644 → 0
View file @
55561990
"""
Django management command to generate a test course in a specific modulestore
"""
import
json
from
django.contrib.auth.models
import
User
from
django.core.management.base
import
BaseCommand
,
CommandError
from
contentstore.management.commands.utils
import
user_from_str
from
contentstore.views.course
import
create_new_course_in_store
from
xmodule.modulestore
import
ModuleStoreEnum
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
class
Command
(
BaseCommand
):
""" Generate a basic course """
help
=
'Generate a course with settings on studio'
def
add_arguments
(
self
,
parser
):
parser
.
add_argument
(
'json'
,
help
=
'JSON object with values for store, user, name, organization, number, fields'
)
def
handle
(
self
,
*
args
,
**
options
):
if
options
[
"json"
]
is
None
:
raise
CommandError
(
"Must pass in JSON object"
)
try
:
settings
=
json
.
loads
(
options
[
"json"
])
except
ValueError
:
raise
CommandError
(
"Invalid JSON"
)
if
not
(
all
(
key
in
settings
for
key
in
(
"store"
,
"user"
,
"organization"
,
"number"
,
"run"
,
"fields"
))):
raise
CommandError
(
"JSON object is missing required fields"
)
if
settings
[
"store"
]
in
[
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
]:
store
=
settings
[
"store"
]
else
:
raise
CommandError
(
"Modulestore invalid_store is not valid"
)
try
:
user
=
user_from_str
(
settings
[
"user"
])
except
User
.
DoesNotExist
:
raise
CommandError
(
"User {user} not found"
.
format
(
user
=
settings
[
"user"
]))
org
=
settings
[
"organization"
]
num
=
settings
[
"number"
]
run
=
settings
[
"run"
]
fields
=
settings
[
"fields"
]
# Create the course
new_course
=
create_new_course_in_store
(
store
,
user
,
org
,
num
,
run
,
fields
)
self
.
stdout
.
write
(
u"Created {}"
.
format
(
unicode
(
new_course
.
id
)))
cms/djangoapps/contentstore/management/commands/tests/test_generate_courses.py
0 → 100644
View file @
229c6858
"""
Unittest for generate a test course in an given modulestore
"""
import
json
import
ddt
import
mock
from
django.core.management
import
CommandError
,
call_command
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
@ddt.ddt
class
TestGenerateCourses
(
ModuleStoreTestCase
):
"""
Unit tests for creating a course in split store via command line
"""
@mock.patch
(
'contentstore.management.commands.generate_courses.logger'
)
def
test_generate_course_in_stores
(
self
,
mock_logger
):
"""
Test that a course is created successfully
"""
settings
=
{
"courses"
:
[{
"organization"
:
"test-course-generator"
,
"number"
:
"1"
,
"run"
:
"1"
,
"user"
:
str
(
self
.
user
.
email
),
"fields"
:
{
"display_name"
:
"test-course"
,
"announcement"
:
"2010-04-20T20:08:21.634121"
}
}]}
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
key
=
modulestore
()
.
make_course_key
(
"test-course-generator"
,
"1"
,
"1"
)
self
.
assertTrue
(
modulestore
()
.
has_course
(
key
))
mock_logger
.
info
.
assert_any_call
(
"Created course-v1:test-course-generator+1+1"
)
mock_logger
.
info
.
assert_any_call
(
"announcement has been set to 2010-04-20T20:08:21.634121"
)
mock_logger
.
info
.
assert_any_call
(
"display_name has been set to test-course"
)
def
test_invalid_json
(
self
):
"""
Test that providing an invalid JSON object will result in the appropriate command error
"""
with
self
.
assertRaisesRegexp
(
CommandError
,
"Invalid JSON object"
):
arg
=
"invalid_json"
call_command
(
"generate_courses"
,
arg
)
def
test_missing_courses_list
(
self
):
"""
Test that a missing list of courses in json will result in the appropriate command error
"""
with
self
.
assertRaisesRegexp
(
CommandError
,
"JSON object is missing courses list"
):
settings
=
{}
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
@mock.patch
(
'contentstore.management.commands.generate_courses.logger'
)
@ddt.data
(
"organization"
,
"number"
,
"run"
,
"fields"
)
def
test_missing_course_settings
(
self
,
setting
,
mock_logger
):
"""
Test that missing required settings in JSON object will result in the appropriate error message
"""
settings
=
{
"courses"
:
[{
"organization"
:
"test-course-generator"
,
"number"
:
"1"
,
"run"
:
"1"
,
"user"
:
str
(
self
.
user
.
email
),
"fields"
:
{
"display_name"
:
"test-course"
}
}]}
del
settings
[
"courses"
][
0
][
setting
]
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
mock_logger
.
warning
.
assert_any_call
(
"Course json is missing "
+
setting
)
@mock.patch
(
'contentstore.management.commands.generate_courses.logger'
)
def
test_invalid_user
(
self
,
mock_logger
):
"""
Test that providing an invalid user in the course JSON will result in the appropriate error message
"""
settings
=
{
"courses"
:
[{
"organization"
:
"test-course-generator"
,
"number"
:
"1"
,
"run"
:
"1"
,
"user"
:
"invalid_user"
,
"fields"
:
{
"display_name"
:
"test-course"
}
}]}
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
mock_logger
.
warning
.
assert_any_call
(
"invalid_user user does not exist"
)
@mock.patch
(
'contentstore.management.commands.generate_courses.logger'
)
def
test_missing_display_name
(
self
,
mock_logger
):
"""
Test that missing required display_name in JSON object will result in the appropriate error message
"""
settings
=
{
"courses"
:
[{
"organization"
:
"test-course-generator"
,
"number"
:
"1"
,
"run"
:
"1"
,
"user"
:
str
(
self
.
user
.
email
),
"fields"
:
{}
}]}
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
mock_logger
.
warning
.
assert_any_call
(
"Fields json is missing display_name"
)
@mock.patch
(
'contentstore.management.commands.generate_courses.logger'
)
def
test_invalid_course_field
(
self
,
mock_logger
):
"""
Test that an invalid course field will result in the appropriate message
"""
settings
=
{
"courses"
:
[{
"organization"
:
"test-course-generator"
,
"number"
:
"1"
,
"run"
:
"1"
,
"user"
:
str
(
self
.
user
.
email
),
"fields"
:
{
"display_name"
:
"test-course"
,
"invalid_field"
:
"invalid_value"
}
}]}
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
mock_logger
.
info
.
assert_any_call
((
u'invalid_field'
)
+
"is not a valid CourseField"
)
@mock.patch
(
'contentstore.management.commands.generate_courses.logger'
)
def
test_invalid_date_setting
(
self
,
mock_logger
):
"""
Test that an invalid date json will result in the appropriate message
"""
settings
=
{
"courses"
:
[{
"organization"
:
"test-course-generator"
,
"number"
:
"1"
,
"run"
:
"1"
,
"user"
:
str
(
self
.
user
.
email
),
"fields"
:
{
"display_name"
:
"test-course"
,
"announcement"
:
"invalid_date"
}
}]}
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
mock_logger
.
info
.
assert_any_call
(
"The date string could not be parsed for announcement"
)
@mock.patch
(
'contentstore.management.commands.generate_courses.logger'
)
def
test_invalid_course_tab_list_setting
(
self
,
mock_logger
):
"""
Test that an invalid course tab list json will result in the appropriate message
"""
settings
=
{
"courses"
:
[{
"organization"
:
"test-course-generator"
,
"number"
:
"1"
,
"run"
:
"1"
,
"user"
:
str
(
self
.
user
.
email
),
"fields"
:
{
"display_name"
:
"test-course"
,
"tabs"
:
"invalid_tabs"
}
}]}
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
mock_logger
.
info
.
assert_any_call
(
"The course tab list string could not be parsed for tabs"
)
@mock.patch
(
'contentstore.management.commands.generate_courses.logger'
)
@ddt.data
(
"mobile_available"
,
"enable_proctored_exams"
)
def
test_missing_course_fields
(
self
,
field
,
mock_logger
):
"""
Test that missing course fields in fields json will result in the appropriate message
"""
settings
=
{
"courses"
:
[{
"organization"
:
"test-course-generator"
,
"number"
:
"1"
,
"run"
:
"1"
,
"user"
:
str
(
self
.
user
.
email
),
"fields"
:
{
"display_name"
:
"test-course"
}
}]}
arg
=
json
.
dumps
(
settings
)
call_command
(
"generate_courses"
,
arg
)
mock_logger
.
info
.
assert_any_call
(
field
+
" has not been set"
)
cms/djangoapps/contentstore/management/commands/tests/test_generate_test_course.py
deleted
100644 → 0
View file @
55561990
"""
Unittest for generate a test course in an given modulestore
"""
import
unittest
import
ddt
from
django.core.management
import
CommandError
,
call_command
from
contentstore.management.commands.generate_test_course
import
Command
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.django
import
modulestore
@ddt.ddt
class
TestGenerateTestCourse
(
ModuleStoreTestCase
):
"""
Unit tests for creating a course in either old mongo or split mongo via command line
"""
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_generate_course_in_stores
(
self
,
store
):
"""
Test that courses are created successfully for both ModuleStores
"""
arg
=
(
'{"store":"'
+
store
+
'",'
+
'"user":"'
+
self
.
user
.
email
+
'",'
+
'"organization":"test-course-generator",'
+
'"number":"1",'
+
'"run":"1",'
+
'"fields":{"display_name":"test-course"}}'
)
call_command
(
"generate_test_course"
,
arg
)
key
=
modulestore
()
.
make_course_key
(
"test-course-generator"
,
"1"
,
"1"
)
self
.
assertTrue
(
modulestore
()
.
has_course
(
key
))
def
test_invalid_json
(
self
):
"""
Test that providing an invalid JSON object will result in the appropriate command error
"""
error_msg
=
"Invalid JSON"
with
self
.
assertRaisesRegexp
(
CommandError
,
error_msg
):
arg
=
"invalid_json"
call_command
(
"generate_test_course"
,
arg
)
def
test_missing_fields
(
self
):
"""
Test that missing required fields in JSON object will result in the appropriate command error
"""
error_msg
=
"JSON object is missing required fields"
with
self
.
assertRaisesRegexp
(
CommandError
,
error_msg
):
arg
=
(
'{"store":"invalid_store",'
+
'"user":"user@example.com",'
+
'"organization":"test-course-generator"}'
)
call_command
(
"generate_test_course"
,
arg
)
def
test_invalid_store
(
self
):
"""
Test that providing an invalid store option will result in the appropriate command error
"""
error_msg
=
"Modulestore invalid_store is not valid"
with
self
.
assertRaisesRegexp
(
CommandError
,
error_msg
):
arg
=
(
'{"store":"invalid_store",'
+
'"user":"user@example.com",'
+
'"organization":"test-course-generator",'
+
'"number":"1",'
+
'"run":"1",'
+
'"fields":{"display_name":"test-course"}}'
)
call_command
(
"generate_test_course"
,
arg
)
def
test_invalid_user
(
self
):
"""
Test that providing an invalid user will result in the appropriate command error
"""
error_msg
=
"User invalid_user not found"
with
self
.
assertRaisesRegexp
(
CommandError
,
error_msg
):
arg
=
(
'{"store":"split",'
+
'"user":"invalid_user",'
+
'"organization":"test-course-generator",'
+
'"number":"1",'
+
'"run":"1",'
+
'"fields":{"display_name":"test-course"}}'
)
call_command
(
"generate_test_course"
,
arg
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment