def _create(cls, target_class, **kwargs): # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately org = kwargs.pop('org', None) # because the factory provides a default 'number' arg, prefer the non-defaulted 'course' arg if any number = kwargs.pop('course', kwargs.pop('number', None)) store = kwargs.pop('modulestore') name = kwargs.get('name', kwargs.get('run', Location.clean(kwargs.get('display_name')))) run = kwargs.get('run', name) user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test) location = Location(org, number, run, 'course', name) with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): # Write the data to the mongo datastore new_course = store.create_xmodule(location, metadata=kwargs.get('metadata', None)) # The rest of kwargs become attributes on the course: for k, v in kwargs.iteritems(): setattr(new_course, k, v) # Save the attributes we just set new_course.save() # Update the data in the mongo datastore store.update_item(new_course, user_id) return new_course
def _create(cls, target_class, **kwargs): # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately org = kwargs.pop('org', None) # because the factory provides a default 'number' arg, prefer the non-defaulted 'course' arg if any number = kwargs.pop('course', kwargs.pop('number', None)) store = kwargs.pop('modulestore') name = kwargs.get( 'name', kwargs.get('run', Location.clean(kwargs.get('display_name')))) run = kwargs.get('run', name) user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test) with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): # Write the data to the mongo datastore kwargs.update(kwargs.get('metadata', {})) course_key = SlashSeparatedCourseKey(org, number, run) # TODO - We really should call create_course here. However, since create_course verifies there are no # duplicates, this breaks several tests that do not clean up properly in between tests. new_course = store.create_xblock(None, course_key, 'course', block_id=run, fields=kwargs) store.update_item(new_course, user_id, allow_not_found=True) return new_course
def _create(cls, target_class, **kwargs): # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately org = kwargs.pop('org', None) # because the factory provides a default 'number' arg, prefer the non-defaulted 'course' arg if any number = kwargs.pop('course', kwargs.pop('number', None)) store = kwargs.pop('modulestore') name = kwargs.get('name', kwargs.get('run', Location.clean(kwargs.get('display_name')))) run = kwargs.pop('run', name) user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test) # Pass the metadata just as field=value pairs kwargs.update(kwargs.pop('metadata', {})) default_store_override = kwargs.pop('default_store', None) with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): if default_store_override is not None: with store.default_store(default_store_override): new_course = store.create_course(org, number, run, user_id, fields=kwargs) else: new_course = store.create_course(org, number, run, user_id, fields=kwargs) last_course.loc = new_course.location return new_course
def _create(cls, target_class, **kwargs): # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately org = kwargs.pop('org', None) # because the factory provides a default 'number' arg, prefer the non-defaulted 'course' arg if any number = kwargs.pop('course', kwargs.pop('number', None)) store = kwargs.pop('modulestore') name = kwargs.get('name', kwargs.get('run', Location.clean(kwargs.get('display_name')))) run = kwargs.get('run', name) user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test) # Pass the metadata just as field=value pairs kwargs.update(kwargs.pop('metadata', {})) default_store_override = kwargs.pop('default_store', None) with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): if default_store_override is not None: with store.default_store(default_store_override): new_course = store.create_course(org, number, run, user_id, fields=kwargs) else: new_course = store.create_course(org, number, run, user_id, fields=kwargs) last_course.loc = new_course.location return new_course
def _create(cls, target_class, **kwargs): """ Create and return a new course. For performance reasons, we do not emit signals during this process, but if you need signals to run, you can pass `emit_signals=True` to this method. """ # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately org = kwargs.pop('org', None) # because the factory provides a default 'number' arg, prefer the non-defaulted 'course' arg if any number = kwargs.pop('course', kwargs.pop('number', None)) store = kwargs.pop('modulestore') name = kwargs.get('name', kwargs.get('run', Location.clean(kwargs.get('display_name')))) run = kwargs.pop('run', name) user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test) emit_signals = kwargs.pop('emit_signals', False) # Pass the metadata just as field=value pairs kwargs.update(kwargs.pop('metadata', {})) default_store_override = kwargs.pop('default_store', None) with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): course_key = store.make_course_key(org, number, run) with store.bulk_operations(course_key, emit_signals=emit_signals): if default_store_override is not None: with store.default_store(default_store_override): new_course = store.create_course(org, number, run, user_id, fields=kwargs) else: new_course = store.create_course(org, number, run, user_id, fields=kwargs) last_course.loc = new_course.location return new_course
def _create(cls, target_class, **kwargs): # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately org = kwargs.pop("org", None) # because the factory provides a default 'number' arg, prefer the non-defaulted 'course' arg if any number = kwargs.pop("course", kwargs.pop("number", None)) store = kwargs.pop("modulestore") name = kwargs.get("name", kwargs.get("run", Location.clean(kwargs.get("display_name")))) run = kwargs.get("run", name) location = Location(org, number, run, "course", name) # Write the data to the mongo datastore new_course = store.create_xmodule(location, metadata=kwargs.get("metadata", None)) # The rest of kwargs become attributes on the course: for k, v in kwargs.iteritems(): setattr(new_course, k, v) # Save the attributes we just set new_course.save() # Update the data in the mongo datastore store.update_item(new_course) return new_course
def test_clean(self): with self.assertDeprecationWarning(count=6): with self.assertRaises(InvalidKeyError): Location._check_location_part('abc123', re.compile(r'\d')) self.assertEqual('abc_', Location._clean('abc123', re.compile(r'\d'))) self.assertEqual('a._%-', Location.clean('a.*:%-')) self.assertEqual('a.__%-', Location.clean_keeping_underscores('a.*:%-')) self.assertEqual('a._:%-', Location.clean_for_url_name('a.*:%-')) self.assertEqual('a_-', Location.clean_for_html('a.*:%-'))
def assign_textbook_id(textbook, used_ids=()): """ Return an ID that can be assigned to a textbook and doesn't match the used_ids """ tid = Location.clean(textbook["tab_title"]) if not tid[0].isdigit(): # stick a random digit in front tid = random.choice(string.digits) + tid while tid in used_ids: # add a random ASCII character to the end tid = tid + random.choice(string.ascii_lowercase) return tid
def _create(cls, target_class, **kwargs): # All class attributes (from this class and base classes) are # passed in via **kwargs. However, some of those aren't actual field values, # so pop those off for use separately org = kwargs.pop('org', None) # because the factory provides a default 'number' arg, prefer the non-defaulted 'course' arg if any number = kwargs.pop('course', kwargs.pop('number', None)) store = kwargs.pop('modulestore') name = kwargs.get('name', kwargs.get('run', Location.clean(kwargs.get('display_name')))) run = kwargs.get('run', name) user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test) with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): # Write the data to the mongo datastore kwargs.update(kwargs.get('metadata', {})) course_key = SlashSeparatedCourseKey(org, number, run) # TODO - We really should call create_course here. However, since create_course verifies there are no # duplicates, this breaks several tests that do not clean up properly in between tests. new_course = store.create_xblock(None, course_key, 'course', block_id=run, fields=kwargs) store.update_item(new_course, user_id, allow_not_found=True) return new_course
def load_course(self, course_dir, course_ids, tracker, target_course_id=None): """ Load a course into this module store course_path: Course directory name returns a CourseDescriptor for the course """ log.debug('========> Starting courselike import from %s', course_dir) with open(self.data_dir / course_dir / self.parent_xml) as course_file: # VS[compat] # TODO (cpennington): Remove this once all fall 2012 courses have # been imported into the cms from xml course_file = StringIO( clean_out_mako_templating(course_file.read())) course_data = etree.parse(course_file, parser=edx_xml_parser).getroot() org = course_data.get('org') if org is None: msg = ("No 'org' attribute set for courselike in {dir}. " "Using default 'edx'".format(dir=course_dir)) log.warning(msg) tracker(msg) org = 'edx' # Parent XML should be something like 'library.xml' or 'course.xml' courselike_label = self.parent_xml.split('.')[0] course = course_data.get(courselike_label) if course is None: msg = ( "No '{courselike_label}' attribute set for course in {dir}." " Using default '{default}'".format( courselike_label=courselike_label, dir=course_dir, default=course_dir)) log.warning(msg) tracker(msg) course = course_dir url_name = course_data.get('url_name', course_data.get('slug')) if url_name: policy_dir = self.data_dir / course_dir / 'policies' / url_name policy_path = policy_dir / 'policy.json' policy = self.load_policy(policy_path, tracker) # VS[compat]: remove once courses use the policy dirs. if policy == {}: dog_stats_api.increment( DEPRECATION_VSCOMPAT_EVENT, tags=( "location:xml_load_course_policy_dir", u"course:{}".format(course), )) old_policy_path = self.data_dir / course_dir / 'policies' / '{0}.json'.format( url_name) policy = self.load_policy(old_policy_path, tracker) else: policy = {} # VS[compat] : 'name' is deprecated, but support it for now... if course_data.get('name'): dog_stats_api.increment( DEPRECATION_VSCOMPAT_EVENT, tags=( "location:xml_load_course_course_data_name", u"course:{}".format(course_data.get('course')), u"org:{}".format(course_data.get('org')), u"name:{}".format(course_data.get('name')), )) url_name = Location.clean(course_data.get('name')) tracker("'name' is deprecated for module xml. Please use " "display_name and url_name.") else: url_name = None course_id = self.get_id(org, course, url_name) if course_ids is not None and course_id not in course_ids: return None def get_policy(usage_id): """ Return the policy dictionary to be applied to the specified XBlock usage """ return policy.get(policy_key(usage_id), {}) services = {} if self.i18n_service: services['i18n'] = self.i18n_service if self.fs_service: services['fs'] = self.fs_service if self.user_service: services['user'] = self.user_service system = ImportSystem( xmlstore=self, course_id=course_id, course_dir=course_dir, error_tracker=tracker, load_error_modules=self.load_error_modules, get_policy=get_policy, mixins=self.xblock_mixins, default_class=self.default_class, select=self.xblock_select, field_data=self.field_data, services=services, target_course_id=target_course_id, ) course_descriptor = system.process_xml( etree.tostring(course_data, encoding='unicode')) # If we fail to load the course, then skip the rest of the loading steps if isinstance(course_descriptor, ErrorDescriptor): return course_descriptor self.content_importers(system, course_descriptor, course_dir, url_name) log.debug('========> Done with courselike import from %s', course_dir) return course_descriptor
def load_course(self, course_dir, course_ids, tracker, target_course_id=None): """ Load a course into this module store course_path: Course directory name returns a CourseDescriptor for the course """ log.debug('========> Starting courselike import from %s', course_dir) with open(self.data_dir / course_dir / self.parent_xml) as course_file: # VS[compat] # TODO (cpennington): Remove this once all fall 2012 courses have # been imported into the cms from xml course_file = StringIO(clean_out_mako_templating(course_file.read())) course_data = etree.parse(course_file, parser=edx_xml_parser).getroot() org = course_data.get('org') if org is None: msg = ("No 'org' attribute set for courselike in {dir}. " "Using default 'edx'".format(dir=course_dir)) log.warning(msg) tracker(msg) org = 'edx' # Parent XML should be something like 'library.xml' or 'course.xml' courselike_label = self.parent_xml.split('.')[0] course = course_data.get(courselike_label) if course is None: msg = ( "No '{courselike_label}' attribute set for course in {dir}." " Using default '{default}'".format( courselike_label=courselike_label, dir=course_dir, default=course_dir ) ) log.warning(msg) tracker(msg) course = course_dir url_name = course_data.get('url_name', course_data.get('slug')) if url_name: policy_dir = self.data_dir / course_dir / 'policies' / url_name policy_path = policy_dir / 'policy.json' policy = self.load_policy(policy_path, tracker) # VS[compat]: remove once courses use the policy dirs. if policy == {}: dog_stats_api.increment( DEPRECATION_VSCOMPAT_EVENT, tags=( "location:xml_load_course_policy_dir", u"course:{}".format(course), ) ) old_policy_path = self.data_dir / course_dir / 'policies' / '{0}.json'.format(url_name) policy = self.load_policy(old_policy_path, tracker) else: policy = {} # VS[compat] : 'name' is deprecated, but support it for now... if course_data.get('name'): dog_stats_api.increment( DEPRECATION_VSCOMPAT_EVENT, tags=( "location:xml_load_course_course_data_name", u"course:{}".format(course_data.get('course')), u"org:{}".format(course_data.get('org')), u"name:{}".format(course_data.get('name')), ) ) url_name = Location.clean(course_data.get('name')) tracker("'name' is deprecated for module xml. Please use " "display_name and url_name.") else: url_name = None course_id = self.get_id(org, course, url_name) if course_ids is not None and course_id not in course_ids: return None def get_policy(usage_id): """ Return the policy dictionary to be applied to the specified XBlock usage """ return policy.get(policy_key(usage_id), {}) services = {} if self.i18n_service: services['i18n'] = self.i18n_service if self.fs_service: services['fs'] = self.fs_service if self.user_service: services['user'] = self.user_service system = ImportSystem( xmlstore=self, course_id=course_id, course_dir=course_dir, error_tracker=tracker, load_error_modules=self.load_error_modules, get_policy=get_policy, mixins=self.xblock_mixins, default_class=self.default_class, select=self.xblock_select, field_data=self.field_data, services=services, target_course_id=target_course_id, ) course_descriptor = system.process_xml(etree.tostring(course_data, encoding='unicode')) # If we fail to load the course, then skip the rest of the loading steps if isinstance(course_descriptor, ErrorDescriptor): return course_descriptor self.content_importers(system, course_descriptor, course_dir, url_name) log.debug('========> Done with courselike import from %s', course_dir) return course_descriptor
def load_course(self, course_dir, course_ids, tracker): """ Load a course into this module store course_path: Course directory name returns a CourseDescriptor for the course """ log.debug('========> Starting course import from {0}'.format(course_dir)) with open(self.data_dir / course_dir / "course.xml") as course_file: # VS[compat] # TODO (cpennington): Remove this once all fall 2012 courses have # been imported into the cms from xml course_file = StringIO(clean_out_mako_templating(course_file.read())) course_data = etree.parse(course_file, parser=edx_xml_parser).getroot() org = course_data.get('org') if org is None: msg = ("No 'org' attribute set for course in {dir}. " "Using default 'edx'".format(dir=course_dir)) log.warning(msg) tracker(msg) org = 'edx' course = course_data.get('course') if course is None: msg = ("No 'course' attribute set for course in {dir}." " Using default '{default}'".format(dir=course_dir, default=course_dir ) ) log.warning(msg) tracker(msg) course = course_dir url_name = course_data.get('url_name', course_data.get('slug')) policy_dir = None if url_name: policy_dir = self.data_dir / course_dir / 'policies' / url_name policy_path = policy_dir / 'policy.json' policy = self.load_policy(policy_path, tracker) # VS[compat]: remove once courses use the policy dirs. if policy == {}: old_policy_path = self.data_dir / course_dir / 'policies' / '{0}.json'.format(url_name) policy = self.load_policy(old_policy_path, tracker) else: policy = {} # VS[compat] : 'name' is deprecated, but support it for now... if course_data.get('name'): url_name = Location.clean(course_data.get('name')) tracker("'name' is deprecated for module xml. Please use " "display_name and url_name.") else: raise ValueError("Can't load a course without a 'url_name' " "(or 'name') set. Set url_name.") course_id = SlashSeparatedCourseKey(org, course, url_name) if course_ids is not None and course_id not in course_ids: return None def get_policy(usage_id): """ Return the policy dictionary to be applied to the specified XBlock usage """ return policy.get(policy_key(usage_id), {}) services = {} if self.i18n_service: services['i18n'] = self.i18n_service if self.fs_service: services['fs'] = self.fs_service system = ImportSystem( xmlstore=self, course_id=course_id, course_dir=course_dir, error_tracker=tracker, parent_tracker=self.parent_trackers[course_id], load_error_modules=self.load_error_modules, get_policy=get_policy, mixins=self.xblock_mixins, default_class=self.default_class, select=self.xblock_select, field_data=self.field_data, services=services, ) course_descriptor = system.process_xml(etree.tostring(course_data, encoding='unicode')) # If we fail to load the course, then skip the rest of the loading steps if isinstance(course_descriptor, ErrorDescriptor): return course_descriptor # NOTE: The descriptors end up loading somewhat bottom up, which # breaks metadata inheritance via get_children(). Instead # (actually, in addition to, for now), we do a final inheritance pass # after we have the course descriptor. compute_inherited_metadata(course_descriptor) # now import all pieces of course_info which is expected to be stored # in <content_dir>/info or <content_dir>/info/<url_name> self.load_extra_content(system, course_descriptor, 'course_info', self.data_dir / course_dir / 'info', course_dir, url_name) # now import all static tabs which are expected to be stored in # in <content_dir>/tabs or <content_dir>/tabs/<url_name> self.load_extra_content(system, course_descriptor, 'static_tab', self.data_dir / course_dir / 'tabs', course_dir, url_name) self.load_extra_content(system, course_descriptor, 'custom_tag_template', self.data_dir / course_dir / 'custom_tags', course_dir, url_name) self.load_extra_content(system, course_descriptor, 'about', self.data_dir / course_dir / 'about', course_dir, url_name) log.debug('========> Done with course import from {0}'.format(course_dir)) return course_descriptor