163 lines
6.5 KiB
Python
163 lines
6.5 KiB
Python
"""
|
|
Django management command to generate a test course from a course config json
|
|
"""
|
|
|
|
|
|
import json
|
|
import logging
|
|
|
|
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
|
from django.core.management.base import BaseCommand, CommandError
|
|
|
|
from cms.djangoapps.contentstore.management.commands.utils import user_from_str
|
|
from cms.djangoapps.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") # lint-amnesty, pylint: disable=raise-missing-from
|
|
except KeyError:
|
|
raise CommandError("JSON object is missing courses list") # lint-amnesty, pylint: disable=raise-missing-from
|
|
|
|
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") # lint-amnesty, pylint: disable=logging-not-lazy
|
|
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(str(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 = list(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") # lint-amnesty, pylint: disable=logging-not-lazy
|
|
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) # lint-amnesty, pylint: disable=logging-not-lazy
|
|
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) # lint-amnesty, pylint: disable=logging-not-lazy
|
|
del fields[field]
|
|
else:
|
|
# CourseField is valid and has been set
|
|
logger.info(field + " has been set to " + str(fields[field])) # lint-amnesty, pylint: disable=logging-not-lazy
|
|
|
|
for field in all_fields:
|
|
if field not in fields:
|
|
logger.info(field + " has not been set") # lint-amnesty, pylint: disable=logging-not-lazy
|
|
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) # lint-amnesty, pylint: disable=logging-not-lazy
|
|
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) # lint-amnesty, pylint: disable=logging-not-lazy
|
|
is_valid = False
|
|
|
|
return is_valid
|