コード例 #1
0
 def test_course_constructor_course_id_no_branch(self):
     testurn = "mit.eecs.6002x"
     testobj = CourseLocator(course_id=testurn)
     self.check_course_locn_fields(testobj, "course_id", course_id=testurn)
     self.assertEqual(testobj.course_id, testurn)
     self.assertEqual(str(testobj), testurn)
     self.assertEqual(testobj.url(), "edx://" + testurn)
コード例 #2
0
    def test_course_keyword_setters(self):
        raise SkipTest()
        # arg list inits
        testobj = CourseLocator(version_guid='versionid')
        self.check_course_locn_fields(testobj, 'versionid arg', 'versionid')

        testobj = CourseLocator(course_id='courseid')
        self.check_course_locn_fields(testobj,
                                      'courseid arg',
                                      course_id='courseid')

        testobj = CourseLocator(course_id='courseid', revision='rev')
        self.check_course_locn_fields(testobj,
                                      'rev arg',
                                      course_id='courseid',
                                      revision='rev')
        # ignores garbage
        testobj = CourseLocator(course_id='courseid',
                                revision='rev',
                                potato='spud')
        self.check_course_locn_fields(testobj,
                                      'extra keyword arg',
                                      course_id='courseid',
                                      revision='rev')

        # url w/ keyword override
        testurn = 'crx/courseid@revision/blockid'
        testobj = CourseLocator(testurn, revision='rev')
        self.check_course_locn_fields(testobj,
                                      'rev override',
                                      course_id='courseid',
                                      revision='rev')
コード例 #3
0
    def translate_location_to_course_locator(self,
                                             old_style_course_id,
                                             location,
                                             published=True,
                                             lower_only=False):
        """
        Used when you only need the CourseLocator and not a full BlockUsageLocator. Probably only
        useful for get_items which wildcards name or category.

        :param course_id: old style course id
        """
        cached = self._get_course_locator_from_cache(old_style_course_id,
                                                     published)
        if cached:
            return cached

        location_id = self._interpret_location_course_id(
            old_style_course_id, location, lower_only)

        entry = self._get_map_entry_form_location(location_id)
        if entry is None:
            raise ItemNotFoundError(location)

        published_course_locator = CourseLocator(package_id=entry['course_id'],
                                                 branch=entry['prod_branch'])
        draft_course_locator = CourseLocator(package_id=entry['course_id'],
                                             branch=entry['draft_branch'])
        self._cache_course_locator(old_style_course_id,
                                   published_course_locator,
                                   draft_course_locator)
        if published:
            return published_course_locator
        else:
            return draft_course_locator
コード例 #4
0
 def test_course_constructor_course_id_no_revision(self):
     testurn = 'edu.mit.eecs.6002x'
     testobj = CourseLocator(course_id=testurn)
     self.check_course_locn_fields(testobj, 'course_id', course_id=testurn)
     self.assertEqual(testobj.course_id, testurn)
     self.assertEqual(str(testobj), testurn)
     self.assertEqual(testobj.url(), 'edx://' + testurn)
コード例 #5
0
ファイル: test_crud.py プロジェクト: danielnegri/edx-platform
    def test_delete_course(self):
        test_course = persistent_factories.PersistentCourseFactory.create(
            org='testx',
            prettyid='edu.harvard.history.doomed',
            display_name='doomed test course',
            user_id='testbot')
        persistent_factories.ItemFactory.create(
            display_name='chapter 1', parent_location=test_course.location)

        id_locator = CourseLocator(course_id=test_course.location.course_id,
                                   branch='draft')
        guid_locator = CourseLocator(
            version_guid=test_course.location.version_guid)
        # verify it can be retireved by id
        self.assertIsInstance(
            modulestore('split').get_course(id_locator), CourseDescriptor)
        # and by guid
        self.assertIsInstance(
            modulestore('split').get_course(guid_locator), CourseDescriptor)
        modulestore('split').delete_course(id_locator.course_id)
        # test can no longer retrieve by id
        self.assertRaises(ItemNotFoundError,
                          modulestore('split').get_course, id_locator)
        # but can by guid
        self.assertIsInstance(
            modulestore('split').get_course(guid_locator), CourseDescriptor)
コード例 #6
0
    def translate_location_to_course_locator(self, course_key, published=True):
        """
        Used when you only need the CourseLocator and not a full BlockUsageLocator. Probably only
        useful for get_items which wildcards name or category.

        :param course_key: a CourseKey
        :param published: a boolean representing whether or not we should return the published or draft version

        Returns a Courselocator
        """
        cached = self._get_course_locator_from_cache(course_key, published)
        if cached:
            return cached

        course_son = self._interpret_location_course_id(course_key)

        entry = self.location_map.find_one(course_son)
        if entry is None:
            raise ItemNotFoundError(course_key)

        published_course_locator = CourseLocator(org=entry['org'],
                                                 offering=entry['offering'],
                                                 branch=entry['prod_branch'])
        draft_course_locator = CourseLocator(org=entry['org'],
                                             offering=entry['offering'],
                                             branch=entry['draft_branch'])
        self._cache_course_locator(course_key, published_course_locator,
                                   draft_course_locator)
        if published:
            return published_course_locator
        else:
            return draft_course_locator
コード例 #7
0
 def test_course_constructor_package_id_no_branch(self):
     org = 'mit.eecs'
     offering = '6002x'
     testurn = '{}+{}'.format(org, offering)
     testobj = CourseLocator(org=org, offering=offering)
     self.check_course_locn_fields(testobj, org=org, offering=offering)
     self.assertEqual(testobj._to_string(), testurn)
コード例 #8
0
    def translate_location_to_course_locator(self, old_style_course_id, location, published=True, lower_only=False):
        """
        Used when you only need the CourseLocator and not a full BlockUsageLocator. Probably only
        useful for get_items which wildcards name or category.

        :param course_id: old style course id
        """
        cached = self._get_course_locator_from_cache(old_style_course_id, published)
        if cached:
            return cached

        location_id = self._interpret_location_course_id(old_style_course_id, location, lower_only)

        maps = self.location_map.find(location_id)
        maps = list(maps)
        if len(maps) == 0:
            raise ItemNotFoundError(location)
        elif len(maps) == 1:
            entry = maps[0]
        else:
            # find entry w/o name, if any; otherwise, pick arbitrary
            entry = maps[0]
            for item in maps:
                if 'name' not in item['_id']:
                    entry = item
                    break
        published_course_locator = CourseLocator(package_id=entry['course_id'], branch=entry['prod_branch'])
        draft_course_locator = CourseLocator(package_id=entry['course_id'], branch=entry['draft_branch'])
        self._cache_course_locator(old_style_course_id, published_course_locator, draft_course_locator)
        if published:
            return published_course_locator
        else:
            return draft_course_locator
コード例 #9
0
 def test_course_constructor_package_id_no_branch(self):
     testurn = 'mit.eecs.6002x'
     testobj = CourseLocator(package_id=testurn)
     self.check_course_locn_fields(testobj, 'package_id', package_id=testurn)
     self.assertEqual(testobj.package_id, testurn)
     self.assertEqual(str(testobj), testurn)
     self.assertEqual(testobj.url(), 'edx://' + testurn)
コード例 #10
0
 def test_course_constructor_course_id_no_branch(self):
     testurn = 'mit.eecs.6002x'
     testobj = CourseLocator(course_id=testurn)
     self.check_course_locn_fields(testobj, 'course_id', course_id=testurn)
     self.assertEqual(testobj.course_id, testurn)
     self.assertEqual(str(testobj), testurn)
     self.assertEqual(testobj.url(), 'edx://' + testurn)
コード例 #11
0
 def test_get_course_negative(self):
     # Now negative testing
     self.assertRaises(InsufficientSpecificationError,
                       modulestore().get_course, CourseLocator(course_id='edu.meh.blah'))
     self.assertRaises(ItemNotFoundError,
                       modulestore().get_course, CourseLocator(course_id='nosuchthing', branch='draft'))
     self.assertRaises(ItemNotFoundError,
                       modulestore().get_course,
                       CourseLocator(course_id='GreekHero', branch='published'))
コード例 #12
0
 def test_course_constructor_course_id_separate_branch(self):
     test_id = "mit.eecs.6002x"
     test_branch = "published"
     expected_urn = "mit.eecs.6002x" + BRANCH_PREFIX + "published"
     testobj = CourseLocator(course_id=test_id, branch=test_branch)
     self.check_course_locn_fields(testobj, "course_id with separate branch", course_id=test_id, branch=test_branch)
     self.assertEqual(testobj.course_id, test_id)
     self.assertEqual(testobj.branch, test_branch)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), "edx://" + expected_urn)
コード例 #13
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
            ))
        )
コード例 #14
0
    def test_course_constructor_bad_package_id(self, bad_id):
        """
        Test all sorts of badly-formed package_ids (and urls with those package_ids)
        """
        with self.assertRaises(InvalidKeyError):
            CourseLocator(org=bad_id, offering='test')

        with self.assertRaises(InvalidKeyError):
            CourseLocator(org='test', offering=bad_id)

        with self.assertRaises(InvalidKeyError):
            CourseKey.from_string('course-locator:test+{}'.format(bad_id))
コード例 #15
0
 def test_course_constructor_course_id_with_branch(self):
     testurn = 'mit.eecs.6002x/' + BRANCH_PREFIX + 'published'
     expected_id = 'mit.eecs.6002x'
     expected_branch = 'published'
     testobj = CourseLocator(course_id=testurn)
     self.check_course_locn_fields(testobj, 'course_id with branch',
                                   course_id=expected_id,
                                   branch=expected_branch,
                                   )
     self.assertEqual(testobj.course_id, expected_id)
     self.assertEqual(testobj.branch, expected_branch)
     self.assertEqual(str(testobj), testurn)
     self.assertEqual(testobj.url(), 'edx://' + testurn)
コード例 #16
0
 def test_course_constructor_course_id_separate_revision(self):
     test_id = 'edu.mit.eecs.6002x'
     test_revision = 'published'
     expected_urn = 'edu.mit.eecs.6002x;published'
     testobj = CourseLocator(course_id=test_id, revision=test_revision)
     self.check_course_locn_fields(testobj, 'course_id with separate revision',
                                   course_id=test_id,
                                   revision=test_revision,
                                   )
     self.assertEqual(testobj.course_id, test_id)
     self.assertEqual(testobj.revision, test_revision)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), 'edx://' + expected_urn)
コード例 #17
0
 def test_course_constructor_package_id_separate_branch(self):
     test_id = 'mit.eecs.6002x'
     test_branch = 'published'
     expected_urn = 'mit.eecs.6002x/' + BRANCH_PREFIX + 'published'
     testobj = CourseLocator(package_id=test_id, branch=test_branch)
     self.check_course_locn_fields(testobj, 'package_id with separate branch',
                                   package_id=test_id,
                                   branch=test_branch,
                                   )
     self.assertEqual(testobj.package_id, test_id)
     self.assertEqual(testobj.branch, test_branch)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), 'edx://' + expected_urn)
コード例 #18
0
 def test_course_constructor_course_id_separate_branch(self):
     test_id = 'mit.eecs.6002x'
     test_branch = 'published'
     expected_urn = 'mit.eecs.6002x' + BRANCH_PREFIX + 'published'
     testobj = CourseLocator(course_id=test_id, branch=test_branch)
     self.check_course_locn_fields(testobj, 'course_id with separate branch',
                                   course_id=test_id,
                                   branch=test_branch,
                                   )
     self.assertEqual(testobj.course_id, test_id)
     self.assertEqual(testobj.branch, test_branch)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), 'edx://' + expected_urn)
コード例 #19
0
 def test_course_constructor_package_id_separate_branch(self):
     org = 'mit.eecs'
     offering = '6002x'
     test_branch = 'published'
     expected_urn = '{}+{}+{}+{}'.format(org, offering, CourseLocator.BRANCH_PREFIX, test_branch)
     testobj = CourseLocator(org=org, offering=offering, branch=test_branch)
     self.check_course_locn_fields(
         testobj,
         org=org,
         offering=offering,
         branch=test_branch,
     )
     self.assertEqual(testobj.branch, test_branch)
     self.assertEqual(testobj._to_string(), expected_urn)
コード例 #20
0
 def test_get_parents(self):
     '''
     get_parent_locations(locator, [usage_id], [branch]): [BlockUsageLocator]
     '''
     locator = CourseLocator(course_id="GreekHero", branch='draft')
     parents = modulestore().get_parent_locations(locator, usage_id='chapter1')
     self.assertEqual(len(parents), 1)
     self.assertEqual(parents[0].usage_id, 'head12345')
     self.assertEqual(parents[0].course_id, "GreekHero")
     locator.usage_id = 'chapter2'
     parents = modulestore().get_parent_locations(locator)
     self.assertEqual(len(parents), 1)
     self.assertEqual(parents[0].usage_id, 'head12345')
     parents = modulestore().get_parent_locations(locator, usage_id='nosuchblock')
     self.assertEqual(len(parents), 0)
コード例 #21
0
 def test_course_constructor_course_id_separate_revision(self):
     test_id = 'edu.mit.eecs.6002x'
     test_revision = 'published'
     expected_urn = 'edu.mit.eecs.6002x;published'
     testobj = CourseLocator(course_id=test_id, revision=test_revision)
     self.check_course_locn_fields(
         testobj,
         'course_id with separate revision',
         course_id=test_id,
         revision=test_revision,
     )
     self.assertEqual(testobj.course_id, test_id)
     self.assertEqual(testobj.revision, test_revision)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), 'edx://' + expected_urn)
コード例 #22
0
 def test_course_constructor_package_id_with_branch(self):
     testurn = 'mit.eecs.6002x/' + BRANCH_PREFIX + 'published'
     expected_id = 'mit.eecs.6002x'
     expected_branch = 'published'
     testobj = CourseLocator(package_id=testurn)
     self.check_course_locn_fields(
         testobj,
         'package_id with branch',
         package_id=expected_id,
         branch=expected_branch,
     )
     self.assertEqual(testobj.package_id, expected_id)
     self.assertEqual(testobj.branch, expected_branch)
     self.assertEqual(str(testobj), testurn)
     self.assertEqual(testobj.url(), 'edx://' + testurn)
コード例 #23
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)
コード例 #24
0
    def _load_item(self, block_id, course_entry_override=None):
        if isinstance(block_id, BlockUsageLocator):
            if isinstance(block_id.block_id, LocalId):
                try:
                    return self.local_modules[block_id]
                except KeyError:
                    raise ItemNotFoundError
            else:
                block_id = block_id.block_id

        json_data = self.module_data.get(block_id)
        if json_data is None:
            # deeper than initial descendant fetch or doesn't exist
            course_info = course_entry_override or self.course_entry
            course_key = CourseLocator(course_info.get('org'),
                                       course_info.get('offering'),
                                       course_info.get('branch'),
                                       course_info['structure']['_id'])
            self.modulestore.cache_items(self, [block_id],
                                         course_key,
                                         lazy=self.lazy)
            json_data = self.module_data.get(block_id)
            if json_data is None:
                raise ItemNotFoundError(block_id)

        class_ = self.load_block_type(json_data.get('category'))
        return self.xblock_from_json(class_, block_id, json_data,
                                     course_entry_override)
コード例 #25
0
    def handle(self, *args, **options):
        if len(args) < 2:
            raise CommandError(
                "rollback_split_course requires 2 arguments (org offering)"
            )

        try:
            locator = CourseLocator(org=args[0], offering=args[1])
        except ValueError:
            raise CommandError("Invalid org or offering string {}, {}".format(*args))

        location = loc_mapper().translate_locator_to_location(locator, get_course=True)
        if not location:
            raise CommandError(
                "This course does not exist in the old Mongo store. "
                "This command is designed to rollback a course, not delete "
                "it entirely."
            )
        old_mongo_course = modulestore('direct').get_item(location)
        if not old_mongo_course:
            raise CommandError(
                "This course does not exist in the old Mongo store. "
                "This command is designed to rollback a course, not delete "
                "it entirely."
            )

        try:
            modulestore('split').delete_course(locator)
        except ItemNotFoundError:
            raise CommandError("No course found with locator {}".format(locator))

        print(
            'Course rolled back successfully. To delete this course entirely, '
            'call the "delete_course" management command.'
        )
コード例 #26
0
ファイル: test_access.py プロジェクト: ybergner/edx-platform
 def setUp(self):
     """ Test case setup """
     self.global_admin = AdminFactory()
     self.instructor = User.objects.create_user('testinstructor', '*****@*****.**', 'foo')
     self.staff = User.objects.create_user('teststaff', '*****@*****.**', 'foo')
     self.location = Location('i4x', 'mitX', '101', 'course', 'test')
     self.locator = CourseLocator(url='edx://mitX.101.test')
コード例 #27
0
 def test_course_constructor_url_course_id_branch_and_version_guid(self):
     test_id_loc = '519665f6223ebd6980884f2b'
     testobj = CourseLocator(url='edx://mit.eecs.6002x' + BRANCH_PREFIX + 'draft' + VERSION_PREFIX + test_id_loc)
     self.check_course_locn_fields(testobj, 'error parsing url with both course ID branch, and version GUID',
                                   course_id='mit.eecs.6002x',
                                   branch='draft',
                                   version_guid=ObjectId(test_id_loc))
コード例 #28
0
    def test_get_items(self):
        '''
        get_items(locator, qualifiers, [branch])
        '''
        locator = CourseLocator(version_guid=self.GUID_D0)
        # get all modules
        matches = modulestore().get_items(locator)
        self.assertEqual(len(matches), 6)
        matches = modulestore().get_items(locator, qualifiers={})
        self.assertEqual(len(matches), 6)
        matches = modulestore().get_items(locator, qualifiers={'category': 'chapter'})
        self.assertEqual(len(matches), 3)
        matches = modulestore().get_items(locator, qualifiers={'category': 'garbage'})
        self.assertEqual(len(matches), 0)
        matches = modulestore().get_items(
            locator,
            qualifiers=
            {
                'category': 'chapter',
                'fields': {'display_name': {'$regex': 'Hera'}}
            }
        )
        self.assertEqual(len(matches), 2)

        matches = modulestore().get_items(locator, qualifiers={'fields': {'children': 'chapter2'}})
        self.assertEqual(len(matches), 1)
        self.assertEqual(matches[0].location.usage_id, 'head12345')
コード例 #29
0
    def test_course_constructor_version_guid(self):
        # generate a random location
        test_id_1 = ObjectId()
        test_id_1_loc = str(test_id_1)
        testobj_1 = CourseLocator(version_guid=test_id_1)
        self.check_course_locn_fields(testobj_1, version_guid=test_id_1)
        self.assertEqual(str(testobj_1.version_guid), test_id_1_loc)
        self.assertEqual(testobj_1._to_string(), u'+'.join((testobj_1.VERSION_PREFIX, test_id_1_loc)))

        # Test using a given string
        test_id_2_loc = '519665f6223ebd6980884f2b'
        test_id_2 = ObjectId(test_id_2_loc)
        testobj_2 = CourseLocator(version_guid=test_id_2)
        self.check_course_locn_fields(testobj_2, version_guid=test_id_2)
        self.assertEqual(str(testobj_2.version_guid), test_id_2_loc)
        self.assertEqual(testobj_2._to_string(), u'+'.join((testobj_2.VERSION_PREFIX, test_id_2_loc)))
コード例 #30
0
 def test_course_constructor_course_id_repeated_branch(self):
     """
     The same branch appears in the course_id and the branch field.
     """
     test_id = "mit.eecs.6002x" + BRANCH_PREFIX + "published"
     test_branch = "published"
     expected_id = "mit.eecs.6002x"
     expected_urn = test_id
     testobj = CourseLocator(course_id=test_id, branch=test_branch)
     self.check_course_locn_fields(
         testobj, "course_id with repeated branch", course_id=expected_id, branch=test_branch
     )
     self.assertEqual(testobj.course_id, expected_id)
     self.assertEqual(testobj.branch, test_branch)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), "edx://" + expected_urn)
コード例 #31
0
    def create_map_entry(self,
                         course_key,
                         org=None,
                         offering=None,
                         draft_branch='draft',
                         prod_branch='published',
                         block_map=None):
        """
        Add a new entry to map this SlashSeparatedCourseKey to the new style CourseLocator.org & offering. If
        org and offering are not provided, it defaults them based on course_key.

        WARNING: Exactly 1 CourseLocator key should index a given SlashSeparatedCourseKey.
        We provide no mechanism to enforce this assertion.

        NOTE: if there's already an entry w the given course_key, this may either overwrite that entry or
        throw an error depending on how mongo is configured.

        :param course_key (SlashSeparatedCourseKey): a SlashSeparatedCourseKey
        :param org (string): the CourseLocator style org
        :param offering (string): the CourseLocator offering
        :param draft_branch: the branch name to assign for drafts. This is hardcoded because old mongo had
        a fixed notion that there was 2 and only 2 versions for modules: draft and production. The old mongo
        did not, however, require that a draft version exist. The new one, however, does require a draft to
        exist.
        :param prod_branch: the branch name to assign for the production (live) copy. In old mongo, every course
        had to have a production version (whereas new split mongo does not require that until the author's ready
        to publish).
        :param block_map: an optional map to specify preferred names for blocks where the keys are the
        Location block names and the values are the BlockUsageLocator.block_id.

        Returns:
            :class:`CourseLocator` representing the new id for the course

        Raises:
            ValueError if one and only one of org and offering is provided. Provide either both or neither.
        """
        if org is None and offering is None:
            assert (isinstance(course_key, SlashSeparatedCourseKey))
            org = course_key.org
            offering = u"{0.course}.{0.run}".format(course_key)
        elif org is None or offering is None:
            raise ValueError(
                u"Either supply both org and offering or neither. Not just one: {}, {}"
                .format(org, offering))

        # very like _interpret_location_id but using mongo subdoc lookup (more performant)
        course_son = self._construct_course_son(course_key)

        self.location_map.insert({
            '_id': course_son,
            'org': org,
            'offering': offering,
            'draft_branch': draft_branch,
            'prod_branch': prod_branch,
            'block_map': block_map or {},
            'schema': self.SCHEMA_VERSION,
        })

        return CourseLocator(org, offering)
コード例 #32
0
 def test_course_constructor_course_id_repeated_branch(self):
     """
     The same branch appears in the course_id and the branch field.
     """
     test_id = 'mit.eecs.6002x' + BRANCH_PREFIX + 'published'
     test_branch = 'published'
     expected_id = 'mit.eecs.6002x'
     expected_urn = test_id
     testobj = CourseLocator(course_id=test_id, branch=test_branch)
     self.check_course_locn_fields(testobj, 'course_id with repeated branch',
                                   course_id=expected_id,
                                   branch=test_branch,
                                   )
     self.assertEqual(testobj.course_id, expected_id)
     self.assertEqual(testobj.branch, test_branch)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), 'edx://' + expected_urn)
コード例 #33
0
 def test_course_constructor_course_id_repeated_revision(self):
     """
     The same revision appears in the course_id and the revision field.
     """
     test_id = 'edu.mit.eecs.6002x;published'
     test_revision = 'published'
     expected_id = 'edu.mit.eecs.6002x'
     expected_urn = 'edu.mit.eecs.6002x;published'
     testobj = CourseLocator(course_id=test_id, revision=test_revision)
     self.check_course_locn_fields(testobj, 'course_id with repeated revision',
                                   course_id=expected_id,
                                   revision=test_revision,
                                   )
     self.assertEqual(testobj.course_id, expected_id)
     self.assertEqual(testobj.revision, test_revision)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), 'edx://' + expected_urn)
コード例 #34
0
 def test_course_constructor_redundant_002(self):
     testurn = 'mit.eecs.6002x' + BRANCH_PREFIX + 'published'
     expected_urn = 'mit.eecs.6002x'
     expected_rev = 'published'
     testobj = CourseLocator(course_id=testurn, url='edx://' + testurn)
     self.check_course_locn_fields(testobj, 'course_id',
                                   course_id=expected_urn,
                                   branch=expected_rev)
コード例 #35
0
 def test_course_constructor_package_id_repeated_branch(self):
     """
     The same branch appears in the package_id and the branch field.
     """
     test_id = 'mit.eecs.6002x/' + BRANCH_PREFIX + 'published'
     test_branch = 'published'
     expected_id = 'mit.eecs.6002x'
     expected_urn = test_id
     testobj = CourseLocator(package_id=test_id, branch=test_branch)
     self.check_course_locn_fields(testobj, 'package_id with repeated branch',
                                   package_id=expected_id,
                                   branch=test_branch,
                                   )
     self.assertEqual(testobj.package_id, expected_id)
     self.assertEqual(testobj.branch, test_branch)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), 'edx://' + expected_urn)
コード例 #36
0
ファイル: course.py プロジェクト: raeeschachar/edx-platform
def course_info_update_handler(request,
                               tag=None,
                               package_id=None,
                               branch=None,
                               version_guid=None,
                               block=None,
                               provided_id=None):
    """
    restful CRUD operations on course_info updates.
    provided_id should be none if it's new (create) and index otherwise.
    GET
        json: return the course info update models
    POST
        json: create an update
    PUT or DELETE
        json: change an existing update
    """
    if 'application/json' not in request.META.get('HTTP_ACCEPT',
                                                  'application/json'):
        return HttpResponseBadRequest("Only supports json requests")

    course_location = loc_mapper().translate_locator_to_location(
        CourseLocator(package_id=package_id), get_course=True)
    updates_location = course_location.replace(category='course_info',
                                               name=block)
    if provided_id == '':
        provided_id = None

    # check that logged in user has permissions to this item (GET shouldn't require this level?)
    if not has_course_access(request.user, updates_location):
        raise PermissionDenied()

    if request.method == 'GET':
        course_updates = get_course_updates(updates_location, provided_id)
        if isinstance(course_updates, dict) and course_updates.get('error'):
            return JsonResponse(
                get_course_updates(updates_location, provided_id),
                course_updates.get('status', 400))
        else:
            return JsonResponse(
                get_course_updates(updates_location, provided_id))
    elif request.method == 'DELETE':
        try:
            return JsonResponse(
                delete_course_update(updates_location, request.json,
                                     provided_id, request.user))
        except:
            return HttpResponseBadRequest("Failed to delete",
                                          content_type="text/plain")
    # can be either and sometimes django is rewriting one to the other:
    elif request.method in ('POST', 'PUT'):
        try:
            return JsonResponse(
                update_course_updates(updates_location, request.json,
                                      provided_id, request.user))
        except:
            return HttpResponseBadRequest("Failed to save",
                                          content_type="text/plain")
コード例 #37
0
 def test_course_constructor_url(self):
     # Test parsing a url when it starts with a version ID and there is also a block ID.
     # This hits the parsers parse_guid method.
     test_id_loc = '519665f6223ebd6980884f2b'
     testobj = CourseLocator(url="edx://" + URL_VERSION_PREFIX +
                             test_id_loc + BLOCK_PREFIX + "hw3")
     self.check_course_locn_fields(testobj,
                                   'test_block constructor',
                                   version_guid=ObjectId(test_id_loc))
コード例 #38
0
 def test_course_constructor_url_package_id_and_version_guid(self):
     test_id_loc = '519665f6223ebd6980884f2b'
     testobj = CourseLocator(url='edx://mit.eecs-honors.6002x/' +
                             VERSION_PREFIX + test_id_loc)
     self.check_course_locn_fields(
         testobj,
         'error parsing url with both course ID and version GUID',
         package_id='mit.eecs-honors.6002x',
         version_guid=ObjectId(test_id_loc))
コード例 #39
0
 def test_course_constructor_redundant_002(self):
     testurn = 'edu.mit.eecs.6002x;published'
     expected_urn = 'edu.mit.eecs.6002x'
     expected_rev = 'published'
     testobj = CourseLocator(course_id=testurn, url='edx://' + testurn)
     self.check_course_locn_fields(testobj,
                                   'course_id',
                                   course_id=expected_urn,
                                   revision=expected_rev)
コード例 #40
0
    def test_course_constructor_version_guid(self):
        # generate a random location
        test_id_1 = ObjectId()
        test_id_1_loc = str(test_id_1)
        testobj_1 = CourseLocator(version_guid=test_id_1)
        self.check_course_locn_fields(testobj_1, 'version_guid', version_guid=test_id_1)
        self.assertEqual(str(testobj_1.version_guid), test_id_1_loc)
        self.assertEqual(str(testobj_1), VERSION_PREFIX + test_id_1_loc)
        self.assertEqual(testobj_1.url(), 'edx://' + VERSION_PREFIX + test_id_1_loc)

        # Test using a given string
        test_id_2_loc = '519665f6223ebd6980884f2b'
        test_id_2 = ObjectId(test_id_2_loc)
        testobj_2 = CourseLocator(version_guid=test_id_2)
        self.check_course_locn_fields(testobj_2, 'version_guid', version_guid=test_id_2)
        self.assertEqual(str(testobj_2.version_guid), test_id_2_loc)
        self.assertEqual(str(testobj_2), VERSION_PREFIX + test_id_2_loc)
        self.assertEqual(testobj_2.url(), 'edx://' + VERSION_PREFIX + test_id_2_loc)
コード例 #41
0
 def test_course_constructor_course_id_repeated_revision(self):
     """
     The same revision appears in the course_id and the revision field.
     """
     test_id = 'edu.mit.eecs.6002x;published'
     test_revision = 'published'
     expected_id = 'edu.mit.eecs.6002x'
     expected_urn = 'edu.mit.eecs.6002x;published'
     testobj = CourseLocator(course_id=test_id, revision=test_revision)
     self.check_course_locn_fields(
         testobj,
         'course_id with repeated revision',
         course_id=expected_id,
         revision=test_revision,
     )
     self.assertEqual(testobj.course_id, expected_id)
     self.assertEqual(testobj.revision, test_revision)
     self.assertEqual(str(testobj), expected_urn)
     self.assertEqual(testobj.url(), 'edx://' + expected_urn)
コード例 #42
0
    def test_update_course_index(self):
        """
        Test changing the org, pretty id, etc of a course. Test that it doesn't allow changing the id, etc.
        """
        locator = CourseLocator(course_id="GreekHero", branch='draft')
        modulestore().update_course_index(locator, {'org': 'funkyU'})
        course_info = modulestore().get_course_index_info(locator)
        self.assertEqual(course_info['org'], 'funkyU')

        modulestore().update_course_index(locator, {'org': 'moreFunky', 'prettyid': 'Ancient Greek Demagods'})
        course_info = modulestore().get_course_index_info(locator)
        self.assertEqual(course_info['org'], 'moreFunky')
        self.assertEqual(course_info['prettyid'], 'Ancient Greek Demagods')

        self.assertRaises(ValueError, modulestore().update_course_index, locator, {'_id': 'funkygreeks'})

        with self.assertRaises(ValueError):
            modulestore().update_course_index(
                locator,
                {'edited_on': datetime.datetime.now(UTC)}
            )
        with self.assertRaises(ValueError):
            modulestore().update_course_index(
                locator,
                {'edited_by': 'sneak'}
            )

        self.assertRaises(ValueError, modulestore().update_course_index, locator,
                          {'versions': {'draft': self.GUID_D1}})

        # an allowed but not necessarily recommended way to revert the draft version
        versions = course_info['versions']
        versions['draft'] = self.GUID_D1
        modulestore().update_course_index(locator, {'versions': versions}, update_versions=True)
        course = modulestore().get_course(locator)
        self.assertEqual(str(course.location.version_guid), self.GUID_D1)

        # an allowed but not recommended way to publish a course
        versions['published'] = self.GUID_D1
        modulestore().update_course_index(locator, {'versions': versions}, update_versions=True)
        course = modulestore().get_course(CourseLocator(course_id=locator.course_id, branch="published"))
        self.assertEqual(str(course.location.version_guid), self.GUID_D1)
コード例 #43
0
    def test_course_urls(self):
        '''
        Test constructor and property accessors.
        '''
        raise SkipTest()
        self.assertRaises(TypeError, CourseLocator, 'empty constructor')

        # url inits
        testurn = 'edx://org/course/category/name'
        self.assertRaises(InvalidLocationError, CourseLocator, url=testurn)
        testurn = 'unknown/versionid/blockid'
        self.assertRaises(InvalidLocationError, CourseLocator, url=testurn)

        testurn = 'cvx/versionid'
        testobj = CourseLocator(testurn)
        self.check_course_locn_fields(testobj, testurn, 'versionid')
        self.assertEqual(testobj, CourseLocator(testobj),
                         'initialization from another instance')

        testurn = 'cvx/versionid/'
        testobj = CourseLocator(testurn)
        self.check_course_locn_fields(testobj, testurn, 'versionid')

        testurn = 'cvx/versionid/blockid'
        testobj = CourseLocator(testurn)
        self.check_course_locn_fields(testobj, testurn, 'versionid')

        testurn = 'cvx/versionid/blockid/extraneousstuff?including=args'
        testobj = CourseLocator(testurn)
        self.check_course_locn_fields(testobj, testurn, 'versionid')

        testurn = 'cvx://versionid/blockid'
        testobj = CourseLocator(testurn)
        self.check_course_locn_fields(testobj, testurn, 'versionid')

        testurn = 'crx/courseid/blockid'
        testobj = CourseLocator(testurn)
        self.check_course_locn_fields(testobj, testurn, course_id='courseid')

        testurn = 'crx/courseid@revision/blockid'
        testobj = CourseLocator(testurn)
        self.check_course_locn_fields(testobj,
                                      testurn,
                                      course_id='courseid',
                                      revision='revision')
        self.assertEqual(testobj, CourseLocator(testobj),
                         'run initialization from another instance')
コード例 #44
0
 def test_url_reverse(self):
     """
     Test the url_reverse method
     """
     locator = CourseLocator(package_id="a.fancy_course-id", branch="branch_1.2-3")
     self.assertEqual(
         '/expression/{}/format'.format(unicode(locator)),
         locator.url_reverse('expression', 'format')
     )
     self.assertEqual(
         '/expression/{}/format'.format(unicode(locator)),
         locator.url_reverse('/expression', '/format')
     )
     self.assertEqual(
         '/expression/{}'.format(unicode(locator)),
         locator.url_reverse('expression/', None)
     )
     self.assertEqual(
         '/expression/{}'.format(unicode(locator)),
         locator.url_reverse('/expression/', '')
     )
コード例 #45
0
    def test_html(self):
        '''
        Ensure CourseLocator generates expected urls.
        '''
        raise SkipTest()
        testobj = CourseLocator(version_guid='versionid')
        self.assertEqual(testobj.html_id(), 'cvx/versionid', 'versionid')
        self.assertEqual(testobj, CourseLocator(testobj.html_id()),
                         'versionid conversion through html_id')

        testobj = CourseLocator(course_id='courseid')
        self.assertEqual(testobj.html_id(), 'crx/courseid', 'courseid')
        self.assertEqual(testobj, CourseLocator(testobj.html_id()),
                         'courseid conversion through html_id')

        testobj = CourseLocator(course_id='courseid', revision='rev')
        self.assertEqual(testobj.html_id(), 'crx/courseid%40rev', 'rev')
        self.assertEqual(testobj, CourseLocator(testobj.html_id()),
                         'rev conversion through html_id')
コード例 #46
0
    def translate_location(self, location, published=True,
                           add_entry_if_missing=True, passed_block_id=None):
        """
        Translate the given module location to a Locator.

        The rationale for auto adding entries was that there should be a reasonable default translation
        if the code just trips into this w/o creating translations.

        Will raise ItemNotFoundError if there's no mapping and add_entry_if_missing is False.

        :param location:  a Location pointing to a module
        :param published: a boolean to indicate whether the caller wants the draft or published branch.
        :param add_entry_if_missing: a boolean as to whether to raise ItemNotFoundError or to create an entry if
        the course
        or block is not found in the map.
        :param passed_block_id: what block_id to assign and save if none is found
        (only if add_entry_if_missing)

        NOTE: unlike old mongo, draft branches contain the whole course; so, it applies to all category
        of locations including course.
        """
        course_son = self._interpret_location_course_id(location.course_key)

        cached_value = self._get_locator_from_cache(location, published)
        if cached_value:
            return cached_value

        entry = self.location_map.find_one(course_son)
        if entry is None:
            if add_entry_if_missing:
                # create a new map
                self.create_map_entry(location.course_key)
                entry = self.location_map.find_one(course_son)
            else:
                raise ItemNotFoundError(location)
        else:
            entry = self._migrate_if_necessary([entry])[0]

        block_id = entry['block_map'].get(self.encode_key_for_mongo(location.name))
        category = location.category
        if block_id is None:
            if add_entry_if_missing:
                block_id = self._add_to_block_map(
                    location, course_son, entry['block_map'], passed_block_id
                )
            else:
                raise ItemNotFoundError(location)
        else:
            # jump_to_id uses a None category.
            if category is None:
                if len(block_id) == 1:
                    # unique match (most common case)
                    category = block_id.keys()[0]
                    block_id = block_id.values()[0]
                else:
                    raise InvalidLocationError()
            elif category in block_id:
                block_id = block_id[category]
            elif add_entry_if_missing:
                block_id = self._add_to_block_map(location, course_son, entry['block_map'])
            else:
                raise ItemNotFoundError(location)

        prod_course_locator = CourseLocator(
            org=entry['org'],
            offering=entry['offering'],
            branch=entry['prod_branch']
        )
        published_usage = BlockUsageLocator(
            prod_course_locator,
            block_type=category,
            block_id=block_id
        )
        draft_usage = BlockUsageLocator(
            prod_course_locator.for_branch(entry['draft_branch']),
            block_type=category,
            block_id=block_id
        )
        if published:
            result = published_usage
        else:
            result = draft_usage

        self._cache_location_map_entry(location, published_usage, draft_usage)
        return result
コード例 #47
0
    def test_translate_locator(self):
        """
        tests translate_locator_to_location(BlockUsageLocator)
        """
        # lookup for non-existent course
        org = 'foo_org'
        course = 'bar_course'
        run = 'baz_run'
        new_style_org = '{}.geek_dept'.format(org)
        new_style_offering = '{}.{}'.format(course, run)
        prob_course_key = CourseLocator(
            org=new_style_org, offering=new_style_offering,
            branch='published',
        )
        prob_locator = BlockUsageLocator(
            prob_course_key,
            block_type='problem',
            block_id='problem2',
        )
        prob_location = loc_mapper().translate_locator_to_location(prob_locator)
        self.assertIsNone(prob_location, 'found entry in empty map table')

        loc_mapper().create_map_entry(
            SlashSeparatedCourseKey(org, course, run),
            new_style_org, new_style_offering,
            block_map={
                'abc123': {'problem': 'problem2'},
                '48f23a10395384929234': {'chapter': 'chapter48f'},
                'baz_run': {'course': 'root'},
            }
        )
        # only one course matches
        prob_location = loc_mapper().translate_locator_to_location(prob_locator)
        # default branch
        self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', None))
        # test get_course keyword
        prob_location = loc_mapper().translate_locator_to_location(prob_locator, get_course=True)
        self.assertEqual(prob_location, SlashSeparatedCourseKey(org, course, run))
        # explicit branch
        prob_locator = prob_locator.for_branch('draft')
        prob_location = loc_mapper().translate_locator_to_location(prob_locator)
        # Even though the problem was set as draft, we always return revision=None to work
        # with old mongo/draft modulestores.
        self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', None))
        prob_locator = BlockUsageLocator(
            prob_course_key.for_branch('production'),
            block_type='problem', block_id='problem2'
        )
        prob_location = loc_mapper().translate_locator_to_location(prob_locator)
        self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', None))
        # same for chapter except chapter cannot be draft in old system
        chap_locator = BlockUsageLocator(
            prob_course_key.for_branch('production'),
            block_type='chapter', block_id='chapter48f',
        )
        chap_location = loc_mapper().translate_locator_to_location(chap_locator)
        self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234'))
        # explicit branch
        chap_locator = chap_locator.for_branch('draft')
        chap_location = loc_mapper().translate_locator_to_location(chap_locator)
        self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234'))
        chap_locator = BlockUsageLocator(
            prob_course_key.for_branch('production'), block_type='chapter', block_id='chapter48f'
        )
        chap_location = loc_mapper().translate_locator_to_location(chap_locator)
        self.assertEqual(chap_location, Location(org, course, run, 'chapter', '48f23a10395384929234'))

        # look for non-existent problem
        prob_locator2 = BlockUsageLocator(
            prob_course_key.for_branch('draft'),
            block_type='problem', block_id='problem3'
        )
        prob_location = loc_mapper().translate_locator_to_location(prob_locator2)
        self.assertIsNone(prob_location, 'Found non-existent problem')

        # add a distractor course
        delta_run = 'delta_run'
        new_style_offering = '{}.{}'.format(course, delta_run)
        loc_mapper().create_map_entry(
            SlashSeparatedCourseKey(org, course, delta_run),
            new_style_org, new_style_offering,
            block_map={'abc123': {'problem': 'problem3'}}
        )
        prob_location = loc_mapper().translate_locator_to_location(prob_locator)
        self.assertEqual(prob_location, Location(org, course, run, 'problem', 'abc123', None))