def test_poll_and_conditional_import(self):
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=["conditional_and_poll"])

        course = modulestore.get_courses()[0]
        chapters = course.get_children()
        ch1 = chapters[0]
        sections = ch1.get_children()

        self.assertEqual(len(sections), 1)

        location = course.location

        conditional_location = Location(location.tag, location.org, location.course, "conditional", "condone")
        module = modulestore.get_instance(course.id, conditional_location)
        self.assertEqual(len(module.children), 1)

        poll_location = Location(location.tag, location.org, location.course, "poll_question", "first_poll")
        module = modulestore.get_instance(course.id, poll_location)
        self.assertEqual(len(module.get_children()), 0)
        self.assertEqual(module.voted, False)
        self.assertEqual(module.poll_answer, "")
        self.assertEqual(module.poll_answers, {})
        self.assertEqual(
            module.answers,
            [{"text": u"Yes", "id": "Yes"}, {"text": u"No", "id": "No"}, {"text": u"Don't know", "id": "Dont_know"}],
        )
Exemple #2
0
def import_course(course_dir, verbose=True):
    course_dir = path(course_dir)
    data_dir = course_dir.dirname()
    course_dirs = [course_dir.basename()]

    # No default class--want to complain if it doesn't find plugins for any
    # module.
    modulestore = XMLModuleStore(data_dir,
                                 default_class=None,
                                 course_dirs=course_dirs)

    def str_of_err(tpl):
        (msg, exc_str) = tpl
        return '{msg}\n{exc}'.format(msg=msg, exc=exc_str)

    courses = modulestore.get_courses()

    n = len(courses)
    if n != 1:
        sys.stderr.write('ERROR: Expect exactly 1 course.  Loaded {n}: {lst}\n'.format(
            n=n, lst=courses))
        return None

    course = courses[0]
    errors = modulestore.get_item_errors(course.location)
    if len(errors) != 0:
        sys.stderr.write('ERRORs during import: {0}\n'.format(
            '\n'.join(map(str_of_err, errors))))

    return course
Exemple #3
0
    def test_dag_course(self, mock_logging):
        """
        Test a course whose structure is not a tree.
        """
        store = XMLModuleStore(
            DATA_DIR,
            source_dirs=['xml_dag'],
            xblock_mixins=(XModuleMixin,),
        )
        course_key = store.get_courses()[0].id

        mock_logging.warning.assert_called_with(
            "%s has more than one definition", course_key.make_usage_key('discussion', 'duplicate_def')
        )

        shared_item_loc = course_key.make_usage_key('html', 'toyhtml')
        shared_item = store.get_item(shared_item_loc)
        parent = shared_item.get_parent()
        self.assertIsNotNone(parent, "get_parent failed to return a value")
        parent_loc = course_key.make_usage_key('vertical', 'vertical_test')
        self.assertEqual(parent.location, parent_loc)
        self.assertIn(shared_item, parent.get_children())
        # ensure it's still a child of the other parent even tho it doesn't claim the other parent as its parent
        other_parent_loc = course_key.make_usage_key('vertical', 'zeta')
        other_parent = store.get_item(other_parent_loc)
        # children rather than get_children b/c the instance returned by get_children != shared_item
        self.assertIn(shared_item_loc, other_parent.children)
    def test_poll_and_conditional_import(self):
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['conditional_and_poll'])

        course = modulestore.get_courses()[0]
        chapters = course.get_children()
        ch1 = chapters[0]
        sections = ch1.get_children()

        self.assertEqual(len(sections), 1)

        conditional_location = course.id.make_usage_key('conditional', 'condone')
        module = modulestore.get_item(conditional_location)
        self.assertEqual(len(module.children), 1)

        poll_location = course.id.make_usage_key('poll_question', 'first_poll')
        module = modulestore.get_item(poll_location)
        self.assertEqual(len(module.get_children()), 0)
        self.assertEqual(module.voted, False)
        self.assertEqual(module.poll_answer, '')
        self.assertEqual(module.poll_answers, {})
        self.assertEqual(
            module.answers,
            [
                {'text': u'Yes', 'id': 'Yes'},
                {'text': u'No', 'id': 'No'},
                {'text': u"Don't know", 'id': 'Dont_know'}
            ]
        )
Exemple #5
0
    def test_colon_in_url_name(self):
        """Ensure that colons in url_names convert to file paths properly"""

        print "Starting import"
        # Not using get_courses because we need the modulestore object too afterward
        modulestore = XMLModuleStore(DATA_DIR, source_dirs=['toy'])
        courses = modulestore.get_courses()
        self.assertEquals(len(courses), 1)
        course = courses[0]

        print "course errors:"
        for (msg, err) in modulestore.get_course_errors(course.id):
            print msg
            print err

        chapters = course.get_children()
        self.assertEquals(len(chapters), 5)

        ch2 = chapters[1]
        self.assertEquals(ch2.url_name, "secret:magic")

        print "Ch2 location: ", ch2.location

        also_ch2 = modulestore.get_item(ch2.location)
        self.assertEquals(ch2, also_ch2)

        print "making sure html loaded"
        loc = course.id.make_usage_key('html', 'secret:toylab')
        html = modulestore.get_item(loc)
        self.assertEquals(html.display_name, "Toy lab")
Exemple #6
0
    def test_cohort_config(self):
        """
        Check that cohort config parsing works right.

        Note: The cohort config on the CourseModule is no longer used.
        See openedx.core.djangoapps.course_groups.models.CourseCohortSettings.
        """
        modulestore = XMLModuleStore(DATA_DIR, source_dirs=['toy'])

        toy_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

        course = modulestore.get_course(toy_id)

        # No config -> False
        self.assertFalse(course.is_cohorted)

        # empty config -> False
        course.cohort_config = {}
        self.assertFalse(course.is_cohorted)

        # false config -> False
        course.cohort_config = {'cohorted': False}
        self.assertFalse(course.is_cohorted)

        # and finally...
        course.cohort_config = {'cohorted': True}
        self.assertTrue(course.is_cohorted)
Exemple #7
0
    def test_colon_in_url_name(self):
        """Ensure that colons in url_names convert to file paths properly"""

        print("Starting import")
        # Not using get_courses because we need the modulestore object too afterward
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['toy'])
        courses = modulestore.get_courses()
        self.assertEquals(len(courses), 1)
        course = courses[0]
        course_id = course.id

        print("course errors:")
        for (msg, err) in modulestore.get_item_errors(course.location):
            print(msg)
            print(err)

        chapters = course.get_children()
        self.assertEquals(len(chapters), 2)

        ch2 = chapters[1]
        self.assertEquals(ch2.url_name, "secret:magic")

        print("Ch2 location: ", ch2.location)

        also_ch2 = modulestore.get_instance(course_id, ch2.location)
        self.assertEquals(ch2, also_ch2)

        print("making sure html loaded")
        cloc = course.location
        loc = Location(cloc.tag, cloc.org, cloc.course, 'html', 'secret:toylab')
        html = modulestore.get_instance(course_id, loc)
        self.assertEquals(html.display_name, "Toy lab")
    def test_unicode(self):
        """Check that courses with unicode characters in filenames and in
        org/course/name import properly. Currently, this means: (a) Having
        files with unicode names does not prevent import; (b) if files are not
        loaded because of unicode filenames, there are appropriate
        exceptions/errors to that effect."""

        print("Starting import")
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['test_unicode'])
        courses = modulestore.get_courses()
        self.assertEquals(len(courses), 1)
        course = courses[0]

        print("course errors:")

        # Expect to find an error/exception about characters in "®esources"
        expect = "InvalidKeyError"
        errors = [
            (msg.encode("utf-8"), err.encode("utf-8"))
            for msg, err
            in modulestore.get_course_errors(course.id)
        ]

        self.assertTrue(any(
            expect in msg or expect in err
            for msg, err in errors
        ))
        chapters = course.get_children()
        self.assertEqual(len(chapters), 4)
    def check_export_roundtrip(self, data_dir, course_dir, mock_get):

        # Patch network calls to retrieve the textbook TOC
        mock_get.return_value.text = dedent("""
            <?xml version="1.0"?><table_of_contents>
            <entry page="5" page_label="ii" name="Table of Contents"/>
            </table_of_contents>
        """).strip()

        root_dir = path(self.temp_dir)
        print("Copying test course to temp dir {0}".format(root_dir))

        data_dir = path(data_dir)
        shutil.copytree(data_dir / course_dir, root_dir / course_dir)

        print("Starting import")
        initial_import = XMLModuleStore(root_dir, course_dirs=[course_dir])

        courses = initial_import.get_courses()
        self.assertEquals(len(courses), 1)
        initial_course = courses[0]

        # export to the same directory--that way things like the custom_tags/ folder
        # will still be there.
        print("Starting export")
        fs = OSFS(root_dir)
        export_fs = fs.makeopendir(course_dir)

        xml = initial_course.export_to_xml(export_fs)
        with export_fs.open('course.xml', 'w') as course_xml:
            course_xml.write(xml)

        print("Starting second import")
        second_import = XMLModuleStore(root_dir, course_dirs=[course_dir])

        courses2 = second_import.get_courses()
        self.assertEquals(len(courses2), 1)
        exported_course = courses2[0]

        print("Checking course equality")

        # HACK: filenames change when changing file formats
        # during imports from old-style courses.  Ignore them.
        strip_filenames(initial_course)
        strip_filenames(exported_course)

        self.assertEquals(initial_course, exported_course)
        self.assertEquals(initial_course.id, exported_course.id)
        course_id = initial_course.id

        print("Checking key equality")
        self.assertEquals(sorted(initial_import.modules[course_id].keys()),
                          sorted(second_import.modules[course_id].keys()))

        print("Checking module equality")
        for location in initial_import.modules[course_id].keys():
            print("Checking", location)
            self.assertEquals(initial_import.modules[course_id][location],
                              second_import.modules[course_id][location])
Exemple #10
0
    def get_course(self, name):
        """Get a test course by directory name.  If there's more than one, error."""
        print("Importing {0}".format(name))

        modulestore = XMLModuleStore(DATA_DIR, course_dirs=[name])
        courses = modulestore.get_courses()
        self.assertEquals(len(courses), 1)
        return courses[0]
Exemple #11
0
 def test_tilde_files_ignored(self, _fake_glob):
     modulestore = XMLModuleStore(DATA_DIR, course_dirs=['tilde'], load_error_modules=False)
     about_location = SlashSeparatedCourseKey('edX', 'tilde', '2012_Fall').make_usage_key(
         'about', 'index',
     )
     about_module = modulestore.get_item(about_location)
     self.assertIn("GREEN", about_module.data)
     self.assertNotIn("RED", about_module.data)
Exemple #12
0
 def test_tilde_files_ignored(self, _fake_glob):
     modulestore = XMLModuleStore(DATA_DIR, source_dirs=['course_ignore'], load_error_modules=False)
     about_location = CourseKey.from_string('edX/course_ignore/2014_Fall').make_usage_key(
         'about', 'index',
     )
     about_module = modulestore.get_item(about_location)
     self.assertIn("GREEN", about_module.data)
     self.assertNotIn("RED", about_module.data)
Exemple #13
0
def import_with_checks(course_dir):
    all_ok = True

    print('Attempting to load "{}"'.format(course_dir))

    course_dir = path(course_dir)
    data_dir = course_dir.dirname()
    source_dirs = [course_dir.basename()]

    # No default class--want to complain if it doesn't find plugins for any
    # module.
    modulestore = XMLModuleStore(
        data_dir,
        default_class=None,
        source_dirs=source_dirs
    )

    def str_of_err(tpl):
        (msg, exc_str) = tpl
        return '{msg}\n{exc}'.format(msg=msg, exc=exc_str)

    courses = modulestore.get_courses()

    n = len(courses)
    if n != 1:
        print('ERROR: Expect exactly 1 course.  Loaded {n}: {lst}'.format(n=n, lst=courses))
        return (False, None)

    course = courses[0]
    errors = modulestore.get_course_errors(course.id)
    if len(errors) != 0:
        all_ok = False
        print(
            '\n' +
            '========================================' +
            'ERRORs during import:' +
            '\n'.join(map(str_of_err, errors)) +
            '========================================' +
            '\n'
        )

    # print course
    validators = (
        traverse_tree,
    )

    print('========================================')
    print('Running validators...')

    for validate in validators:
        print('Running {}'.format(validate.__name__))
        all_ok = validate(course) and all_ok

    if all_ok:
        print('Course passes all checks!')
    else:
        print('Course fails some checks.  See above for errors.')
    return all_ok, course
Exemple #14
0
    def check_export_roundtrip(self, data_dir, course_dir):
        root_dir = path(self.temp_dir)
        print("Copying test course to temp dir {0}".format(root_dir))

        data_dir = path(data_dir)
        shutil.copytree(data_dir / course_dir, root_dir / course_dir)

        print("Starting import")
        initial_import = XMLModuleStore(root_dir, course_dirs=[course_dir])

        courses = initial_import.get_courses()
        self.assertEquals(len(courses), 1)
        initial_course = courses[0]

        # export to the same directory--that way things like the custom_tags/ folder
        # will still be there.
        print("Starting export")
        fs = OSFS(root_dir)
        export_fs = fs.makeopendir(course_dir)

        xml = initial_course.export_to_xml(export_fs)
        with export_fs.open('course.xml', 'w') as course_xml:
            course_xml.write(xml)

        print("Starting second import")
        second_import = XMLModuleStore(root_dir, course_dirs=[course_dir])

        courses2 = second_import.get_courses()
        self.assertEquals(len(courses2), 1)
        exported_course = courses2[0]

        print("Checking course equality")

        # HACK: filenames change when changing file formats
        # during imports from old-style courses.  Ignore them.
        strip_filenames(initial_course)
        strip_filenames(exported_course)

        self.assertEquals(initial_course, exported_course)
        self.assertEquals(initial_course.id, exported_course.id)
        course_id = initial_course.id

        print("Checking key equality")
        self.assertEquals(sorted(initial_import.modules[course_id].keys()),
                          sorted(second_import.modules[course_id].keys()))

        print("Checking module equality")
        for location in initial_import.modules[course_id].keys():
            print("Checking", location)
            if location.category == 'html':
                print(
                    "Skipping html modules--they can't import in"
                    " final form without writing files..."
                )
                continue
            self.assertEquals(initial_import.modules[course_id][location],
                              second_import.modules[course_id][location])
def import_with_checks(course_dir, verbose=True):
    all_ok = True

    print "Attempting to load '{0}'".format(course_dir)

    course_dir = path(course_dir)
    data_dir = course_dir.dirname()
    course_dirs = [course_dir.basename()]

    # No default class--want to complain if it doesn't find plugins for any
    # module.
    modulestore = XMLModuleStore(data_dir,
                   default_class=None,
                   course_dirs=course_dirs)

    def str_of_err(tpl):
        (msg, exc_str) = tpl
        return '{msg}\n{exc}'.format(msg=msg, exc=exc_str)

    courses = modulestore.get_courses()

    n = len(courses)
    if n != 1:
        print 'ERROR: Expect exactly 1 course.  Loaded {n}: {lst}'.format(
            n=n, lst=courses)
        return (False, None)

    course = courses[0]
    errors = modulestore.get_item_errors(course.location)
    if len(errors) != 0:
        all_ok = False
        print '\n'
        print "=" * 40
        print 'ERRORs during import:'
        print '\n'.join(map(str_of_err, errors))
        print "=" * 40
        print '\n'


    #print course
    validators = (
        traverse_tree,
        )

    print "=" * 40
    print "Running validators..."

    for validate in validators:
        print 'Running {0}'.format(validate.__name__)
        all_ok = validate(course) and all_ok


    if all_ok:
        print 'Course passes all checks!'
    else:
        print "Course fails some checks.  See above for errors."
    return all_ok, course
    def get_course(self, name):
        """Get a test course by directory name.  If there's more than one, error."""
        print("Importing {0}".format(name))

        modulestore = XMLModuleStore(
            DATA_DIR, course_dirs=[name], xblock_mixins=(InheritanceMixin,), xblock_select=only_xmodules
        )
        courses = modulestore.get_courses()
        self.assertEquals(len(courses), 1)
        return courses[0]
    def test_static_tabs_import(self):
        """Make sure that the static tabs are imported correctly"""

        modulestore = XMLModuleStore(DATA_DIR, source_dirs=['toy'])

        location_tab_syllabus = Location("edX", "toy", "2012_Fall", "static_tab", "syllabus", None)
        toy_tab_syllabus = modulestore.get_item(location_tab_syllabus)
        self.assertEqual(toy_tab_syllabus.display_name, 'Syllabus')
        self.assertEqual(toy_tab_syllabus.course_staff_only, False)

        location_tab_resources = Location("edX", "toy", "2012_Fall", "static_tab", "resources", None)
        toy_tab_resources = modulestore.get_item(location_tab_resources)
        self.assertEqual(toy_tab_resources.display_name, 'Resources')
        self.assertEqual(toy_tab_resources.course_staff_only, True)
    def test_word_cloud_import(self):
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['word_cloud'])

        course = modulestore.get_courses()[0]
        chapters = course.get_children()
        ch1 = chapters[0]
        sections = ch1.get_children()

        self.assertEqual(len(sections), 1)

        location = course.id.make_usage_key('word_cloud', 'cloud1')
        module = modulestore.get_item(location)
        self.assertEqual(len(module.get_children()), 0)
        self.assertEqual(module.num_inputs, 5)
        self.assertEqual(module.num_top_words, 250)
    def test_graphicslidertool_import(self):
        '''
        Check to see if definition_from_xml in gst_module.py
        works properly.  Pulls data from the graphic_slider_tool directory
        in the test data directory.
        '''
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['graphic_slider_tool'])

        sa_id = SlashSeparatedCourseKey("edX", "gst_test", "2012_Fall")
        location = sa_id.make_usage_key("graphical_slider_tool", "sample_gst")
        gst_sample = modulestore.get_item(location)
        render_string_from_sample_gst_xml = """
        <slider var="a" style="width:400px;float:left;"/>\
<plot style="margin-top:15px;margin-bottom:15px;"/>""".strip()
        self.assertIn(render_string_from_sample_gst_xml, gst_sample.data)
Exemple #20
0
    def test_graphicslidertool_import(self):
        '''
        Check to see if definition_from_xml in gst_module.py
        works properly.  Pulls data from the graphic_slider_tool directory
        in the test data directory.
        '''
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['graphic_slider_tool'])

        sa_id = "edX/gst_test/2012_Fall"
        location = Location(["i4x", "edX", "gst_test", "graphical_slider_tool", "sample_gst"])
        gst_sample = modulestore.get_instance(sa_id, location)
        render_string_from_sample_gst_xml = """
        <slider var="a" style="width:400px;float:left;"/>\
<plot style="margin-top:15px;margin-bottom:15px;"/>""".strip()
        self.assertEqual(gst_sample.render, render_string_from_sample_gst_xml)
    def test_word_cloud_import(self):
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=["word_cloud"])

        course = modulestore.get_courses()[0]
        chapters = course.get_children()
        ch1 = chapters[0]
        sections = ch1.get_children()

        self.assertEqual(len(sections), 1)

        location = course.location
        location = Location(location.tag, location.org, location.course, "word_cloud", "cloud1")
        module = modulestore.get_instance(course.id, location)
        self.assertEqual(len(module.get_children()), 0)
        self.assertEqual(module.num_inputs, 5)
        self.assertEqual(module.num_top_words, 250)
    def test_definition_loading(self):
        """When two courses share the same org and course name and
        both have a module with the same url_name, the definitions shouldn't clash.

        TODO (vshnayder): once we have a CMS, this shouldn't
        happen--locations should uniquely name definitions.  But in
        our imperfect XML world, it can (and likely will) happen."""

        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'two_toys'])

        location = Location("edX", "toy", "2012_Fall", "video", "Welcome", None)
        toy_video = modulestore.get_item(location)
        location_two = Location("edX", "toy", "TT_2012_Fall", "video", "Welcome", None)
        two_toy_video = modulestore.get_item(location_two)
        self.assertEqual(toy_video.youtube_id_1_0, "p2Q6BrNhdh8")
        self.assertEqual(two_toy_video.youtube_id_1_0, "p2Q6BrNhdh9")
Exemple #23
0
    def test_unicode_chars_in_xml_content(self):
        # edX/full/6.002_Spring_2012 has non-ASCII chars, and during
        # uniquification of names, would raise a UnicodeError. It no longer does.

        # Ensure that there really is a non-ASCII character in the course.
        with open(os.path.join(DATA_DIR, "toy/sequential/vertical_sequential.xml")) as xmlf:
            xml = xmlf.read()
            with self.assertRaises(UnicodeDecodeError):
                xml.decode('ascii')

        # Load the course, but don't make error modules.  This will succeed,
        # but will record the errors.
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['toy'], load_error_modules=False)

        # Look up the errors during load. There should be none.
        errors = modulestore.get_course_errors(SlashSeparatedCourseKey("edX", "toy", "2012_Fall"))
        assert errors == []
Exemple #24
0
    def test_definition_loading(self):
        """When two courses share the same org and course name and
        both have a module with the same url_name, the definitions shouldn't clash.

        TODO (vshnayder): once we have a CMS, this shouldn't
        happen--locations should uniquely name definitions.  But in
        our imperfect XML world, it can (and likely will) happen."""

        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'two_toys'])

        toy_id = "edX/toy/2012_Fall"
        two_toy_id = "edX/toy/TT_2012_Fall"

        location = Location(["i4x", "edX", "toy", "video", "Welcome"])
        toy_video = modulestore.get_instance(toy_id, location)
        two_toy_video = modulestore.get_instance(two_toy_id, location)
        self.assertEqual(etree.fromstring(toy_video.data).get('youtube'), "1.0:p2Q6BrNhdh8")
        self.assertEqual(etree.fromstring(two_toy_video.data).get('youtube'), "1.0:p2Q6BrNhdh9")
Exemple #25
0
    def test_unicode_chars_in_xml_content(self):
        # edX/full/6.002_Spring_2012 has non-ASCII chars, and during
        # uniquification of names, would raise a UnicodeError. It no longer does.

        # Ensure that there really is a non-ASCII character in the course.
        with open(os.path.join(DATA_DIR, "full/sequential/Administrivia_and_Circuit_Elements.xml")) as xmlf:
            xml = xmlf.read()
            with assert_raises(UnicodeDecodeError):
                xml.decode('ascii')

        # Load the course, but don't make error modules.  This will succeed,
        # but will record the errors.
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['full'], load_error_modules=False)

        # Look up the errors during load. There should be none.
        location = CourseDescriptor.id_to_location("edX/full/6.002_Spring_2012")
        errors = modulestore.get_item_errors(location)
        assert errors == []
class DummyModulestore(object):
    """
    A mixin that allows test classes to have convenience functions to get a module given a location
    """
    get_test_system = get_test_system()

    def setup_modulestore(self, name):
        self.modulestore = XMLModuleStore(DATA_DIR, course_dirs=[name])

    def get_course(self, name):
        """Get a test course by directory name.  If there's more than one, error."""
        courses = self.modulestore.get_courses()
        return courses[0]

    def get_module_from_location(self, location, course):
        course = self.get_course(course)
        if not isinstance(location, Location):
            location = Location(location)
        descriptor = self.modulestore.get_instance(course.id, location, depth=None)
        return descriptor.xmodule(self.test_system)
    def test_url_name_mangling(self):
        """
        Make sure that url_names are only mangled once.
        """

        modulestore = XMLModuleStore(DATA_DIR, source_dirs=['toy'])

        toy_id = CourseKey.from_string('edX/toy/2012_Fall')

        course = modulestore.get_course(toy_id)
        chapters = course.get_children()
        ch1 = chapters[0]
        sections = ch1.get_children()

        self.assertEqual(len(sections), 4)

        for i in (2, 3):
            video = sections[i]
            # Name should be 'video_{hash}'
            print("video {0} url_name: {1}".format(i, video.url_name))
            self.assertEqual(len(video.url_name), len('video_') + 12)
class DummyModulestore(object):
    """
    A mixin that allows test classes to have convenience functions to get a module given a location
    """

    def get_module_system(self, descriptor):
        raise NotImplementedError("Sub-tests must specify how to generate a module-system")

    def setup_modulestore(self, name):
        self.modulestore = XMLModuleStore(DATA_DIR, course_dirs=[name])

    def get_course(self, _):
        """Get a test course by directory name.  If there's more than one, error."""
        courses = self.modulestore.get_courses()
        return courses[0]

    def get_module_from_location(self, location, course):
        course = self.get_course(course)
        if not isinstance(location, Location):
            location = Location(location)
        descriptor = self.modulestore.get_instance(course.id, location, depth=None)
        descriptor.xmodule_runtime = self.get_module_system(descriptor)
        return descriptor
Exemple #29
0
    def test_cohort_config(self):
        """
        Check that cohort config parsing works right.
        """
        modulestore = XMLModuleStore(DATA_DIR, source_dirs=['toy'])

        toy_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

        course = modulestore.get_course(toy_id)

        # No config -> False
        self.assertFalse(course.is_cohorted)

        # empty config -> False
        course.cohort_config = {}
        self.assertFalse(course.is_cohorted)

        # false config -> False
        course.cohort_config = {'cohorted': False}
        self.assertFalse(course.is_cohorted)

        # and finally...
        course.cohort_config = {'cohorted': True}
        self.assertTrue(course.is_cohorted)
Exemple #30
0
    def test_cohort_config(self):
        """
        Check that cohort config parsing works right.
        """
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['toy'])

        toy_id = "edX/toy/2012_Fall"

        course = modulestore.get_course(toy_id)

        # No config -> False
        self.assertFalse(course.is_cohorted)

        # empty config -> False
        course.cohort_config = {}
        self.assertFalse(course.is_cohorted)

        # false config -> False
        course.cohort_config = {'cohorted': False}
        self.assertFalse(course.is_cohorted)

        # and finally...
        course.cohort_config = {'cohorted': True}
        self.assertTrue(course.is_cohorted)
    def build(self, contentstore=None, course_ids=None):
        """
        A contextmanager that returns an isolated xml modulestore

        Args:
            contentstore: The contentstore that this modulestore should use to store
                all of its assets.
        """
        modulestore = XMLModuleStore(
            DATA_DIR,
            course_ids=course_ids,
            default_class='xmodule.hidden_module.HiddenDescriptor',
            xblock_mixins=XBLOCK_MIXINS,
        )

        yield modulestore
Exemple #32
0
    def __init__(self, load_error_modules):

        xmlstore = XMLModuleStore("data_dir", source_dirs=[],
                                  load_error_modules=load_error_modules)
        course_id = SlashSeparatedCourseKey(ORG, COURSE, 'test_run')
        course_dir = "test_dir"
        error_tracker = Mock()

        super(DummySystem, self).__init__(
            xmlstore=xmlstore,
            course_id=course_id,
            course_dir=course_dir,
            error_tracker=error_tracker,
            load_error_modules=load_error_modules,
            field_data=KvsFieldData(DictKeyValueStore()),
        )
Exemple #33
0
    def check_export_roundtrip(self, data_dir, course_dir):
        root_dir = path(self.temp_dir)
        print("Copying test course to temp dir {0}".format(root_dir))

        data_dir = path(data_dir)
        shutil.copytree(data_dir / course_dir, root_dir / course_dir)

        print("Starting import")
        initial_import = XMLModuleStore(root_dir, course_dirs=[course_dir])

        courses = initial_import.get_courses()
        self.assertEquals(len(courses), 1)
        initial_course = courses[0]

        # export to the same directory--that way things like the custom_tags/ folder
        # will still be there.
        print("Starting export")
        fs = OSFS(root_dir)
        export_fs = fs.makeopendir(course_dir)

        xml = initial_course.export_to_xml(export_fs)
        with export_fs.open('course.xml', 'w') as course_xml:
            course_xml.write(xml)

        print("Starting second import")
        second_import = XMLModuleStore(root_dir, course_dirs=[course_dir])

        courses2 = second_import.get_courses()
        self.assertEquals(len(courses2), 1)
        exported_course = courses2[0]

        print("Checking course equality")

        # HACK: filenames change when changing file formats
        # during imports from old-style courses.  Ignore them.
        strip_filenames(initial_course)
        strip_filenames(exported_course)

        self.assertEquals(initial_course, exported_course)
        self.assertEquals(initial_course.id, exported_course.id)
        course_id = initial_course.id

        print("Checking key equality")
        self.assertEquals(sorted(initial_import.modules[course_id].keys()),
                          sorted(second_import.modules[course_id].keys()))

        print("Checking module equality")
        for location in initial_import.modules[course_id].keys():
            print("Checking", location)
            if location.category == 'html':
                print("Skipping html modules--they can't import in"
                      " final form without writing files...")
                continue
            self.assertEquals(initial_import.modules[course_id][location],
                              second_import.modules[course_id][location])
    def __init__(self, load_error_modules, course_id=None):

        xmlstore = XMLModuleStore("data_dir", source_dirs=[],
                                  load_error_modules=load_error_modules)
        if course_id is None:
            course_id = CourseKey.from_string('/'.join([ORG, COURSE, 'test_run']))
        course_dir = "test_dir"
        error_tracker = Mock()

        super(DummySystem, self).__init__(  # lint-amnesty, pylint: disable=super-with-arguments
            xmlstore=xmlstore,
            course_id=course_id,
            course_dir=course_dir,
            error_tracker=error_tracker,
            load_error_modules=load_error_modules,
            field_data=KvsFieldData(DictKeyValueStore()),
        )
Exemple #35
0
    def __init__(self, load_error_modules, course_id=None):

        xmlstore = XMLModuleStore("data_dir", source_dirs=[],
                                  load_error_modules=load_error_modules)
        if course_id is None:
            course_id = CourseKey.from_string('/'.join([ORG, COURSE, 'test_run']))
        course_dir = "test_dir"
        error_tracker = Mock()

        super().__init__(
            xmlstore=xmlstore,
            course_id=course_id,
            course_dir=course_dir,
            error_tracker=error_tracker,
            load_error_modules=load_error_modules,
            field_data=KvsFieldData(DictKeyValueStore()),
        )
Exemple #36
0
    def test_branch_setting(self):
        """
        Test the branch setting context manager
        """
        store = XMLModuleStore(DATA_DIR, source_dirs=['toy'])
        course = store.get_courses()[0]

        # XML store allows published_only branch setting
        with store.branch_setting(ModuleStoreEnum.Branch.published_only, course.id):
            store.get_item(course.location)

        # XML store does NOT allow draft_preferred branch setting
        with self.assertRaises(ValueError):
            with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, course.id):
                # verify that the above context manager raises a ValueError
                pass  # pragma: no cover
Exemple #37
0
    def __init__(self, load_error_modules):

        xmlstore = XMLModuleStore("data_dir", course_dirs=[], load_error_modules=load_error_modules)
        course_id = "/".join([ORG, COURSE, 'test_run'])
        course_dir = "test_dir"
        policy = {}
        error_tracker = Mock()
        parent_tracker = Mock()

        super(DummySystem, self).__init__(
            xmlstore,
            course_id,
            course_dir,
            policy,
            error_tracker,
            parent_tracker,
            load_error_modules=load_error_modules,
        )
    def __init__(self, load_error_modules):

        xmlstore = XMLModuleStore("data_dir", course_dirs=[],
                                  load_error_modules=load_error_modules)
        course_id = "/".join([ORG, COURSE, 'test_run'])
        course_dir = "test_dir"
        error_tracker = Mock()
        parent_tracker = Mock()

        super(DummySystem, self).__init__(
            xmlstore=xmlstore,
            course_id=course_id,
            course_dir=course_dir,
            error_tracker=error_tracker,
            parent_tracker=parent_tracker,
            load_error_modules=load_error_modules,
            field_data=KvsFieldData(DictKeyValueStore()),
            id_reader=LocationReader(),
        )
Exemple #39
0
    def __init__(self, load_error_modules, library=False):

        if library:
            xmlstore = LibraryXMLModuleStore("data_dir", source_dirs=[], load_error_modules=load_error_modules)
        else:
            xmlstore = XMLModuleStore("data_dir", source_dirs=[], load_error_modules=load_error_modules)
        course_id = SlashSeparatedCourseKey(ORG, COURSE, 'test_run')
        course_dir = "test_dir"
        error_tracker = Mock()

        super(DummySystem, self).__init__(
            xmlstore=xmlstore,
            course_id=course_id,
            course_dir=course_dir,
            error_tracker=error_tracker,
            load_error_modules=load_error_modules,
            mixins=(InheritanceMixin, XModuleMixin),
            field_data=KvsFieldData(DictKeyValueStore()),
        )
Exemple #40
0
    def __init__(self, load_error_modules, library=False):

        if library:
            xmlstore = LibraryXMLModuleStore("data_dir", source_dirs=[], load_error_modules=load_error_modules)
        else:
            xmlstore = XMLModuleStore("data_dir", source_dirs=[], load_error_modules=load_error_modules)
        course_id = CourseKey.from_string('/'.join([ORG, COURSE, RUN]))
        course_dir = "test_dir"
        error_tracker = Mock()

        super(DummySystem, self).__init__(  # lint-amnesty, pylint: disable=super-with-arguments
            xmlstore=xmlstore,
            course_id=course_id,
            course_dir=course_dir,
            error_tracker=error_tracker,
            load_error_modules=load_error_modules,
            mixins=(InheritanceMixin, XModuleMixin),
            field_data=KvsFieldData(DictKeyValueStore()),
        )
Exemple #41
0
    def test_get_courses_for_wiki(self):
        """
        Test the get_courses_for_wiki method
        """
        store = XMLModuleStore(DATA_DIR, source_dirs=['toy', 'simple'])
        for course in store.get_courses():
            course_locations = store.get_courses_for_wiki(course.wiki_slug)
            self.assertEqual(len(course_locations), 1)
            self.assertIn(course.location.course_key, course_locations)

        course_locations = store.get_courses_for_wiki('no_such_wiki')
        self.assertEqual(len(course_locations), 0)

        # now set toy course to share the wiki with simple course
        toy_course = store.get_course(CourseKey.from_string('edX/toy/2012_Fall'))
        toy_course.wiki_slug = 'simple'

        course_locations = store.get_courses_for_wiki('toy')
        self.assertEqual(len(course_locations), 0)

        course_locations = store.get_courses_for_wiki('simple')
        self.assertEqual(len(course_locations), 2)
        for course_number in ['toy', 'simple']:
            self.assertIn(CourseKey.from_string('/'.join(['edX', course_number, '2012_Fall'])), course_locations)
Exemple #42
0
    def test_get_courses_for_wiki(self):
        """
        Test the get_courses_for_wiki method
        """
        store = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'simple'])
        for course in store.get_courses():
            course_locations = store.get_courses_for_wiki(course.wiki_slug)
            self.assertEqual(len(course_locations), 1)
            self.assertIn(course.location, course_locations)

        course_locations = store.get_courses_for_wiki('no_such_wiki')
        self.assertEqual(len(course_locations), 0)

        # now set toy course to share the wiki with simple course
        toy_course = store.get_course(SlashSeparatedCourseKey('edX', 'toy', '2012_Fall'))
        toy_course.wiki_slug = 'simple'

        course_locations = store.get_courses_for_wiki('toy')
        self.assertEqual(len(course_locations), 0)

        course_locations = store.get_courses_for_wiki('simple')
        self.assertEqual(len(course_locations), 2)
        for course_number in ['toy', 'simple']:
            self.assertIn(Location('edX', course_number, '2012_Fall', 'course', '2012_Fall'), course_locations)
Exemple #43
0
    def handle(self, *args, **options):
        if len(args) != 1:
            raise CommandError(u'Must called with arguments: {}'.format(
                self.args))

        xml_module_store = XMLModuleStore(
            data_dir=settings.DATA_DIR,
            default_class='xmodule.hidden_module.HiddenDescriptor',
            load_error_modules=True,
            xblock_mixins=settings.XBLOCK_MIXINS,
            xblock_select=settings.XBLOCK_SELECT_FUNCTION,
        )

        export_dir = path(args[0])

        for course_id, course_modules in six.iteritems(
                xml_module_store.modules):
            course_path = course_id.replace('/', '_')
            for location, descriptor in six.iteritems(course_modules):
                location_path = text_type(location).replace('/', '_')
                data = {}
                for field_name, field in six.iteritems(descriptor.fields):
                    try:
                        data[field_name] = field.read_json(descriptor)
                    except Exception as exc:  # pylint: disable=broad-except
                        data[field_name] = {
                            '$type':
                            str(type(exc)),
                            '$value':
                            descriptor._field_data.get(descriptor, field_name)  # pylint: disable=protected-access
                        }

                outdir = export_dir / course_path
                outdir.makedirs_p()
                with open(outdir / location_path + '.json', 'w') as outfile:
                    json.dump(data, outfile, sort_keys=True, indent=4)
                    print('', file=outfile)
Exemple #44
0
 def test_xml_modulestore_type(self):
     store = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'simple'])
     self.assertEqual(store.get_modulestore_type(),
                      ModuleStoreEnum.Type.xml)
Exemple #45
0
 def test_xml_modulestore_type(self):
     store = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'simple'])
     assert_equals(store.get_modulestore_type('foo/bar/baz'), XML_MODULESTORE_TYPE)
Exemple #46
0
 def test_xml_modulestore_type(self):
     store = XMLModuleStore(DATA_DIR, source_dirs=[])
     self.assertEqual(store.get_modulestore_type(),
                      ModuleStoreEnum.Type.xml)
class ConditionalBlockXmlTest(unittest.TestCase):
    """
    Make sure ConditionalBlock works, by loading data in from an XML-defined course.
    """
    def setUp(self):
        super().setUp()
        self.test_system = get_test_system()
        self.modulestore = XMLModuleStore(DATA_DIR,
                                          source_dirs=['conditional_and_poll'])
        courses = self.modulestore.get_courses()
        assert len(courses) == 1
        self.course = courses[0]

    def get_module_for_location(self, location):
        descriptor = self.modulestore.get_item(location, depth=None)
        return self.test_system.get_module(descriptor)

    @patch('xmodule.x_module.descriptor_global_local_resource_url')
    @patch.dict(settings.FEATURES, {'ENABLE_EDXNOTES': False})
    def test_conditional_module(self, _):
        """Make sure that conditional module works"""
        # edx - HarvardX
        # cond_test - ER22x
        location = BlockUsageLocator(CourseLocator("HarvardX",
                                                   "ER22x",
                                                   "2013_Spring",
                                                   deprecated=True),
                                     "conditional",
                                     "condone",
                                     deprecated=True)

        module = self.get_module_for_location(location)
        html = module.render(STUDENT_VIEW).content
        mako_service = module.xmodule_runtime.service(module, 'mako')
        html_expect = mako_service.render_template(
            'conditional_ajax.html',
            {
                # Test ajax url is just usage-id / handler_name
                'ajax_url': f'{str(location)}/xmodule_handler',
                'element_id': 'i4x-HarvardX-ER22x-conditional-condone',
                'depends': 'i4x-HarvardX-ER22x-problem-choiceprob'
            })
        assert html == html_expect

        ajax = json.loads(module.handle_ajax('', ''))
        fragments = ajax['fragments']
        assert not any(
            ('This is a secret' in item['content']) for item in fragments)

        # Now change state of the capa problem to make it completed
        inner_module = self.get_module_for_location(
            location.replace(category="problem", name='choiceprob'))
        inner_module.attempts = 1
        # Save our modifications to the underlying KeyValueStore so they can be persisted
        inner_module.save()

        ajax = json.loads(module.handle_ajax('', ''))
        fragments = ajax['fragments']
        assert any(
            ('This is a secret' in item['content']) for item in fragments)

    def test_conditional_module_with_empty_sources_list(self):
        """
        If a ConditionalBlock is initialized with an empty sources_list, we assert that the sources_list is set
        via generating UsageKeys from the values in xml_attributes['sources']
        """
        dummy_system = Mock()
        dummy_location = BlockUsageLocator(
            CourseLocator("edX", "conditional_test", "test_run"),
            "conditional", "SampleConditional")
        dummy_scope_ids = ScopeIds(None, None, dummy_location, dummy_location)
        dummy_field_data = DictFieldData({
            'data': '<conditional/>',
            'xml_attributes': {
                'sources': 'i4x://HarvardX/ER22x/poll_question/T15_poll'
            },
            'children': None,
        })
        conditional = ConditionalBlock(
            dummy_system,
            dummy_field_data,
            dummy_scope_ids,
        )

        new_run = conditional.location.course_key.run  # lint-amnesty, pylint: disable=unused-variable
        assert conditional.sources_list[0] == BlockUsageLocator.from_string(conditional.xml_attributes['sources'])\
            .replace(run=dummy_location.course_key.run)

    def test_conditional_module_parse_sources(self):
        dummy_system = Mock()
        dummy_location = BlockUsageLocator(
            CourseLocator("edX", "conditional_test", "test_run"),
            "conditional", "SampleConditional")
        dummy_scope_ids = ScopeIds(None, None, dummy_location, dummy_location)
        dummy_field_data = DictFieldData({
            'data': '<conditional/>',
            'xml_attributes': {
                'sources':
                'i4x://HarvardX/ER22x/poll_question/T15_poll;i4x://HarvardX/ER22x/poll_question/T16_poll'
            },  # lint-amnesty, pylint: disable=line-too-long
            'children': None,
        })
        conditional = ConditionalBlock(
            dummy_system,
            dummy_field_data,
            dummy_scope_ids,
        )
        assert conditional.parse_sources(conditional.xml_attributes) == [
            'i4x://HarvardX/ER22x/poll_question/T15_poll',
            'i4x://HarvardX/ER22x/poll_question/T16_poll'
        ]

    def test_conditional_module_parse_attr_values(self):
        root = '<conditional attempted="false"></conditional>'
        xml_object = etree.XML(root)
        definition = ConditionalBlock.definition_from_xml(xml_object,
                                                          Mock())[0]
        expected_definition = {
            'show_tag_list': [],
            'conditional_attr': 'attempted',
            'conditional_value': 'false',
            'conditional_message': ''
        }

        assert definition == expected_definition

    def test_presence_attributes_in_xml_attributes(self):
        modules = ConditionalFactory.create(self.test_system)
        modules['cond_module'].save()
        modules['cond_module'].definition_to_xml(Mock())
        expected_xml_attributes = {
            'attempted': 'true',
            'message':
            'You must complete {link} before you can access this unit.',
            'sources': ''
        }
        self.assertDictEqual(modules['cond_module'].xml_attributes,
                             expected_xml_attributes)
Exemple #48
0
    def test_export_roundtrip(self, course_dir, mock_get):

        # Patch network calls to retrieve the textbook TOC
        mock_get.return_value.text = dedent("""
            <?xml version="1.0"?><table_of_contents>
            <entry page="5" page_label="ii" name="Table of Contents"/>
            </table_of_contents>
        """).strip()

        root_dir = path(self.temp_dir)
        print "Copying test course to temp dir {0}".format(root_dir)

        data_dir = path(DATA_DIR)
        shutil.copytree(data_dir / course_dir, root_dir / course_dir)

        print "Starting import"
        initial_import = XMLModuleStore(root_dir,
                                        source_dirs=[course_dir],
                                        xblock_mixins=(XModuleMixin, ))

        courses = initial_import.get_courses()
        self.assertEquals(len(courses), 1)
        initial_course = courses[0]

        # export to the same directory--that way things like the custom_tags/ folder
        # will still be there.
        print "Starting export"
        file_system = OSFS(root_dir)
        initial_course.runtime.export_fs = file_system.makedir(course_dir,
                                                               recreate=True)
        root = lxml.etree.Element('root')

        initial_course.add_xml_to_node(root)
        with initial_course.runtime.export_fs.open('course.xml',
                                                   'wb') as course_xml:
            lxml.etree.ElementTree(root).write(course_xml, encoding='utf-8')

        print "Starting second import"
        second_import = XMLModuleStore(root_dir,
                                       source_dirs=[course_dir],
                                       xblock_mixins=(XModuleMixin, ))

        courses2 = second_import.get_courses()
        self.assertEquals(len(courses2), 1)
        exported_course = courses2[0]

        print "Checking course equality"

        # HACK: filenames change when changing file formats
        # during imports from old-style courses.  Ignore them.
        strip_filenames(initial_course)
        strip_filenames(exported_course)

        self.assertTrue(blocks_are_equivalent(initial_course, exported_course))
        self.assertEquals(initial_course.id, exported_course.id)
        course_id = initial_course.id

        print "Checking key equality"
        self.assertItemsEqual(initial_import.modules[course_id].keys(),
                              second_import.modules[course_id].keys())

        print "Checking module equality"
        for location in initial_import.modules[course_id].keys():
            print("Checking", location)
            self.assertTrue(
                blocks_are_equivalent(
                    initial_import.modules[course_id][location],
                    second_import.modules[course_id][location]))
Exemple #49
0
 def setup_modulestore(self, name):
     self.modulestore = XMLModuleStore(DATA_DIR, course_dirs=[name])
def perform_xlint(
        data_dir, source_dirs,
        default_class='xmodule.raw_module.RawDescriptor',
        load_error_modules=True,
        xblock_mixins=(LocationMixin, XModuleMixin)):
    err_cnt = 0
    warn_cnt = 0

    module_store = XMLModuleStore(
        data_dir,
        default_class=default_class,
        source_dirs=source_dirs,
        load_error_modules=load_error_modules,
        xblock_mixins=xblock_mixins
    )

    # check all data source path information
    for course_dir in source_dirs:
        _err_cnt, _warn_cnt = validate_data_source_paths(path(data_dir), course_dir)
        err_cnt += _err_cnt
        warn_cnt += _warn_cnt

    # first count all errors and warnings as part of the XMLModuleStore import
    for err_log in module_store._course_errors.itervalues():  # pylint: disable=protected-access
        for err_log_entry in err_log.errors:
            msg = err_log_entry[0]
            if msg.startswith('ERROR:'):
                err_cnt += 1
            else:
                warn_cnt += 1

    # then count outright all courses that failed to load at all
    for err_log in module_store.errored_courses.itervalues():
        for err_log_entry in err_log.errors:
            msg = err_log_entry[0]
            print(msg)
            if msg.startswith('ERROR:'):
                err_cnt += 1
            else:
                warn_cnt += 1

    for course_id in module_store.modules.keys():
        # constrain that courses only have 'chapter' children
        err_cnt += validate_category_hierarchy(
            module_store, course_id, "course", "chapter"
        )
        # constrain that chapters only have 'sequentials'
        err_cnt += validate_category_hierarchy(
            module_store, course_id, "chapter", "sequential"
        )
        # constrain that sequentials only have 'verticals'
        err_cnt += validate_category_hierarchy(
            module_store, course_id, "sequential", "vertical"
        )
        # validate the course policy overrides any defaults
        # which have changed over time
        warn_cnt += validate_course_policy(module_store, course_id)
        # don't allow metadata on verticals, since we can't edit them in studio
        err_cnt += validate_no_non_editable_metadata(
            module_store, course_id, "vertical"
        )
        # don't allow metadata on chapters, since we can't edit them in studio
        err_cnt += validate_no_non_editable_metadata(
            module_store, course_id, "chapter"
        )
        # don't allow metadata on sequences that we can't edit
        err_cnt += validate_no_non_editable_metadata(
            module_store, course_id, "sequential"
        )

        # check for a presence of a course marketing video
        if not module_store.has_item(course_id.make_usage_key('about', 'video')):
            print(
                "WARN: Missing course marketing video. It is recommended "
                "that every course have a marketing video."
            )
            warn_cnt += 1

    print("\n")
    print("------------------------------------------")
    print("VALIDATION SUMMARY: {err} Errors   {warn} Warnings".format(
        err=err_cnt, warn=warn_cnt)
    )

    if err_cnt > 0:
        print(
            "This course is not suitable for importing. Please fix courseware "
            "according to specifications before importing."
        )
    elif warn_cnt > 0:
        print(
            "This course can be imported, but some errors may occur "
            "during the run of the course. It is recommend that you fix "
            "your courseware before importing"
        )
    else:
        print("This course can be imported successfully.")

    return err_cnt