def test_update_section_grader_type(self): # Get the descriptor and the section_grader_type and assert they are the default values descriptor = modulestore().get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) self.assertEqual('notgraded', section_grader_type['graderType']) self.assertEqual(None, descriptor.format) self.assertEqual(False, descriptor.graded) # Change the default grader type to Homework, which should also mark the section as graded CourseGradingModel.update_section_grader_type(self.course, 'Homework', self.user) descriptor = modulestore().get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) self.assertEqual('Homework', section_grader_type['graderType']) self.assertEqual('Homework', descriptor.format) self.assertEqual(True, descriptor.graded) # Change the grader type back to notgraded, which should also unmark the section as graded CourseGradingModel.update_section_grader_type(self.course, 'notgraded', self.user) descriptor = modulestore().get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) self.assertEqual('notgraded', section_grader_type['graderType']) self.assertEqual(None, descriptor.format) self.assertEqual(False, descriptor.graded)
def test_update_section_grader_type(self): # Get the descriptor and the section_grader_type and assert they are the default values descriptor = get_modulestore(self.course.location).get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) self.assertEqual("Not Graded", section_grader_type["graderType"]) self.assertEqual(None, descriptor.lms.format) self.assertEqual(False, descriptor.lms.graded) # Change the default grader type to Homework, which should also mark the section as graded CourseGradingModel.update_section_grader_type(self.course.location, {"graderType": "Homework"}) descriptor = get_modulestore(self.course.location).get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) self.assertEqual("Homework", section_grader_type["graderType"]) self.assertEqual("Homework", descriptor.lms.format) self.assertEqual(True, descriptor.lms.graded) # Change the grader type back to Not Graded, which should also unmark the section as graded CourseGradingModel.update_section_grader_type(self.course.location, {"graderType": "Not Graded"}) descriptor = get_modulestore(self.course.location).get_item(self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type(self.course.location) self.assertEqual("Not Graded", section_grader_type["graderType"]) self.assertEqual(None, descriptor.lms.format) self.assertEqual(False, descriptor.lms.graded)
def test_update_section_grader_type(self): # Get the descriptor and the section_grader_type and assert they are the default values descriptor = get_modulestore(self.course.location).get_item( self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type( self.course_locator) self.assertEqual('notgraded', section_grader_type['graderType']) self.assertEqual(None, descriptor.format) self.assertEqual(False, descriptor.graded) # Change the default grader type to Homework, which should also mark the section as graded CourseGradingModel.update_section_grader_type(self.course, 'Homework', self.user) descriptor = get_modulestore(self.course.location).get_item( self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type( self.course_locator) self.assertEqual('Homework', section_grader_type['graderType']) self.assertEqual('Homework', descriptor.format) self.assertEqual(True, descriptor.graded) # Change the grader type back to notgraded, which should also unmark the section as graded CourseGradingModel.update_section_grader_type(self.course, 'notgraded', self.user) descriptor = get_modulestore(self.course.location).get_item( self.course.location) section_grader_type = CourseGradingModel.get_section_grader_type( self.course_locator) self.assertEqual('notgraded', section_grader_type['graderType']) self.assertEqual(None, descriptor.format) self.assertEqual(False, descriptor.graded)
def test_contentstore_views_entrance_exam_post_new_sequential_confirm_grader( self): """ Unit Test: test_contentstore_views_entrance_exam_post """ resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) # Reload the test course now that the exam module has been added self.course = modulestore().get_course(self.course.id) # Add a new child sequential to the exam module # Confirm that the grader type is 'Entrance Exam' chapter_locator_string = json.loads( resp.content.decode('utf-8')).get('locator') # chapter_locator = UsageKey.from_string(chapter_locator_string) seq_data = { 'category': "sequential", 'display_name': "Entrance Exam Subsection", 'parent_locator': chapter_locator_string, } resp = self.client.ajax_post(reverse_url('xblock_handler'), seq_data) seq_locator_string = json.loads( resp.content.decode('utf-8')).get('locator') seq_locator = UsageKey.from_string(seq_locator_string) section_grader_type = CourseGradingModel.get_section_grader_type( seq_locator) self.assertEqual(GRADER_TYPES['ENTRANCE_EXAM'], section_grader_type['graderType'])
def test_contentstore_views_entrance_exam_post_new_sequential_confirm_grader(self): """ Unit Test: test_contentstore_views_entrance_exam_post """ resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) # Reload the test course now that the exam module has been added self.course = modulestore().get_course(self.course.id) # Add a new child sequential to the exam module # Confirm that the grader type is 'Entrance Exam' chapter_locator_string = json.loads(resp.content).get('locator') # chapter_locator = UsageKey.from_string(chapter_locator_string) seq_data = { 'category': "sequential", 'display_name': "Entrance Exam Subsection", 'parent_locator': chapter_locator_string, } resp = self.client.ajax_post(reverse_url('xblock_handler'), seq_data) seq_locator_string = json.loads(resp.content).get('locator') seq_locator = UsageKey.from_string(seq_locator_string) section_grader_type = CourseGradingModel.get_section_grader_type(seq_locator) self.assertEqual(GRADER_TYPES['ENTRANCE_EXAM'], section_grader_type['graderType'])
def assignment_type_update(request, org, course, category, name): ''' CRUD operations on assignment types for sections and subsections and anything else gradable. ''' location = Location(['i4x', org, course, category, name]) if not has_access(request.user, location): return HttpResponseForbidden() if request.method == 'GET': return JsonResponse(CourseGradingModel.get_section_grader_type(location)) elif request.method in ('POST', 'PUT'): # post or put, doesn't matter. return JsonResponse(CourseGradingModel.update_section_grader_type(location, request.POST))
def assignment_type_update(request, org, course, category, name): ''' CRUD operations on assignment types for sections and subsections and anything else gradable. ''' location = Location(['i4x', org, course, category, name]) if not has_access(request.user, location): return HttpResponseForbidden() if request.method == 'GET': return JsonResponse(CourseGradingModel.get_section_grader_type(location)) elif request.method in ('POST', 'PUT'): # post or put, doesn't matter. return JsonResponse(CourseGradingModel.update_section_grader_type(location, request.POST))
def assignment_type_update(request, org, course, category, name): """ CRUD operations on assignment types for sections and subsections and anything else gradable. """ location = Location(["i4x", org, course, category, name]) if not has_access(request.user, location): return HttpResponseForbidden() if request.method == "GET": rsp = CourseGradingModel.get_section_grader_type(location) elif request.method in ("POST", "PUT"): # post or put, doesn't matter. rsp = CourseGradingModel.update_section_grader_type(location, request.POST) return JsonResponse(rsp)
def xblock_handler(request, usage_key_string): """ The restful handler for xblock requests. DELETE json: delete this xblock instance from the course. GET json: returns representation of the xblock (locator id, data, and metadata). if ?fields=graderType, it returns the graderType for the unit instead of the above. html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view) PUT or POST or PATCH json: if xblock locator is specified, update the xblock instance. The json payload can contain these fields, all optional: :data: the new value for the data. :children: the unicode representation of the UsageKeys of children for this xblock. :metadata: new values for the metadata fields. Any whose values are None will be deleted not set to None! Absent ones will be left alone. :nullout: which metadata fields to set to None :graderType: change how this unit is graded :publish: can be: 'make_public': publish the content 'republish': publish this item *only* if it was previously published 'discard_changes' - reverts to the last published version Note: If 'discard_changes', the other fields will not be used; that is, it is not possible to update and discard changes in a single operation. The JSON representation on the updated xblock (minus children) is returned. if usage_key_string is not specified, create a new xblock instance, either by duplicating an existing xblock, or creating an entirely new one. The json playload can contain these fields: :parent_locator: parent for new xblock, required for both duplicate and create new instance :duplicate_source_locator: if present, use this as the source for creating a duplicate copy :category: type of xblock, required if duplicate_source_locator is not present. :display_name: name for new xblock, optional :boilerplate: template name for populating fields, optional and only used if duplicate_source_locator is not present The locator (unicode representation of a UsageKey) for the created xblock (minus children) is returned. """ if usage_key_string: usage_key = usage_key_with_run(usage_key_string) if not has_course_access(request.user, usage_key.course_key): raise PermissionDenied() if request.method == 'GET': accept_header = request.META.get('HTTP_ACCEPT', 'application/json') if 'application/json' in accept_header: fields = request.REQUEST.get('fields', '').split(',') if 'graderType' in fields: # right now can't combine output of this w/ output of _get_module_info, but worthy goal return JsonResponse(CourseGradingModel.get_section_grader_type(usage_key)) # TODO: pass fields to _get_module_info and only return those rsp = _get_module_info(_get_xblock(usage_key, request.user)) return JsonResponse(rsp) else: return HttpResponse(status=406) elif request.method == 'DELETE': _delete_item(usage_key, request.user) return JsonResponse() else: # Since we have a usage_key, we are updating an existing xblock. return _save_xblock( request.user, _get_xblock(usage_key, request.user), data=request.json.get('data'), children=request.json.get('children'), metadata=request.json.get('metadata'), nullout=request.json.get('nullout'), grader_type=request.json.get('graderType'), publish=request.json.get('publish'), ) elif request.method in ('PUT', 'POST'): if 'duplicate_source_locator' in request.json: parent_usage_key = usage_key_with_run(request.json['parent_locator']) duplicate_source_usage_key = usage_key_with_run(request.json['duplicate_source_locator']) dest_usage_key = _duplicate_item( parent_usage_key, duplicate_source_usage_key, request.user, request.json.get('display_name'), ) return JsonResponse({"locator": unicode(dest_usage_key), "courseKey": unicode(dest_usage_key.course_key)}) else: return _create_item(request) else: return HttpResponseBadRequest( "Only instance creation is supported without a usage key.", content_type="text/plain" )
def xblock_handler(request, usage_key_string): """ The restful handler for xblock requests. DELETE json: delete this xblock instance from the course. GET json: returns representation of the xblock (locator id, data, and metadata). if ?fields=graderType, it returns the graderType for the unit instead of the above. html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view) PUT or POST or PATCH json: if xblock locator is specified, update the xblock instance. The json payload can contain these fields, all optional: :data: the new value for the data. :children: the unicode representation of the UsageKeys of children for this xblock. :metadata: new values for the metadata fields. Any whose values are None will be deleted not set to None! Absent ones will be left alone. :nullout: which metadata fields to set to None :graderType: change how this unit is graded :publish: can be: 'make_public': publish the content 'republish': publish this item *only* if it was previously published 'discard_changes' - reverts to the last published version Note: If 'discard_changes', the other fields will not be used; that is, it is not possible to update and discard changes in a single operation. The JSON representation on the updated xblock (minus children) is returned. if usage_key_string is not specified, create a new xblock instance, either by duplicating an existing xblock, or creating an entirely new one. The json playload can contain these fields: :parent_locator: parent for new xblock, required for both duplicate and create new instance :duplicate_source_locator: if present, use this as the source for creating a duplicate copy :category: type of xblock, required if duplicate_source_locator is not present. :display_name: name for new xblock, optional :boilerplate: template name for populating fields, optional and only used if duplicate_source_locator is not present The locator (unicode representation of a UsageKey) for the created xblock (minus children) is returned. """ if usage_key_string: usage_key = usage_key_with_run(usage_key_string) if not has_course_author_access(request.user, usage_key.course_key): raise PermissionDenied() if request.method == 'GET': accept_header = request.META.get('HTTP_ACCEPT', 'application/json') if 'application/json' in accept_header: fields = request.REQUEST.get('fields', '').split(',') if 'graderType' in fields: # right now can't combine output of this w/ output of _get_module_info, but worthy goal return JsonResponse( CourseGradingModel.get_section_grader_type(usage_key)) # TODO: pass fields to _get_module_info and only return those rsp = _get_module_info(_get_xblock(usage_key, request.user)) return JsonResponse(rsp) else: return HttpResponse(status=406) elif request.method == 'DELETE': _delete_item(usage_key, request.user) return JsonResponse() else: # Since we have a usage_key, we are updating an existing xblock. return _save_xblock( request.user, _get_xblock(usage_key, request.user), data=request.json.get('data'), children_strings=request.json.get('children'), metadata=request.json.get('metadata'), nullout=request.json.get('nullout'), grader_type=request.json.get('graderType'), publish=request.json.get('publish'), ) elif request.method in ('PUT', 'POST'): if 'duplicate_source_locator' in request.json: parent_usage_key = usage_key_with_run( request.json['parent_locator']) duplicate_source_usage_key = usage_key_with_run( request.json['duplicate_source_locator']) dest_usage_key = _duplicate_item( parent_usage_key, duplicate_source_usage_key, request.user, request.json.get('display_name'), ) return JsonResponse({ "locator": unicode(dest_usage_key), "courseKey": unicode(dest_usage_key.course_key) }) else: return _create_item(request) else: return HttpResponseBadRequest( "Only instance creation is supported without a usage key.", content_type="text/plain")
def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None): """ The restful handler for xblock requests. DELETE json: delete this xblock instance from the course. Supports query parameters "recurse" to delete all children and "all_versions" to delete from all (mongo) versions. GET json: returns representation of the xblock (locator id, data, and metadata). if ?fields=graderType, it returns the graderType for the unit instead of the above. html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view) PUT or POST json: if xblock locator is specified, update the xblock instance. The json payload can contain these fields, all optional: :data: the new value for the data. :children: the locator ids of children for this xblock. :metadata: new values for the metadata fields. Any whose values are None will be deleted not set to None! Absent ones will be left alone. :nullout: which metadata fields to set to None :graderType: change how this unit is graded :publish: can be one of three values, 'make_public, 'make_private', or 'create_draft' The JSON representation on the updated xblock (minus children) is returned. if xblock locator is not specified, create a new xblock instance, either by duplicating an existing xblock, or creating an entirely new one. The json playload can contain these fields: :parent_locator: parent for new xblock, required for both duplicate and create new instance :duplicate_source_locator: if present, use this as the source for creating a duplicate copy :category: type of xblock, required if duplicate_source_locator is not present. :display_name: name for new xblock, optional :boilerplate: template name for populating fields, optional and only used if duplicate_source_locator is not present The locator (and old-style id) for the created xblock (minus children) is returned. """ if package_id is not None: locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block) if not has_course_access(request.user, locator): raise PermissionDenied() old_location = loc_mapper().translate_locator_to_location(locator) if request.method == 'GET': accept_header = request.META.get('HTTP_ACCEPT', 'application/json') if 'application/json' in accept_header: fields = request.REQUEST.get('fields', '').split(',') if 'graderType' in fields: # right now can't combine output of this w/ output of _get_module_info, but worthy goal return JsonResponse( CourseGradingModel.get_section_grader_type(locator)) # TODO: pass fields to _get_module_info and only return those rsp = _get_module_info(locator) return JsonResponse(rsp) else: return HttpResponse(status=406) elif request.method == 'DELETE': delete_children = str_to_bool( request.REQUEST.get('recurse', 'False')) delete_all_versions = str_to_bool( request.REQUEST.get('all_versions', 'False')) # delete item from loc_mapper and cache loc_mapper().delete_item_mapping(locator, old_location) return _delete_item_at_location(old_location, delete_children, delete_all_versions, request.user) else: # Since we have a package_id, we are updating an existing xblock. if block == 'handouts' and old_location is None: # update handouts location in loc_mapper course_location = loc_mapper().translate_locator_to_location( locator, get_course=True) old_location = course_location.replace(category='course_info', name=block) locator = loc_mapper().translate_location( course_location.course_id, old_location) return _save_item( request, locator, old_location, data=request.json.get('data'), children=request.json.get('children'), metadata=request.json.get('metadata'), nullout=request.json.get('nullout'), grader_type=request.json.get('graderType'), publish=request.json.get('publish'), ) elif request.method in ('PUT', 'POST'): if 'duplicate_source_locator' in request.json: parent_locator = BlockUsageLocator(request.json['parent_locator']) duplicate_source_locator = BlockUsageLocator( request.json['duplicate_source_locator']) # _duplicate_item is dealing with locations to facilitate the recursive call for # duplicating children. parent_location = loc_mapper().translate_locator_to_location( parent_locator) duplicate_source_location = loc_mapper( ).translate_locator_to_location(duplicate_source_locator) dest_location = _duplicate_item( parent_location, duplicate_source_location, request.json.get('display_name'), request.user, ) course_location = loc_mapper().translate_locator_to_location( BlockUsageLocator(parent_locator), get_course=True) dest_locator = loc_mapper().translate_location( course_location.course_id, dest_location, False, True) return JsonResponse({"locator": unicode(dest_locator)}) else: return _create_item(request) else: return HttpResponseBadRequest( "Only instance creation is supported without a package_id.", content_type="text/plain")
def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None): """ The restful handler for xblock requests. DELETE json: delete this xblock instance from the course. Supports query parameters "recurse" to delete all children and "all_versions" to delete from all (mongo) versions. GET json: returns representation of the xblock (locator id, data, and metadata). if ?fields=graderType, it returns the graderType for the unit instead of the above. html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view) PUT or POST json: if xblock locator is specified, update the xblock instance. The json payload can contain these fields, all optional: :data: the new value for the data. :children: the locator ids of children for this xblock. :metadata: new values for the metadata fields. Any whose values are None will be deleted not set to None! Absent ones will be left alone. :nullout: which metadata fields to set to None :graderType: change how this unit is graded :publish: can be one of three values, 'make_public, 'make_private', or 'create_draft' The JSON representation on the updated xblock (minus children) is returned. if xblock locator is not specified, create a new xblock instance, either by duplicating an existing xblock, or creating an entirely new one. The json playload can contain these fields: :parent_locator: parent for new xblock, required for both duplicate and create new instance :duplicate_source_locator: if present, use this as the source for creating a duplicate copy :category: type of xblock, required if duplicate_source_locator is not present. :display_name: name for new xblock, optional :boilerplate: template name for populating fields, optional and only used if duplicate_source_locator is not present The locator (and old-style id) for the created xblock (minus children) is returned. """ if package_id is not None: locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block) if not has_course_access(request.user, locator): raise PermissionDenied() old_location = loc_mapper().translate_locator_to_location(locator) if request.method == 'GET': accept_header = request.META.get('HTTP_ACCEPT', 'application/json') if 'application/json' in accept_header: fields = request.REQUEST.get('fields', '').split(',') if 'graderType' in fields: # right now can't combine output of this w/ output of _get_module_info, but worthy goal return JsonResponse(CourseGradingModel.get_section_grader_type(locator)) # TODO: pass fields to _get_module_info and only return those rsp = _get_module_info(locator) return JsonResponse(rsp) else: return HttpResponse(status=406) elif request.method == 'DELETE': delete_children = str_to_bool(request.REQUEST.get('recurse', 'False')) delete_all_versions = str_to_bool(request.REQUEST.get('all_versions', 'False')) return _delete_item_at_location(old_location, delete_children, delete_all_versions, request.user) else: # Since we have a package_id, we are updating an existing xblock. if block == 'handouts' and old_location is None: # update handouts location in loc_mapper course_location = loc_mapper().translate_locator_to_location(locator, get_course=True) old_location = course_location.replace(category='course_info', name=block) locator = loc_mapper().translate_location(course_location.course_id, old_location) return _save_item( request, locator, old_location, data=request.json.get('data'), children=request.json.get('children'), metadata=request.json.get('metadata'), nullout=request.json.get('nullout'), grader_type=request.json.get('graderType'), publish=request.json.get('publish'), ) elif request.method in ('PUT', 'POST'): if 'duplicate_source_locator' in request.json: parent_locator = BlockUsageLocator(request.json['parent_locator']) duplicate_source_locator = BlockUsageLocator(request.json['duplicate_source_locator']) # _duplicate_item is dealing with locations to facilitate the recursive call for # duplicating children. parent_location = loc_mapper().translate_locator_to_location(parent_locator) duplicate_source_location = loc_mapper().translate_locator_to_location(duplicate_source_locator) dest_location = _duplicate_item( parent_location, duplicate_source_location, request.json.get('display_name'), request.user, ) course_location = loc_mapper().translate_locator_to_location(BlockUsageLocator(parent_locator), get_course=True) dest_locator = loc_mapper().translate_location(course_location.course_id, dest_location, False, True) return JsonResponse({"locator": unicode(dest_locator)}) else: return _create_item(request) else: return HttpResponseBadRequest( "Only instance creation is supported without a package_id.", content_type="text/plain" )
def xblock_handler(request, tag=None, course_id=None, branch=None, version_guid=None, block=None): """ The restful handler for xblock requests. DELETE json: delete this xblock instance from the course. Supports query parameters "recurse" to delete all children and "all_versions" to delete from all (mongo) versions. GET json: returns representation of the xblock (locator id, data, and metadata). if ?fields=graderType, it returns the graderType for the unit instead of the above. html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view) PUT or POST json: if xblock locator is specified, update the xblock instance. The json payload can contain these fields, all optional: :data: the new value for the data. :children: the locator ids of children for this xblock. :metadata: new values for the metadata fields. Any whose values are None will be deleted not set to None! Absent ones will be left alone. :nullout: which metadata fields to set to None :graderType: change how this unit is graded :publish: can be one of three values, 'make_public, 'make_private', or 'create_draft' The JSON representation on the updated xblock (minus children) is returned. if xblock locator is not specified, create a new xblock instance. The json playload can contain these fields: :parent_locator: parent for new xblock, required :category: type of xblock, required :display_name: name for new xblock, optional :boilerplate: template name for populating fields, optional The locator (and old-style id) for the created xblock (minus children) is returned. """ if course_id is not None: locator = BlockUsageLocator(course_id=course_id, branch=branch, version_guid=version_guid, usage_id=block) if not has_access(request.user, locator): raise PermissionDenied() old_location = loc_mapper().translate_locator_to_location(locator) if request.method == 'GET': if 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'): fields = request.REQUEST.get('fields', '').split(',') if 'graderType' in fields: # right now can't combine output of this w/ output of _get_module_info, but worthy goal return JsonResponse(CourseGradingModel.get_section_grader_type(locator)) # TODO: pass fields to _get_module_info and only return those rsp = _get_module_info(locator) return JsonResponse(rsp) else: component = modulestore().get_item(old_location) # Wrap the generated fragment in the xmodule_editor div so that the javascript # can bind to it correctly component.runtime.wrappers.append(partial(wrap_xblock, handler_prefix)) try: content = component.render('studio_view').content # catch exceptions indiscriminately, since after this point they escape the # dungeon and surface as uneditable, unsaveable, and undeletable # component-goblins. except Exception as exc: # pylint: disable=W0703 log.debug("Unable to render studio_view for %r", component, exc_info=True) content = render_to_string('html_error.html', {'message': str(exc)}) return render_to_response('component.html', { 'preview': get_preview_html(request, component), 'editor': content }) elif request.method == 'DELETE': delete_children = str_to_bool(request.REQUEST.get('recurse', 'False')) delete_all_versions = str_to_bool(request.REQUEST.get('all_versions', 'False')) return _delete_item_at_location(old_location, delete_children, delete_all_versions) else: # Since we have a course_id, we are updating an existing xblock. return _save_item( request, locator, old_location, data=request.json.get('data'), children=request.json.get('children'), metadata=request.json.get('metadata'), nullout=request.json.get('nullout'), grader_type=request.json.get('graderType'), publish=request.json.get('publish'), ) elif request.method in ('PUT', 'POST'): return _create_item(request) else: return HttpResponseBadRequest( "Only instance creation is supported without a course_id.", content_type="text/plain" )
def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None): """ The restful handler for xblock requests. DELETE json: delete this xblock instance from the course. Supports query parameters "recurse" to delete all children and "all_versions" to delete from all (mongo) versions. GET json: returns representation of the xblock (locator id, data, and metadata). if ?fields=graderType, it returns the graderType for the unit instead of the above. html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view) PUT or POST json: if xblock locator is specified, update the xblock instance. The json payload can contain these fields, all optional: :data: the new value for the data. :children: the locator ids of children for this xblock. :metadata: new values for the metadata fields. Any whose values are None will be deleted not set to None! Absent ones will be left alone. :nullout: which metadata fields to set to None :graderType: change how this unit is graded :publish: can be one of three values, 'make_public, 'make_private', or 'create_draft' The JSON representation on the updated xblock (minus children) is returned. if xblock locator is not specified, create a new xblock instance, either by duplicating an existing xblock, or creating an entirely new one. The json playload can contain these fields: :parent_locator: parent for new xblock, required for both duplicate and create new instance :duplicate_source_locator: if present, use this as the source for creating a duplicate copy :category: type of xblock, required if duplicate_source_locator is not present. :display_name: name for new xblock, optional :boilerplate: template name for populating fields, optional and only used if duplicate_source_locator is not present The locator (and old-style id) for the created xblock (minus children) is returned. """ if package_id is not None: locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block) if not has_course_access(request.user, locator): raise PermissionDenied() old_location = loc_mapper().translate_locator_to_location(locator) if request.method == 'GET': if 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'): fields = request.REQUEST.get('fields', '').split(',') if 'graderType' in fields: # right now can't combine output of this w/ output of _get_module_info, but worthy goal return JsonResponse(CourseGradingModel.get_section_grader_type(locator)) # TODO: pass fields to _get_module_info and only return those rsp = _get_module_info(locator) return JsonResponse(rsp) else: component = modulestore().get_item(old_location) # Wrap the generated fragment in the xmodule_editor div so that the javascript # can bind to it correctly component.runtime.wrappers.append(partial(wrap_xblock, handler_prefix)) try: content = component.render('studio_view').content # catch exceptions indiscriminately, since after this point they escape the # dungeon and surface as uneditable, unsaveable, and undeletable # component-goblins. except Exception as exc: # pylint: disable=W0703 log.debug("Unable to render studio_view for %r", component, exc_info=True) content = render_to_string('html_error.html', {'message': str(exc)}) return render_to_response('component.html', { 'preview': get_preview_html(request, component), 'editor': content }) elif request.method == 'DELETE': delete_children = str_to_bool(request.REQUEST.get('recurse', 'False')) delete_all_versions = str_to_bool(request.REQUEST.get('all_versions', 'False')) return _delete_item_at_location(old_location, delete_children, delete_all_versions) else: # Since we have a package_id, we are updating an existing xblock. return _save_item( request, locator, old_location, data=request.json.get('data'), children=request.json.get('children'), metadata=request.json.get('metadata'), nullout=request.json.get('nullout'), grader_type=request.json.get('graderType'), publish=request.json.get('publish'), ) elif request.method in ('PUT', 'POST'): if 'duplicate_source_locator' in request.json: parent_locator = BlockUsageLocator(request.json['parent_locator']) duplicate_source_locator = BlockUsageLocator(request.json['duplicate_source_locator']) # _duplicate_item is dealing with locations to facilitate the recursive call for # duplicating children. parent_location = loc_mapper().translate_locator_to_location(parent_locator) duplicate_source_location = loc_mapper().translate_locator_to_location(duplicate_source_locator) dest_location = _duplicate_item( parent_location, duplicate_source_location, request.json.get('display_name') ) course_location = loc_mapper().translate_locator_to_location(BlockUsageLocator(parent_locator), get_course=True) dest_locator = loc_mapper().translate_location(course_location.course_id, dest_location, False, True) return JsonResponse({"locator": unicode(dest_locator)}) else: return _create_item(request) else: return HttpResponseBadRequest( "Only instance creation is supported without a package_id.", content_type="text/plain" )
def xblock_handler(request, usage_key_string): """ The restful handler for xblock requests. DELETE json: delete this xblock instance from the course. GET json: returns representation of the xblock (locator id, data, and metadata). if ?fields=graderType, it returns the graderType for the unit instead of the above. html: returns HTML for rendering the xblock (which includes both the "preview" view and the "editor" view) PUT or POST json: if xblock locator is specified, update the xblock instance. The json payload can contain these fields, all optional: :data: the new value for the data. :children: the unicode representation of the UsageKeys of children for this xblock. :metadata: new values for the metadata fields. Any whose values are None will be deleted not set to None! Absent ones will be left alone. :nullout: which metadata fields to set to None :graderType: change how this unit is graded :publish: can be one of three values, 'make_public, 'make_private', or 'create_draft' The JSON representation on the updated xblock (minus children) is returned. if usage_key_string is not specified, create a new xblock instance, either by duplicating an existing xblock, or creating an entirely new one. The json playload can contain these fields: :parent_locator: parent for new xblock, required for both duplicate and create new instance :duplicate_source_locator: if present, use this as the source for creating a duplicate copy :category: type of xblock, required if duplicate_source_locator is not present. :display_name: name for new xblock, optional :boilerplate: template name for populating fields, optional and only used if duplicate_source_locator is not present The locator (unicode representation of a UsageKey) for the created xblock (minus children) is returned. """ if usage_key_string: usage_key = UsageKey.from_string(usage_key_string) if not has_course_access(request.user, usage_key.course_key): raise PermissionDenied() if request.method == "GET": accept_header = request.META.get("HTTP_ACCEPT", "application/json") if "application/json" in accept_header: fields = request.REQUEST.get("fields", "").split(",") if "graderType" in fields: # right now can't combine output of this w/ output of _get_module_info, but worthy goal return JsonResponse(CourseGradingModel.get_section_grader_type(usage_key)) # TODO: pass fields to _get_module_info and only return those rsp = _get_module_info(usage_key, request.user) return JsonResponse(rsp) else: return HttpResponse(status=406) elif request.method == "DELETE": _delete_item(usage_key, request.user) return JsonResponse() else: # Since we have a usage_key, we are updating an existing xblock. return _save_item( request.user, usage_key, data=request.json.get("data"), children=request.json.get("children"), metadata=request.json.get("metadata"), nullout=request.json.get("nullout"), grader_type=request.json.get("graderType"), publish=request.json.get("publish"), ) elif request.method in ("PUT", "POST"): if "duplicate_source_locator" in request.json: parent_usage_key = UsageKey.from_string(request.json["parent_locator"]) duplicate_source_usage_key = UsageKey.from_string(request.json["duplicate_source_locator"]) dest_usage_key = _duplicate_item( parent_usage_key, duplicate_source_usage_key, request.json.get("display_name"), request.user ) return JsonResponse({"locator": unicode(dest_usage_key)}) else: return _create_item(request) else: return HttpResponseBadRequest( "Only instance creation is supported without a usage key.", content_type="text/plain" )