def create_toy_course(self, org='edX', course='toy', run='2012_Fall'): """ Create an equivalent to the toy xml course """ # with self.store.bulk_write_operations(self.store.make_course_key(org, course, run)): self.toy_loc = self.create_sample_course( org, course, run, TOY_BLOCK_INFO_TREE, { "textbooks": [[ "Textbook", "https://s3.amazonaws.com/edx-textbooks/guttag_computation_v3/" ]], "wiki_slug": "toy", "display_name": "Toy Course", "graded": True, "tabs": [ CoursewareTab(), CourseInfoTab(), StaticTab(name="Syllabus", url_slug="syllabus"), StaticTab(name="Resources", url_slug="resources"), DiscussionTab(), WikiTab(), ProgressTab(), ], "discussion_topics": { "General": { "id": "i4x-edX-toy-course-2012_Fall" } }, "graceperiod": datetime.timedelta(days=2, seconds=21599), "start": datetime.datetime(2015, 07, 17, 12, tzinfo=pytz.utc), "xml_attributes": { "filename": ["course/2012_Fall.xml", "course/2012_Fall.xml"] }, "pdf_textbooks": [{ "tab_title": "Sample Multi Chapter Textbook", "id": "MyTextbook", "chapters": [{ "url": "/static/Chapter1.pdf", "title": "Chapter 1" }, { "url": "/static/Chapter2.pdf", "title": "Chapter 2" }] }], "course_image": "just_a_test.jpg", })
def create_and_save_xmodule(self, location, definition_data=None, metadata=None, system=None, fields={}): """ Create the new xmodule and save it. Does not return the new module because if the caller will insert it as a child, it's inherited metadata will completely change. The difference between this and just doing create_xmodule and update_item is this ensures static_tabs get pointed to by the course. :param location: a Location--must have a category :param definition_data: can be empty. The initial definition_data for the kvs :param metadata: can be empty, the initial metadata for the kvs :param system: if you already have an xblock from the course, the xblock.runtime value """ # differs from split mongo in that I believe most of this logic should be above the persistence # layer but added it here to enable quick conversion. I'll need to reconcile these. new_object = self.create_xmodule(location, definition_data, metadata, system, fields) location = new_object.scope_ids.usage_id self.update_item(new_object, allow_not_found=True) # VS[compat] cdodge: This is a hack because static_tabs also have references from the course module, so # if we add one then we need to also add it to the policy information (i.e. metadata) # we should remove this once we can break this reference from the course to static tabs # TODO move this special casing to app tier (similar to attaching new element to parent) if location.category == 'static_tab': course = self._get_course_for_item(location) course.tabs.append( StaticTab( name=new_object.display_name, url_slug=new_object.scope_ids.usage_id.name, ) ) self.update_item(course) return new_object
def _create_item(request): """View for create items.""" usage_key = usage_key_with_run(request.json['parent_locator']) category = request.json['category'] display_name = request.json.get('display_name') if not has_course_access(request.user, usage_key.course_key): raise PermissionDenied() store = modulestore() parent = store.get_item(usage_key) dest_usage_key = usage_key.replace(category=category, name=uuid4().hex) # get the metadata, display_name, and definition from the request metadata = {} data = None template_id = request.json.get('boilerplate') if template_id: clz = parent.runtime.load_block_type(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') if display_name is not None: metadata['display_name'] = display_name # TODO need to fix components that are sending definition_data as strings, instead of as dicts # For now, migrate them into dicts here. if isinstance(data, basestring): data = {'data': data} created_block = store.create_child( request.user.id, usage_key, dest_usage_key.block_type, block_id=dest_usage_key.block_id, definition_data=data, metadata=metadata, runtime=parent.runtime, ) # VS[compat] cdodge: This is a hack because static_tabs also have references from the course module, so # if we add one then we need to also add it to the policy information (i.e. metadata) # we should remove this once we can break this reference from the course to static tabs if category == 'static_tab': display_name = display_name or _("Empty") # Prevent name being None course = store.get_course(dest_usage_key.course_key) course.tabs.append( StaticTab( name=display_name, url_slug=dest_usage_key.name, ) ) store.update_item(course, request.user.id) return JsonResponse({"locator": unicode(created_block.location), "courseKey": unicode(created_block.location.course_key)})
def _create_item(request): """View for create items.""" usage_key = UsageKey.from_string(request.json['parent_locator']) # usage_key's course_key may have an empty run property usage_key = usage_key.replace( course_key=modulestore().fill_in_run(usage_key.course_key)) category = request.json['category'] display_name = request.json.get('display_name') if not has_course_access(request.user, usage_key.course_key): raise PermissionDenied() store = modulestore() parent = store.get_item(usage_key) dest_usage_key = usage_key.replace(category=category, name=uuid4().hex) # get the metadata, display_name, and definition from the request metadata = {} data = None template_id = request.json.get('boilerplate') if template_id: clz = parent.runtime.load_block_type(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') if display_name is not None: metadata['display_name'] = display_name created_block = store.create_child( request.user.id, usage_key, dest_usage_key.block_type, block_id=dest_usage_key.block_id, definition_data=data, metadata=metadata, runtime=parent.runtime, ) # VS[compat] cdodge: This is a hack because static_tabs also have references from the course module, so # if we add one then we need to also add it to the policy information (i.e. metadata) # we should remove this once we can break this reference from the course to static tabs if category == 'static_tab': course = store.get_course(dest_usage_key.course_key) course.tabs.append( StaticTab( name=display_name, url_slug=dest_usage_key.name, )) store.update_item(course, request.user.id) return JsonResponse({ "locator": unicode(created_block.location), "courseKey": unicode(created_block.location.course_key) })
def get_tab_by_locator(tab_list, usage_key_string): """ Look for a tab with the specified locator. Returns the first matching tab. """ tab_location = UsageKey.from_string(usage_key_string) item = modulestore().get_item(tab_location) static_tab = StaticTab( name=item.display_name, url_slug=item.location.name, ) return CourseTabList.get_tab_by_id(tab_list, static_tab.tab_id)
def get_tab_by_locator(tab_list, tab_locator): """ Look for a tab with the specified locator. Returns the first matching tab. """ tab_location = loc_mapper().translate_locator_to_location( BlockUsageLocator(tab_locator)) item = modulestore('direct').get_item(tab_location) static_tab = StaticTab( name=item.display_name, url_slug=item.location.name, ) return CourseTabList.get_tab_by_id(tab_list, static_tab.tab_id)
def get_tab_by_locator( tab_list: List[CourseTab], tab_location: Union[str, UsageKey]) -> Optional[CourseTab]: """ Look for a tab with the specified locator. Returns the first matching tab. """ if isinstance(tab_location, str): tab_location = UsageKey.from_string(tab_location) item = modulestore().get_item(tab_location) static_tab = StaticTab( name=item.display_name, url_slug=item.location.name, ) return CourseTabList.get_tab_by_id(tab_list, static_tab.tab_id)
def _create(cls, target_class, **kwargs): """ Uses ``**kwargs``: :parent_location: (required): the location of the parent module (e.g. the parent course or section) :category: the category of the resulting item. :data: (optional): the data for the item (e.g. XML problem definition for a problem item) :display_name: (optional): the display name of the item :metadata: (optional): dictionary of metadata attributes :boilerplate: (optional) the boilerplate for overriding field values :publish_item: (optional) whether or not to publish the item (default is True) :target_class: is ignored """ # 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 # catch any old style users before they get into trouble assert 'template' not in kwargs parent_location = kwargs.pop('parent_location', None) data = kwargs.pop('data', None) category = kwargs.pop('category', None) display_name = kwargs.pop('display_name', None) metadata = kwargs.pop('metadata', {}) location = kwargs.pop('location') user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test) publish_item = kwargs.pop('publish_item', True) assert isinstance(location, UsageKey) assert location != parent_location store = kwargs.pop('modulestore') # This code was based off that in cms/djangoapps/contentstore/views.py parent = kwargs.pop('parent', None) or store.get_item(parent_location) with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): if 'boilerplate' in kwargs: template_id = kwargs.pop('boilerplate') clz = XBlock.load_class(category, select=prefer_xmodules) template = clz.get_template(template_id) assert template is not None metadata.update(template.get('metadata', {})) if not isinstance(data, basestring): data.update(template.get('data')) # replace the display name with an optional parameter passed in from the caller if display_name is not None: metadata['display_name'] = display_name runtime = parent.runtime if parent else None store.create_item( user_id, location.course_key, location.block_type, block_id=location.block_id, metadata=metadata, definition_data=data, runtime=runtime ) module = store.get_item(location) for attr, val in kwargs.items(): setattr(module, attr, val) # Save the attributes we just set module.save() store.update_item(module, user_id) # VS[compat] cdodge: This is a hack because static_tabs also have references from the course module, so # if we add one then we need to also add it to the policy information (i.e. metadata) # we should remove this once we can break this reference from the course to static tabs if category == 'static_tab': course = store.get_course(location.course_key) course.tabs.append( StaticTab( name=display_name, url_slug=location.name, ) ) store.update_item(course, user_id) # parent and publish the item, so it can be accessed if 'detached' not in module._class_tags: parent.children.append(location) store.update_item(parent, user_id) if publish_item: store.publish(parent.location, user_id) elif publish_item: store.publish(location, user_id) # return the published item return store.get_item(location)
def create_xblock(parent_locator, user, category, display_name, boilerplate=None, is_entrance_exam=False): """ Performs the actual grunt work of creating items/xblocks -- knows nothing about requests, views, etc. """ store = modulestore() usage_key = usage_key_with_run(parent_locator) with store.bulk_operations(usage_key.course_key): parent = store.get_item(usage_key) dest_usage_key = usage_key.replace(category=category, name=uuid4().hex) # get the metadata, display_name, and definition from the caller metadata = {} data = None template_id = boilerplate if template_id: clz = parent.runtime.load_block_type(category) if clz is not None: template = clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') if display_name is not None: metadata['display_name'] = display_name # We should use the 'fields' kwarg for newer module settings/values (vs. metadata or data) fields = {} # Entrance Exams: Chapter module positioning child_position = None if ENTRANCE_EXAMS.is_enabled(): if category == 'chapter' and is_entrance_exam: fields['is_entrance_exam'] = is_entrance_exam fields['in_entrance_exam'] = True # Inherited metadata, all children will have it child_position = 0 # TODO need to fix components that are sending definition_data as strings, instead of as dicts # For now, migrate them into dicts here. if isinstance(data, str): data = {'data': data} created_block = store.create_child( user.id, usage_key, dest_usage_key.block_type, block_id=dest_usage_key.block_id, fields=fields, definition_data=data, metadata=metadata, runtime=parent.runtime, position=child_position, ) # Entrance Exams: Grader assignment if ENTRANCE_EXAMS.is_enabled(): course_key = usage_key.course_key course = store.get_course(course_key) if hasattr(course, 'entrance_exam_enabled') and course.entrance_exam_enabled: if category == 'sequential' and parent_locator == course.entrance_exam_id: # Clean up any pre-existing entrance exam graders remove_entrance_exam_graders(course_key, user) grader = { "type": GRADER_TYPES['ENTRANCE_EXAM'], "min_count": 0, "drop_count": 0, "short_label": "Entrance", "weight": 0 } grading_model = CourseGradingModel.update_grader_from_json( course.id, grader, user ) CourseGradingModel.update_section_grader_type( created_block, grading_model['type'], user ) # VS[compat] cdodge: This is a hack because static_tabs also have references from the course module, so # if we add one then we need to also add it to the policy information (i.e. metadata) # we should remove this once we can break this reference from the course to static tabs if category == 'static_tab': display_name = display_name or _("Empty") # Prevent name being None course = store.get_course(dest_usage_key.course_key) course.tabs.append( StaticTab( name=display_name, url_slug=dest_usage_key.block_id, ) ) store.update_item(course, user.id) return created_block