Example #1
0
    def test_ensure_fully_via_keyword(self):
        # arg list inits
        raise SkipTest()
        testobj = BlockUsageLocator(version_guid='versionid')
        self.assertRaises(InsufficientSpecificationError,
                          BlockUsageLocator.ensure_fully_specified, testobj)

        testurn = 'crx/courseid@revision/blockid'
        testobj = BlockUsageLocator(version_guid='versionid',
                                    usage_id='myblock')
        self.assertIsInstance(
            BlockUsageLocator.ensure_fully_specified(testurn),
            BlockUsageLocator, testurn)

        testobj = BlockUsageLocator(course_id='courseid')
        self.assertRaises(InsufficientSpecificationError,
                          BlockUsageLocator.ensure_fully_specified, testobj)

        testobj = BlockUsageLocator(course_id='courseid', revision='rev')
        self.assertRaises(InsufficientSpecificationError,
                          BlockUsageLocator.ensure_fully_specified, testobj)

        testobj = BlockUsageLocator(course_id='courseid',
                                    revision='rev',
                                    usage_id='this_block')
        self.assertIsInstance(
            BlockUsageLocator.ensure_fully_specified(testurn),
            BlockUsageLocator, testurn)
Example #2
0
    def test_post_course_update(self):
        """
        Test that a user can successfully post on course updates and handouts of a course
        whose location in not in loc_mapper
        """
        # create a course via the view handler
        course_location = Location(['i4x', 'Org_1', 'Course_1', 'course', 'Run_1'])
        course_locator = loc_mapper().translate_location(
            course_location.course_id, course_location, False, True
        )
        self.client.ajax_post(
            course_locator.url_reverse('course'),
            {
                'org': course_location.org,
                'number': course_location.course,
                'display_name': 'test course',
                'run': course_location.name,
            }
        )

        branch = u'draft'
        version = None
        block = u'updates'
        updates_locator = BlockUsageLocator(
            package_id=course_location.course_id.replace('/', '.'), branch=branch, version_guid=version, block_id=block
        )

        content = u"Sample update"
        payload = {'content': content, 'date': 'January 8, 2013'}
        course_update_url = updates_locator.url_reverse('course_info_update')
        resp = self.client.ajax_post(course_update_url, payload)

        # check that response status is 200 not 400
        self.assertEqual(resp.status_code, 200)

        payload = json.loads(resp.content)
        self.assertHTMLEqual(payload['content'], content)

        # now test that calling translate_location returns a locator whose block_id is 'updates'
        updates_location = course_location.replace(category='course_info', name=block)
        updates_locator = loc_mapper().translate_location(course_location.course_id, updates_location)
        self.assertTrue(isinstance(updates_locator, BlockUsageLocator))
        self.assertEqual(updates_locator.block_id, block)

        # check posting on handouts
        block = u'handouts'
        handouts_locator = BlockUsageLocator(
            package_id=updates_locator.package_id, branch=updates_locator.branch, version_guid=version, block_id=block
        )
        course_handouts_url = handouts_locator.url_reverse('xblock')
        content = u"Sample handout"
        payload = {"data": content}
        resp = self.client.ajax_post(course_handouts_url, payload)

        # check that response status is 200 not 500
        self.assertEqual(resp.status_code, 200)

        payload = json.loads(resp.content)
        self.assertHTMLEqual(payload['data'], content)
    def xblock_from_json(self,
                         class_,
                         usage_id,
                         json_data,
                         course_entry_override=None):
        if course_entry_override is None:
            course_entry_override = self.course_entry
        # most likely a lazy loader or the id directly
        definition = json_data.get('definition', {})
        definition_id = self.modulestore.definition_locator(definition)

        # If no usage id is provided, generate an in-memory id
        if usage_id is None:
            usage_id = LocalId()

        block_locator = BlockUsageLocator(
            version_guid=course_entry_override['_id'],
            usage_id=usage_id,
            course_id=course_entry_override.get('course_id'),
            branch=course_entry_override.get('branch'))

        kvs = SplitMongoKVS(
            definition,
            json_data.get('fields', {}),
            json_data.get('_inherited_settings'),
        )
        field_data = DbModel(kvs)

        try:
            module = self.construct_xblock_from_class(
                class_, field_data,
                ScopeIds(None, json_data.get('category'), definition_id,
                         block_locator))
        except Exception:
            log.warning("Failed to load descriptor", exc_info=True)
            return ErrorDescriptor.from_json(
                json_data,
                self,
                BlockUsageLocator(version_guid=course_entry_override['_id'],
                                  usage_id=usage_id),
                error_msg=exc_info_to_str(sys.exc_info()))

        edit_info = json_data.get('edit_info', {})
        module.edited_by = edit_info.get('edited_by')
        module.edited_on = edit_info.get('edited_on')
        module.previous_version = edit_info.get('previous_version')
        module.update_version = edit_info.get('update_version')
        module.definition_locator = self.modulestore.definition_locator(
            definition)
        # decache any pending field settings
        module.save()

        # If this is an in-memory block, store it in this system
        if isinstance(block_locator.usage_id, LocalId):
            self.local_modules[block_locator] = module

        return module
Example #4
0
 def test_split_orphan(self):
     """
     Test that old mongo finds the orphans
     """
     orphans = self.split_mongo.get_orphans(self.split_course_id, ['static_tab', 'about', 'course_info'], 'draft')
     self.assertEqual(len(orphans), 3, "Wrong # {}".format(orphans))
     location = BlockUsageLocator(course_id=self.split_course_id, branch='draft', usage_id='OrphanChapter')
     self.assertIn(location, orphans)
     location = BlockUsageLocator(course_id=self.split_course_id, branch='draft', usage_id='OrphanVert')
     self.assertIn(location, orphans)
     location = BlockUsageLocator(course_id=self.split_course_id, branch='draft', usage_id='OrphanHtml')
     self.assertIn(location, orphans)
Example #5
0
 def test_update_block(self):
     """
     test update_block_location_translator(location, usage_id, old_course_id=None)
     """
     org = 'foo_org'
     course = 'bar_course'
     new_style_course_id = '{}.geek_dept.{}.baz_run'.format(org, course)
     loc_mapper().create_map_entry(
         Location('i4x', org, course, 'course', 'baz_run'),
         new_style_course_id,
         block_map={
             'abc123': {'problem': 'problem2'},
             '48f23a10395384929234': {'chapter': 'chapter48f'},
             '1': {'chapter': 'chapter1', 'problem': 'problem1'},
         }
     )
     new_style_course_id2 = '{}.geek_dept.{}.delta_run'.format(org, course)
     loc_mapper().create_map_entry(
         Location('i4x', org, course, 'course', 'delta_run'),
         new_style_course_id2,
         block_map={
             'abc123': {'problem': 'problem3'},
             '48f23a10395384929234': {'chapter': 'chapter48b'},
             '1': {'chapter': 'chapter2', 'problem': 'problem2'},
         }
     )
     location = Location('i4x', org, course, 'problem', '1')
     # change in all courses to same value
     loc_mapper().update_block_location_translator(location, 'problem1')
     trans_loc = loc_mapper().translate_locator_to_location(
         BlockUsageLocator(course_id=new_style_course_id, usage_id='problem1', branch='published')
     )
     self.assertEqual(trans_loc, location)
     trans_loc = loc_mapper().translate_locator_to_location(
         BlockUsageLocator(course_id=new_style_course_id2, usage_id='problem1', branch='published')
     )
     self.assertEqual(trans_loc, location)
     # try to change to overwrite used usage_id
     location = Location('i4x', org, course, 'chapter', '48f23a10395384929234')
     with self.assertRaises(DuplicateItemError):
         loc_mapper().update_block_location_translator(location, 'chapter2')
     # just change the one course
     loc_mapper().update_block_location_translator(location, 'chapter2', '{}/{}/{}'.format(org, course, 'baz_run'))
     trans_loc = loc_mapper().translate_locator_to_location(
         BlockUsageLocator(course_id=new_style_course_id, usage_id='chapter2', branch='published')
     )
     self.assertEqual(trans_loc.name, '48f23a10395384929234')
     # but this still points to the old
     trans_loc = loc_mapper().translate_locator_to_location(
         BlockUsageLocator(course_id=new_style_course_id2, usage_id='chapter2', branch='published')
     )
     self.assertEqual(trans_loc.name, '1')
Example #6
0
    def xblock_from_json(self,
                         class_,
                         usage_id,
                         json_data,
                         course_entry_override=None):
        if course_entry_override is None:
            course_entry_override = self.course_entry
        # most likely a lazy loader or the id directly
        definition = json_data.get('definition', {})

        block_locator = BlockUsageLocator(
            version_guid=course_entry_override['_id'],
            usage_id=usage_id,
            course_id=course_entry_override.get('course_id'),
            branch=course_entry_override.get('branch'))

        kvs = SplitMongoKVS(definition, json_data.get('fields', {}),
                            json_data.get('_inherited_settings'),
                            block_locator, json_data.get('category'))
        model_data = DbModel(
            kvs,
            class_,
            None,
            SplitMongoKVSid(
                # DbModel req's that these support .url()
                block_locator,
                self.modulestore.definition_locator(definition)))

        try:
            module = class_(self, model_data)
        except Exception:
            log.warning("Failed to load descriptor", exc_info=True)
            if usage_id is None:
                usage_id = "MISSING"
            return ErrorDescriptor.from_json(
                json_data,
                self,
                BlockUsageLocator(version_guid=course_entry_override['_id'],
                                  usage_id=usage_id),
                error_msg=exc_info_to_str(sys.exc_info()))

        edit_info = json_data.get('edit_info', {})
        module.edited_by = edit_info.get('edited_by')
        module.edited_on = edit_info.get('edited_on')
        module.previous_version = edit_info.get('previous_version')
        module.update_version = edit_info.get('update_version')
        module.definition_locator = self.modulestore.definition_locator(
            definition)
        # decache any pending field settings
        module.save()
        return module
Example #7
0
 def test_inheritance(self):
     """
     The actual test
     """
     # Note, not testing value where defined (course) b/c there's no
     # defined accessor for it on CourseDescriptor.
     locator = BlockUsageLocator(course_id="GreekHero", usage_id="problem3_2", branch='draft')
     node = modulestore().get_item(locator)
     # inherited
     self.assertEqual(node.graceperiod, datetime.timedelta(hours=2))
     locator = BlockUsageLocator(course_id="GreekHero", usage_id="problem1", branch='draft')
     node = modulestore().get_item(locator)
     # overridden
     self.assertEqual(node.graceperiod, datetime.timedelta(hours=4))
Example #8
0
 def test_delete_block(self):
     """
     test delete_block_location_translator(location, old_course_id=None)
     """
     org = 'foo_org'
     course = 'bar_course'
     new_style_course_id = '{}.geek_dept.{}.baz_run'.format(org, course)
     loc_mapper().create_map_entry(
         Location('i4x', org, course, 'course', 'baz_run'),
         new_style_course_id,
         block_map={
             'abc123': {'problem': 'problem2'},
             '48f23a10395384929234': {'chapter': 'chapter48f'},
             '1': {'chapter': 'chapter1', 'problem': 'problem1'},
         }
     )
     new_style_course_id2 = '{}.geek_dept.{}.delta_run'.format(org, course)
     loc_mapper().create_map_entry(
         Location('i4x', org, course, 'course', 'delta_run'),
         new_style_course_id2,
         block_map={
             'abc123': {'problem': 'problem3'},
             '48f23a10395384929234': {'chapter': 'chapter48b'},
             '1': {'chapter': 'chapter2', 'problem': 'problem2'},
         }
     )
     location = Location('i4x', org, course, 'problem', '1')
     # delete from all courses
     loc_mapper().delete_block_location_translator(location)
     self.assertIsNone(loc_mapper().translate_locator_to_location(
         BlockUsageLocator(course_id=new_style_course_id, usage_id='problem1', branch='published')
     ))
     self.assertIsNone(loc_mapper().translate_locator_to_location(
         BlockUsageLocator(course_id=new_style_course_id2, usage_id='problem2', branch='published')
     ))
     # delete from one course
     location = location.replace(name='abc123')
     loc_mapper().delete_block_location_translator(location, '{}/{}/{}'.format(org, course, 'baz_run'))
     with self.assertRaises(ItemNotFoundError):
         loc_mapper().translate_location(
             '{}/{}/{}'.format(org, course, 'baz_run'),
             location,
             add_entry_if_missing=False
         )
     locator = loc_mapper().translate_location(
         '{}/{}/{}'.format(org, course, 'delta_run'),
         location,
         add_entry_if_missing=False
     )
     self.assertEqual(locator.usage_id, 'problem3')
Example #9
0
    def test_negative_has_item(self):
        # negative tests--not found
        # no such course or block
        course_id = 'GreekHero'
        locator = BlockUsageLocator(course_id="doesnotexist", usage_id="head23456", branch='draft')
        self.assertFalse(modulestore().has_item(course_id, locator))
        locator = BlockUsageLocator(course_id="wonderful", usage_id="doesnotexist", branch='draft')
        self.assertFalse(modulestore().has_item(course_id, locator))

        # negative tests--insufficient specification
        self.assertRaises(InsufficientSpecificationError, BlockUsageLocator)
        self.assertRaises(InsufficientSpecificationError,
                          modulestore().has_item, None, BlockUsageLocator(version_guid=self.GUID_D1))
        self.assertRaises(InsufficientSpecificationError,
                          modulestore().has_item, None, BlockUsageLocator(course_id='GreekHero'))
Example #10
0
 def check_subtree(node):
     if node:
         node_loc = node.location
         self.assertFalse(modulestore().has_item(reusable_location.course_id,
             BlockUsageLocator(
                 course_id=node_loc.course_id,
                 branch=node_loc.branch,
                 usage_id=node.location.usage_id)))
         locator = BlockUsageLocator(
             version_guid=node.location.version_guid,
             usage_id=node.location.usage_id)
         self.assertTrue(modulestore().has_item(reusable_location.course_id, locator))
         if node.has_children:
             for sub in node.get_children():
                 check_subtree(sub)
Example #11
0
    def test_delete_item(self):
        course = self.create_course_for_deletion()
        self.assertRaises(ValueError,
                          modulestore().delete_item,
                          course.location,
                          'deleting_user')
        reusable_location = BlockUsageLocator(
            course_id=course.location.course_id,
            usage_id=course.location.usage_id,
            branch='draft')

        # delete a leaf
        problems = modulestore().get_items(reusable_location, {'category': 'problem'})
        locn_to_del = problems[0].location
        new_course_loc = modulestore().delete_item(locn_to_del, 'deleting_user', delete_children=True)
        deleted = BlockUsageLocator(course_id=reusable_location.course_id,
                                    branch=reusable_location.branch,
                                    usage_id=locn_to_del.usage_id)
        self.assertFalse(modulestore().has_item(reusable_location.course_id, deleted))
        self.assertRaises(VersionConflictError, modulestore().has_item, reusable_location.course_id, locn_to_del)
        locator = BlockUsageLocator(
            version_guid=locn_to_del.version_guid,
            usage_id=locn_to_del.usage_id
        )
        self.assertTrue(modulestore().has_item(reusable_location.course_id, locator))
        self.assertNotEqual(new_course_loc.version_guid, course.location.version_guid)

        # delete a subtree
        nodes = modulestore().get_items(reusable_location, {'category': 'chapter'})
        new_course_loc = modulestore().delete_item(nodes[0].location, 'deleting_user', delete_children=True)
        # check subtree

        def check_subtree(node):
            if node:
                node_loc = node.location
                self.assertFalse(modulestore().has_item(reusable_location.course_id,
                    BlockUsageLocator(
                        course_id=node_loc.course_id,
                        branch=node_loc.branch,
                        usage_id=node.location.usage_id)))
                locator = BlockUsageLocator(
                    version_guid=node.location.version_guid,
                    usage_id=node.location.usage_id)
                self.assertTrue(modulestore().has_item(reusable_location.course_id, locator))
                if node.has_children:
                    for sub in node.get_children():
                        check_subtree(sub)
        check_subtree(nodes[0])
def import_status_handler(request,
                          tag=None,
                          package_id=None,
                          branch=None,
                          version_guid=None,
                          block=None,
                          filename=None):
    """
    Returns an integer corresponding to the status of a file import. These are:

        0 : No status info found (import done or upload still in progress)
        1 : Extracting file
        2 : Validating.
        3 : Importing to mongo

    """
    location = BlockUsageLocator(package_id=package_id,
                                 branch=branch,
                                 version_guid=version_guid,
                                 block_id=block)
    if not has_course_access(request.user, location):
        raise PermissionDenied()

    try:
        session_status = request.session["import_status"]
        status = session_status[location.package_id + filename]
    except KeyError:
        status = 0

    return JsonResponse({"ImportStatus": status})
Example #13
0
def course_index(request, course_id, branch, version_guid, block):
    """
    Display an editable course overview.

    org, course, name: Attributes of the Location for the item to edit
    """
    location = BlockUsageLocator(course_id=course_id, branch=branch, version_guid=version_guid, usage_id=block)
    # TODO: when converting to split backend, if location does not have a usage_id,
    # we'll need to get the course's root block_id
    if not has_access(request.user, location):
        raise PermissionDenied()


    old_location = loc_mapper().translate_locator_to_location(location)

    lms_link = get_lms_link_for_item(old_location)

    course = modulestore().get_item(old_location, depth=3)
    sections = course.get_children()

    return render_to_response('overview.html', {
        'context_course': course,
        'lms_link': lms_link,
        'sections': sections,
        'course_graders': json.dumps(
            CourseGradingModel.fetch(course.location).graders
        ),
        'parent_location': course.location,
        'new_section_category': 'chapter',
        'new_subsection_category': 'sequential',
        'new_unit_category': 'vertical',
        'category': 'vertical'
    })
Example #14
0
 def _create_item(self, category, name, data, metadata, parent_category, parent_name, runtime):
     """
     Create the item of the given category and block id in split and old mongo, add it to the optional
     parent. The parent category is only needed because old mongo requires it for the id.
     """
     location = Location('i4x', 'test_org', 'test_course', category, name)
     self.old_mongo.create_and_save_xmodule(location, data, metadata, runtime)
     if isinstance(data, basestring):
         fields = {'data': data}
     else:
         fields = data.copy()
     fields.update(metadata)
     if parent_name:
         # add child to parent in mongo
         parent_location = Location('i4x', 'test_org', 'test_course', parent_category, parent_name)
         parent = self.old_mongo.get_item(parent_location)
         parent.children.append(location.url())
         self.old_mongo.update_children(parent_location, parent.children)
         # create pointer for split
         course_or_parent_locator = BlockUsageLocator(
             course_id=self.split_course_id,
             branch='draft',
             usage_id=parent_name
         )
     else:
         course_or_parent_locator = CourseLocator(
             course_id='test_org.test_course.runid',
             branch='draft',
         )
     self.split_mongo.create_item(course_or_parent_locator, category, self.userid, usage_id=name, fields=fields)
Example #15
0
def assets_handler(request, tag=None, course_id=None, branch=None, version_guid=None, block=None, asset_id=None):
    """
    The restful handler for assets.
    It allows retrieval of all the assets (as an HTML page), as well as uploading new assets,
    deleting assets, and changing the "locked" state of an asset.

    GET
        html: return html page of all course assets (note though that a range of assets can be requested using start
        and max query parameters)
        json: not currently supported
    POST
        json: create (or update?) an asset. The only updating that can be done is changing the lock state.
    PUT
        json: update the locked state of an asset
    DELETE
        json: delete an asset
    """
    location = BlockUsageLocator(course_id=course_id, branch=branch, version_guid=version_guid, usage_id=block)
    if not has_access(request.user, location):
        raise PermissionDenied()

    if 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'):
        if request.method == 'GET':
            raise NotImplementedError('coming soon')
        else:
            return _update_asset(request, location, asset_id)
    elif request.method == 'GET':  # assume html
        return _asset_index(request, location)
    else:
        return HttpResponseNotFound()
Example #16
0
def course_team_handler(request,
                        tag=None,
                        package_id=None,
                        branch=None,
                        version_guid=None,
                        block=None,
                        email=None):
    """
    The restful handler for course team users.

    GET
        html: return html page for managing course team
        json: return json representation of a particular course team member (email is required).
    POST or PUT
        json: modify the permissions for a particular course team member (email is required, as well as role in the payload).
    DELETE:
        json: remove a particular course team member from the course team (email is required).
    """
    location = BlockUsageLocator(package_id=package_id,
                                 branch=branch,
                                 version_guid=version_guid,
                                 block_id=block)
    if not has_course_access(request.user, location):
        raise PermissionDenied()

    if 'application/json' in request.META.get('HTTP_ACCEPT',
                                              'application/json'):
        return _course_team_user(request, location, email)
    elif request.method == 'GET':  # assume html
        return _manage_users(request, location)
    else:
        return HttpResponseNotFound()
Example #17
0
def test_mobi_course_action(request, course_id, action):
    if action not in ["structure", "updates", "handouts"]:
        return JsonResponse(status=404)

    if action != "structure":
        course_locator = BlockUsageLocator(package_id=course_id,
                                           branch='draft',
                                           version_guid=None,
                                           block_id=action)
        course_location = loc_mapper().translate_locator_to_location(
            course_locator)

        def get_course_info_by_action(location, action):
            """
            return data depend on action
            """
            if action == "updates":
                return get_course_updates(location, None)
            elif action == "handouts":
                module = get_modulestore(location).get_item(location)
                return {"data": getattr(module, 'data', '')}
            else:
                return {"error": 20000, "errmsg": "some error occur!"}

        return JsonResponse(get_course_info_by_action(course_location, action))
    else:
        return JsonResponse(_course_json(request, course_id))
Example #18
0
    def test_update_children(self):
        """
        test updating an item's children ensuring the definition doesn't version but the course does if it should
        """
        locator = BlockUsageLocator(course_id="GreekHero", usage_id="chapter3", branch='draft')
        block = modulestore().get_item(locator)
        pre_def_id = block.definition_locator.definition_id
        pre_version_guid = block.location.version_guid

        # reorder children
        self.assertGreater(len(block.children), 0, "meaningless test")
        moved_child = block.children.pop()
        block.save()  # decache model changes
        updated_problem = modulestore().update_item(block, 'childchanger')
        # check that course version changed and course's previous is the other one
        self.assertEqual(updated_problem.definition_locator.definition_id, pre_def_id)
        self.assertNotEqual(updated_problem.location.version_guid, pre_version_guid)
        self.assertEqual(updated_problem.children, block.children)
        self.assertNotIn(moved_child, updated_problem.children)
        locator.usage_id = "chapter1"
        other_block = modulestore().get_item(locator)
        other_block.children.append(moved_child)
        other_block.save()  # decache model changes
        other_updated = modulestore().update_item(other_block, 'childchanger')
        self.assertIn(moved_child, other_updated.children)
Example #19
0
 def create_subtree_for_deletion(self, parent, category_queue):
     if not category_queue:
         return
     node = modulestore().create_item(parent, category_queue[0], 'deleting_user')
     node_loc = BlockUsageLocator(parent.as_course_locator(), usage_id=node.location.usage_id)
     for _ in range(4):
         self.create_subtree_for_deletion(node_loc, category_queue[1:])
Example #20
0
def orphan_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None):
    """
    View for handling orphan related requests. GET gets all of the current orphans.
    DELETE removes all orphans (requires is_staff access)

    An orphan is a block whose category is not in the DETACHED_CATEGORY list, is not the root, and is not reachable
    from the root via children

    :param request:
    :param package_id: Locator syntax package_id
    """
    location = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block)
    # DHM: when split becomes back-end, move or conditionalize this conversion
    old_location = loc_mapper().translate_locator_to_location(location)
    if request.method == 'GET':
        if has_course_access(request.user, old_location):
            return JsonResponse(modulestore().get_orphans(old_location, 'draft'))
        else:
            raise PermissionDenied()
    if request.method == 'DELETE':
        if request.user.is_staff:
            items = modulestore().get_orphans(old_location, 'draft')
            for itemloc in items:
                modulestore('draft').delete_item(itemloc, delete_all_versions=True)
            return JsonResponse({'deleted': items})
        else:
            raise PermissionDenied()
Example #21
0
 def location(self):
     try:
         return Location(self.scope_ids.usage_id)
     except InvalidLocationError:
         if isinstance(self.scope_ids.usage_id, BlockUsageLocator):
             return self.scope_ids.usage_id
         else:
             return BlockUsageLocator(self.scope_ids.usage_id)
Example #22
0
 def create_course_for_deletion(self):
     course = modulestore().create_course('nihilx', 'deletion', 'deleting_user')
     root = BlockUsageLocator(
         course_id=course.location.course_id,
         usage_id=course.location.usage_id,
         branch='draft')
     for _ in range(4):
         self.create_subtree_for_deletion(root, ['chapter', 'vertical', 'problem'])
     return modulestore().get_item(root)
Example #23
0
 def _locator_to_location(self, reference):
     """
     Convert the referenced locator to a location casting to and from a string as necessary
     """
     stringify = isinstance(reference, basestring)
     if stringify:
         reference = BlockUsageLocator(url=reference)
     location = loc_mapper().translate_locator_to_location(reference)
     return location.url() if stringify else location
Example #24
0
 def test_split_orphan(self):
     """
     Test that old mongo finds the orphans
     """
     orphans = self.split_mongo.get_orphans(self.split_package_id, 'draft')
     self.assertEqual(len(orphans), 3, "Wrong # {}".format(orphans))
     location = BlockUsageLocator(package_id=self.split_package_id,
                                  branch='draft',
                                  block_id='OrphanChapter')
     self.assertIn(location, orphans)
     location = BlockUsageLocator(package_id=self.split_package_id,
                                  branch='draft',
                                  block_id='OrphanVert')
     self.assertIn(location, orphans)
     location = BlockUsageLocator(package_id=self.split_package_id,
                                  branch='draft',
                                  block_id='OrphanHtml')
     self.assertIn(location, orphans)
Example #25
0
def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, version_guid=None, block=None):
    """
    The restful handler for requests for rendered xblock views.

    Returns a json object containing two keys:
        html: The rendered html of the view
        resources: A list of tuples where the first element is the resource hash, and
            the second is the resource description
    """
    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)

    accept_header = request.META.get('HTTP_ACCEPT', 'application/json')

    if 'application/x-fragment+json' in accept_header:
        store = get_modulestore(old_location)
        component = store.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, 'StudioRuntime'))

        if view_name == 'studio_view':
            try:
                fragment = component.render('studio_view')
            # 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)
                fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))

            store.save_xmodule(component)

        elif view_name == 'student_view':
            fragment = get_preview_fragment(request, component)
            fragment.content = render_to_string('component.html', {
                'preview': fragment.content,
                'label': component.display_name or component.scope_ids.block_type,
            })
        else:
            raise Http404

        hashed_resources = OrderedDict()
        for resource in fragment.resources:
            hashed_resources[hash_resource(resource)] = resource

        return JsonResponse({
            'html': fragment.content,
            'resources': hashed_resources.items()
        })

    else:
        return HttpResponse(status=406)
Example #26
0
def course_handler(request,
                   tag=None,
                   package_id=None,
                   branch=None,
                   version_guid=None,
                   block=None):
    """
    The restful handler for course specific requests.
    It provides the course tree with the necessary information for identifying and labeling the parts. The root
    will typically be a 'course' object but may not be especially as we support modules.

    GET
        html: return course listing page if not given a course id
        html: return html page overview for the given course if given a course id
        json: return json representing the course branch's index entry as well as dag w/ all of the children
        replaced w/ json docs where each doc has {'_id': , 'display_name': , 'children': }
    POST
        json: create a course, return resulting json
        descriptor (same as in GET course/...). Leaving off /branch/draft would imply create the course w/ default
        branches. Cannot change the structure contents ('_id', 'display_name', 'children') but can change the
        index entry.
    PUT
        json: update this course (index entry not xblock) such as repointing head, changing display name, org,
        package_id. Return same json as above.
    DELETE
        json: delete this branch from this course (leaving off /branch/draft would imply delete the course)
    """
    response_format = request.REQUEST.get('format', 'html')
    if response_format == 'json' or 'application/json' in request.META.get(
            'HTTP_ACCEPT', 'application/json'):
        if request.method == 'GET':
            return JsonResponse(
                _course_json(request, package_id, branch, version_guid, block))
        elif request.method == 'POST':  # not sure if this is only post. If one will have ids, it goes after access
            return create_new_course(request)
        elif not has_course_access(
                request.user,
                BlockUsageLocator(package_id=package_id,
                                  branch=branch,
                                  version_guid=version_guid,
                                  block_id=block)):
            raise PermissionDenied()
        elif request.method == 'PUT':
            raise NotImplementedError()
        elif request.method == 'DELETE':
            raise NotImplementedError()
        else:
            return HttpResponseBadRequest()
    elif request.method == 'GET':  # assume html
        if package_id is None:
            return course_listing(request)
        else:
            return course_index(request, package_id, branch, version_guid,
                                block)
    else:
        return HttpResponseNotFound()
Example #27
0
    def test_cloned_course(self):
        """
        Test making a course which points to an existing draft and published but not making any changes to either.
        """
        pre_time = datetime.datetime.now(UTC)
        original_locator = CourseLocator(course_id="wonderful", branch='draft')
        original_index = modulestore().get_course_index_info(original_locator)
        new_draft = modulestore().create_course(
            'leech', 'best_course', 'leech_master', id_root='best',
            versions_dict=original_index['versions'])
        new_draft_locator = new_draft.location
        self.assertRegexpMatches(new_draft_locator.course_id, r'best.*')
        # the edited_by and other meta fields on the new course will be the original author not this one
        self.assertEqual(new_draft.edited_by, '*****@*****.**')
        self.assertLess(new_draft.edited_on, pre_time)
        self.assertEqual(new_draft.location.version_guid, original_index['versions']['draft'])
        # however the edited_by and other meta fields on course_index will be this one
        new_index = modulestore().get_course_index_info(new_draft_locator)
        self.assertGreaterEqual(new_index["edited_on"], pre_time)
        self.assertLessEqual(new_index["edited_on"], datetime.datetime.now(UTC))
        self.assertEqual(new_index['edited_by'], 'leech_master')

        new_published_locator = CourseLocator(course_id=new_draft_locator.course_id, branch='published')
        new_published = modulestore().get_course(new_published_locator)
        self.assertEqual(new_published.edited_by, '*****@*****.**')
        self.assertLess(new_published.edited_on, pre_time)
        self.assertEqual(new_published.location.version_guid, original_index['versions']['published'])

        # changing this course will not change the original course
        # using new_draft.location will insert the chapter under the course root
        new_item = modulestore().create_item(
            new_draft.location, 'chapter', 'leech_master',
            fields={'display_name': 'new chapter'}
        )
        new_draft_locator.version_guid = None
        new_index = modulestore().get_course_index_info(new_draft_locator)
        self.assertNotEqual(new_index['versions']['draft'], original_index['versions']['draft'])
        new_draft = modulestore().get_course(new_draft_locator)
        self.assertEqual(new_item.edited_by, 'leech_master')
        self.assertGreaterEqual(new_item.edited_on, pre_time)
        self.assertNotEqual(new_item.location.version_guid, original_index['versions']['draft'])
        self.assertNotEqual(new_draft.location.version_guid, original_index['versions']['draft'])
        structure_info = modulestore().get_course_history_info(new_draft_locator)
        self.assertGreaterEqual(structure_info["edited_on"], pre_time)
        self.assertLessEqual(structure_info["edited_on"], datetime.datetime.now(UTC))
        self.assertEqual(structure_info['edited_by'], 'leech_master')

        original_course = modulestore().get_course(original_locator)
        self.assertEqual(original_course.location.version_guid, original_index['versions']['draft'])
        self.assertFalse(
            modulestore().has_item(new_draft_locator.course_id, BlockUsageLocator(
                original_locator,
                usage_id=new_item.location.usage_id
            ))
        )
Example #28
0
 def test_block_constructor_url_version_prefix(self):
     test_id_loc = '519665f6223ebd6980884f2b'
     testobj = BlockUsageLocator(url='edx://mit.eecs.6002x' +
                                 VERSION_PREFIX + test_id_loc +
                                 BLOCK_PREFIX + 'lab2')
     self.check_block_locn_fields(
         testobj,
         'error parsing URL with version and block',
         course_id='mit.eecs.6002x',
         block='lab2',
         version_guid=ObjectId(test_id_loc))
Example #29
0
 def from_json(self, value):
     """
     Parse the json value as a Location
     """
     try:
         return Location(value)
     except InvalidLocationError:
         if isinstance(value, BlockUsageLocator):
             return value
         else:
             return BlockUsageLocator(value)
Example #30
0
def _get_locator_and_course(package_id, branch, version_guid, block_id, user, depth=0):
    """
    Internal method used to calculate and return the locator and course module
    for the view functions in this file.
    """
    locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block_id)
    if not has_course_access(user, locator):
        raise PermissionDenied()
    course_location = loc_mapper().translate_locator_to_location(locator)
    course_module = modulestore().get_item(course_location, depth=depth)
    return locator, course_module