Example #1
0
    def setUp(self):
        """
        Initial data setup
        """
        super(TestSubsectionGating, self).setUp()  # lint-amnesty, pylint: disable=super-with-arguments

        # Enable subsection gating for the test course
        self.course.enable_subsection_gating = True
        self.save_course()

        # create a chapter
        self.chapter = ItemFactory.create(parent_location=self.course.location,
                                          category='chapter',
                                          display_name='untitled chapter')

        # create 2 sequentials
        self.seq1 = ItemFactory.create(parent_location=self.chapter.location,
                                       category='sequential',
                                       display_name='untitled sequential 1')
        self.seq1_url = reverse_usage_url('xblock_handler', self.seq1.location)

        self.seq2 = ItemFactory.create(parent_location=self.chapter.location,
                                       category='sequential',
                                       display_name='untitled sequential 2')
        self.seq2_url = reverse_usage_url('xblock_handler', self.seq2.location)
Example #2
0
    def test_can_get_usage_info_when_special_characters_are_used(self):
        """
        Test if group configurations json updated successfully when special
         characters are being used in content experiment
        """
        self._add_user_partitions(count=1)
        __, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0', special_characters=u"JOSÉ ANDRÉS")

        actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course, )

        expected = [{
            'id': 0,
            'name': 'Name 0',
            'scheme': 'random',
            'description': 'Description 0',
            'version': UserPartition.VERSION,
            'groups': [
                {'id': 0, 'name': 'Group A', 'version': 1},
                {'id': 1, 'name': 'Group B', 'version': 1},
                {'id': 2, 'name': 'Group C', 'version': 1},
            ],
            'usage': [{
                'url': reverse_usage_url("container_handler", split_test.location),
                'label': u"Test Unit 0 / Test Content Experiment 0JOSÉ ANDRÉS",
                'validation': None,
            }],
            'parameters': {},
            'active': True,
        }]

        self.assertEqual(actual, expected)
    def _get_usage_dict(course, unit, item, scheme_name=None):
        """
        Get usage info for unit/module.
        """
        parent_unit = get_parent_unit(item)

        if unit == parent_unit and not item.has_children:
            # Display the topmost unit page if
            # the item is a child of the topmost unit and doesn't have its own children.
            unit_for_url = unit
        elif (not parent_unit and unit.get_parent()) or (unit == parent_unit and item.has_children):
            # Display the item's page rather than the unit page if
            # the item is one level below the topmost unit and has children, or
            # the item itself *is* the topmost unit (and thus does not have a parent unit, but is not an orphan).
            unit_for_url = item
        else:
            # If the item is nested deeper than two levels (the topmost unit > vertical > ... > item)
            # display the page for the nested vertical element.
            parent = item.get_parent()
            nested_vertical = item
            while parent != parent_unit:
                nested_vertical = parent
                parent = parent.get_parent()
            unit_for_url = nested_vertical

        unit_url = reverse_usage_url(
            'container_handler',
            course.location.course_key.make_usage_key(unit_for_url.location.block_type, unit_for_url.location.block_id)
        )

        usage_dict = {'label': u"{} / {}".format(unit.display_name, item.display_name), 'url': unit_url}
        if scheme_name == RANDOM_SCHEME:
            validation_summary = item.general_validation_message()
            usage_dict.update({'validation': validation_summary.to_json() if validation_summary else None})
        return usage_dict
    def test_preview_conditional_module_children_context(
            self, mock_is_condition_satisfied):
        """
        Tests that when empty context is pass to children of ConditionalBlock it will not raise KeyError.
        """
        mock_is_condition_satisfied.return_value = True
        client = Client()
        client.login(username=self.user.username, password=self.user_password)

        with self.store.default_store(ModuleStoreEnum.Type.split):
            course = CourseFactory.create()

            conditional_block = ItemFactory.create(
                parent_location=course.location, category="conditional")

            # child conditional_block
            ItemFactory.create(parent_location=conditional_block.location,
                               category="conditional")

            url = reverse_usage_url(
                'preview_handler',
                conditional_block.location,
                kwargs={'handler': 'xmodule_handler/conditional_get'})
            response = client.post(url)
            self.assertEqual(response.status_code, 200)
Example #5
0
    def test_post_course_update(self):
        """
        Test that a user can successfully post on course updates and handouts of a course
        """
        self.post_course_update()

        updates_location = self.course.id.make_usage_key(
            'course_info', 'updates')
        self.assertTrue(isinstance(updates_location, UsageKey))
        self.assertEqual(updates_location.block_id, u'updates')

        # check posting on handouts
        handouts_location = self.course.id.make_usage_key(
            'course_info', 'handouts')
        course_handouts_url = reverse_usage_url('xblock_handler',
                                                handouts_location)

        content = u"Sample handout"
        payload = {'data': content}
        resp = self.client.ajax_post(course_handouts_url, payload)

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

        payload = json.loads(resp.content.decode('utf-8'))
        self.assertHTMLEqual(payload['data'], content)
Example #6
0
def create_export_tarball(course_module, course_key, context, status=None):
    """
    Generates the export tarball, or returns None if there was an error.

    Updates the context with any error information if applicable.
    """
    name = course_module.url_name
    export_file = NamedTemporaryFile(prefix=name + '.', suffix=".tar.gz")
    root_dir = path(mkdtemp())

    try:
        if isinstance(course_key, LibraryLocator):
            export_library_to_xml(modulestore(), contentstore(), course_key, root_dir, name)
        else:
            export_course_to_xml(modulestore(), contentstore(), course_module.id, root_dir, name)

        if status:
            status.set_state('Compressing')
            status.increment_completed_steps()
        LOGGER.debug('tar file being generated at %s', export_file.name)
        with tarfile.open(name=export_file.name, mode='w:gz') as tar_file:
            tar_file.add(root_dir / name, arcname=name)

    except SerializationError as exc:
        LOGGER.exception('There was an error exporting %s', course_key, exc_info=True)
        parent = None
        try:
            failed_item = modulestore().get_item(exc.location)
            parent_loc = modulestore().get_parent_location(failed_item.location)

            if parent_loc is not None:
                parent = modulestore().get_item(parent_loc)
        except:  # pylint: disable=bare-except
            # if we have a nested exception, then we'll show the more generic error message
            pass

        context.update({
            'in_err': True,
            'raw_err_msg': str(exc),
            'edit_unit_url': reverse_usage_url("container_handler", parent.location) if parent else "",
        })
        if status:
            status.fail(json.dumps({'raw_error_msg': context['raw_err_msg'],
                                    'edit_unit_url': context['edit_unit_url']}))
        raise
    except Exception as exc:
        LOGGER.exception('There was an error exporting %s', course_key, exc_info=True)
        context.update({
            'in_err': True,
            'edit_unit_url': None,
            'raw_err_msg': str(exc)})
        if status:
            status.fail(json.dumps({'raw_error_msg': context['raw_err_msg']}))
        raise
    finally:
        if os.path.exists(root_dir / name):
            shutil.rmtree(root_dir / name)

    return export_file
 def _get_discussion_enabled_status(self, usage_key, client=None):
     """
     Issue a GET request to fetch value of discussion_enabled flag of xblock represented by param:usage_key
     """
     client = client if client is not None else self.client
     url = reverse_usage_url("xblock_handler", usage_key)
     resp = client.get(url, HTTP_ACCEPT="application/json")
     return resp
 def _update_item(self, usage_key, metadata):
     """
     Helper method: Uses the REST API to update the fields of an XBlock.
     This will result in the XBlock's editor_saved() method being called.
     """
     update_url = reverse_usage_url("xblock_handler", usage_key)
     return self.client.ajax_post(update_url, data={
         'metadata': metadata,
     })
Example #9
0
 def test_delete_item_signal_handler_called(self, mock_remove_prereq,
                                            mock_set_required):
     seq3 = ItemFactory.create(parent_location=self.chapter.location,
                               category='sequential',
                               display_name='untitled sequential 3')
     self.client.delete(reverse_usage_url('xblock_handler', seq3.location))
     mock_remove_prereq.assert_called_with(seq3.location)
     mock_set_required.assert_called_with(seq3.location.course_key,
                                          seq3.location, None, None, None)
Example #10
0
 def make_them_public(self):
     objs = [self.course, self.chapter, self.sequential]
     for x in objs:
         problem_usage_key = x.location #self.response_usage_key(x)
         problem_update_url = reverse_usage_url("xblock_handler", problem_usage_key)
         self.client.ajax_post(
             problem_update_url,
             data={'publish': 'make_public'}
         )
Example #11
0
    def test_handle_requests(self, aside_key_class):
        """
        Checks that handler to save tags in StructuredTagsAside works properly
        """
        handler_url = reverse_usage_url(
            'component_handler',
            six.text_type(aside_key_class(self.problem.location, self.aside_name)),
            kwargs={'handler': 'save_tags'}
        )

        client = AjaxEnabledTestClient()
        client.login(username=self.user.username, password=self.user_password)

        response = client.post(handler_url, json.dumps({}), content_type="application/json")
        self.assertEqual(response.status_code, 400)

        response = client.post(handler_url, json.dumps({'undefined_tag': ['undefined1', 'undefined2']}),
                               content_type="application/json")
        self.assertEqual(response.status_code, 400)

        response = client.post(handler_url, json.dumps({self.aside_tag_dif: ['undefined1', 'undefined2']}),
                               content_type="application/json")
        self.assertEqual(response.status_code, 400)

        def _test_helper_func(problem_location):
            """
            Helper function
            """
            problem = modulestore().get_item(problem_location)
            asides = problem.runtime.get_asides(problem)
            tag_aside = None
            for aside in asides:
                if isinstance(aside, StructuredTagsAside):
                    tag_aside = aside
                    break
            return tag_aside

        response = client.post(handler_url, json.dumps({self.aside_tag_dif: [self.aside_tag_dif_value]}),
                               content_type="application/json")
        self.assertEqual(response.status_code, 200)

        tag_aside = _test_helper_func(self.problem.location)
        self.assertIsNotNone(tag_aside, "Necessary StructuredTagsAside object isn't found")
        self.assertEqual(tag_aside.saved_tags[self.aside_tag_dif], [self.aside_tag_dif_value])

        response = client.post(handler_url, json.dumps({self.aside_tag_dif: [self.aside_tag_dif_value,
                                                                             self.aside_tag_dif_value2]}),
                               content_type="application/json")
        self.assertEqual(response.status_code, 200)

        tag_aside = _test_helper_func(self.problem.location)
        self.assertIsNotNone(tag_aside, "Necessary StructuredTagsAside object isn't found")
        self.assertEqual(tag_aside.saved_tags[self.aside_tag_dif], [self.aside_tag_dif_value,
                                                                    self.aside_tag_dif_value2])
        def _get_settings_html():
            """
            Helper function to get block settings HTML
            Used to check the available libraries.
            """
            edit_view_url = reverse_usage_url("xblock_view_handler", lib_block.location, {"view_name": STUDIO_VIEW})

            resp = self.client.get_json(edit_view_url)
            self.assertEqual(resp.status_code, 200)

            return parse_json(resp)['html']
Example #13
0
    def test_can_get_correct_usage_info_for_unit(self):
        """
        When group access is set on the unit level, the usage info should return a url to the unit, not
        the sequential parent of the unit.
        """
        self.course.user_partitions = [
            UserPartition(
                id=0,
                name='User Partition',
                scheme=UserPartition.get_scheme('cohort'),
                description='User Partition',
                groups=[
                    Group(id=0, name="Group")
                ],
            ),
        ]
        vertical, __ = self._create_problem_with_content_group(
            cid=0, group_id=0, name_suffix='0'
        )

        self.client.ajax_post(
            reverse_usage_url("xblock_handler", vertical.location),
            data={'metadata': {'group_access': {0: [0]}}}
        )

        actual = self._get_user_partition('cohort')
        # order of usage list is arbitrary, sort for reliable comparison
        actual['groups'][0]['usage'].sort(key=itemgetter('label'))
        expected = {
            'id': 0,
            'name': 'User Partition',
            'scheme': 'cohort',
            'description': 'User Partition',
            'version': UserPartition.VERSION,
            'groups': [
                {'id': 0, 'name': 'Group', 'version': 1, 'usage': [
                    {
                        'url': u"/container/{}".format(vertical.location),
                        'label': u"Test Subsection 0 / Test Unit 0"
                    },
                    {
                        'url': u"/container/{}".format(vertical.location),
                        'label': u"Test Unit 0 / Test Problem 0"
                    }
                ]},
            ],
            u'parameters': {},
            u'active': True,
        }

        self.maxDiff = None

        assert actual == expected
 def set_discussion_enabled_status(self, xblock, value, client=None):
     """
     Issue a POST request to update value of discussion_enabled flag of param:xblock's
     """
     client = client if client is not None else self.client
     xblock_location = xblock.location
     url = reverse_usage_url("xblock_handler", xblock_location)
     resp = client.post(
         url,
         HTTP_ACCEPT="application/json",
         data=json.dumps({"metadata": {"discussion_enabled": value}}),
         content_type="application/json",
     )
     return resp
    def _refresh_children(self, lib_content_block, status_code_expected=200):
        """
        Helper method: Uses the REST API to call the 'refresh_children' handler
        of a LibraryContent block
        """
        if 'user' not in lib_content_block.runtime._services:  # pylint: disable=protected-access
            user_service = DjangoXBlockUserService(self.user)
            lib_content_block.runtime._services['user'] = user_service  # pylint: disable=protected-access

        handler_url = reverse_usage_url('component_handler',
                                        lib_content_block.location,
                                        kwargs={'handler': 'refresh_children'})
        response = self.client.ajax_post(handler_url)
        self.assertEqual(response.status_code, status_code_expected)
        return modulestore().get_item(lib_content_block.location)
Example #16
0
    def _verify_deprecated_info(self, course_id, advanced_modules, info,
                                deprecated_block_types):
        """
        Verify deprecated info.
        """
        expected_blocks = []
        for block_type in deprecated_block_types:
            expected_blocks.append([
                reverse_usage_url('container_handler', self.vertical.location),
                f'{block_type} Problem'
            ])

        self.assertEqual(info['deprecated_enabled_block_types'], [
            component for component in advanced_modules
            if component in deprecated_block_types
        ])

        self.assertCountEqual(info['blocks'], expected_blocks)
        self.assertEqual(
            info['advance_settings_url'],
            reverse_course_url('advanced_settings_handler', course_id))
    def test_block_branch_not_changed_by_preview_handler(self, default_store):
        """
        Tests preview_handler should not update blocks being previewed
        """
        client = Client()
        client.login(username=self.user.username, password=self.user_password)

        with self.store.default_store(default_store):
            course = CourseFactory.create()

            block = ItemFactory.create(parent_location=course.location,
                                       category="problem")

            url = reverse_usage_url(
                'preview_handler',
                block.location,
                kwargs={'handler': 'xmodule_handler/problem_check'})
            response = client.post(url)
            self.assertEqual(response.status_code, 200)
            self.assertFalse(modulestore().has_changes(modulestore().get_item(
                block.location)))
Example #18
0
    def _create_problem_with_content_group(self, cid, group_id, name_suffix='', special_characters='', orphan=False):
        """
        Create a problem
        Assign content group to the problem.
        """
        vertical_parent_location = self.course.location
        if not orphan:
            subsection = ItemFactory.create(
                category='sequential',
                parent_location=self.course.location,
                display_name=u"Test Subsection {}".format(name_suffix)
            )
            vertical_parent_location = subsection.location

        vertical = ItemFactory.create(
            category='vertical',
            parent_location=vertical_parent_location,
            display_name=u"Test Unit {}".format(name_suffix)
        )

        problem = ItemFactory.create(
            category='problem',
            parent_location=vertical.location,
            display_name=u"Test Problem {}{}".format(name_suffix, special_characters)
        )

        group_access_content = {'group_access': {cid: [group_id]}}

        self.client.ajax_post(
            reverse_usage_url("xblock_handler", problem.location),
            data={'metadata': group_access_content}
        )

        if not orphan:
            self.course.children.append(subsection.location)
        self.save_course()

        return vertical, problem
    def test_read_only_role(self, use_org_level_role):
        """
        Test the read-only role (LibraryUserRole and its org-level equivalent)
        """
        # As staff user, add a block to self.library:
        block = self._add_simple_content_block()

        # Login as a non_staff_user:
        self._login_as_non_staff_user()
        self.assertFalse(self._can_access_library(self.library))

        block_url = reverse_usage_url('xblock_handler', block.location)

        def can_read_block():
            """ Check if studio lets us view the XBlock in the library """
            response = self.client.get_json(block_url)
            self.assertIn(response.status_code,
                          (200, 403))  # 400 would be ambiguous
            return response.status_code == 200

        def can_edit_block():
            """ Check if studio lets us edit the XBlock in the library """
            response = self.client.ajax_post(block_url)
            self.assertIn(response.status_code,
                          (200, 403))  # 400 would be ambiguous
            return response.status_code == 200

        def can_delete_block():
            """ Check if studio lets us delete the XBlock in the library """
            response = self.client.delete(block_url)
            self.assertIn(response.status_code,
                          (200, 403))  # 400 would be ambiguous
            return response.status_code == 200

        def can_copy_block():
            """ Check if studio lets us duplicate the XBlock in the library """
            response = self.client.ajax_post(
                reverse_url('xblock_handler'), {
                    'parent_locator': str(self.library.location),
                    'duplicate_source_locator': str(block.location),
                })
            self.assertIn(response.status_code,
                          (200, 403))  # 400 would be ambiguous
            return response.status_code == 200

        def can_create_block():
            """ Check if studio lets us make a new XBlock in the library """
            response = self.client.ajax_post(
                reverse_url('xblock_handler'), {
                    'parent_locator': str(self.library.location),
                    'category': 'html',
                })
            self.assertIn(response.status_code,
                          (200, 403))  # 400 would be ambiguous
            return response.status_code == 200

        # Check that we do not have read or write access to block:
        self.assertFalse(can_read_block())
        self.assertFalse(can_edit_block())
        self.assertFalse(can_delete_block())
        self.assertFalse(can_copy_block())
        self.assertFalse(can_create_block())

        # Give non_staff_user read-only permission:
        if use_org_level_role:
            OrgLibraryUserRole(self.lib_key.org).add_users(self.non_staff_user)
        else:
            LibraryUserRole(self.lib_key).add_users(self.non_staff_user)

        self.assertTrue(self._can_access_library(self.library))
        self.assertTrue(can_read_block())
        self.assertFalse(can_edit_block())
        self.assertFalse(can_delete_block())
        self.assertFalse(can_copy_block())
        self.assertFalse(can_create_block())
Example #20
0
    def _create_content_experiment(self, cid=-1, group_id=None, cid_for_problem=None,
                                   name_suffix='', special_characters=''):
        """
        Create content experiment.

        Assign Group Configuration to the experiment if cid is provided.
        Assigns a problem to the first group in the split test if group_id and cid_for_problem is provided.
        """
        sequential = ItemFactory.create(
            category='sequential',
            parent_location=self.course.location,
            display_name=u'Test Subsection {}'.format(name_suffix)
        )
        vertical = ItemFactory.create(
            category='vertical',
            parent_location=sequential.location,
            display_name=u'Test Unit {}'.format(name_suffix)
        )
        c0_url = self.course.id.make_usage_key("vertical", "split_test_cond0")
        c1_url = self.course.id.make_usage_key("vertical", "split_test_cond1")
        c2_url = self.course.id.make_usage_key("vertical", "split_test_cond2")
        split_test = ItemFactory.create(
            category='split_test',
            parent_location=vertical.location,
            user_partition_id=cid,
            display_name=u"Test Content Experiment {}{}".format(name_suffix, special_characters),
            group_id_to_child={"0": c0_url, "1": c1_url, "2": c2_url}
        )
        ItemFactory.create(
            parent_location=split_test.location,
            category="vertical",
            display_name="Condition 0 vertical",
            location=c0_url,
        )
        c1_vertical = ItemFactory.create(
            parent_location=split_test.location,
            category="vertical",
            display_name="Condition 1 vertical",
            location=c1_url,
        )
        ItemFactory.create(
            parent_location=split_test.location,
            category="vertical",
            display_name="Condition 2 vertical",
            location=c2_url,
        )

        problem = None
        if group_id and cid_for_problem:
            problem = ItemFactory.create(
                category='problem',
                parent_location=c1_vertical.location,
                display_name=u"Test Problem"
            )
            self.client.ajax_post(
                reverse_usage_url("xblock_handler", problem.location),
                data={'metadata': {'group_access': {cid_for_problem: [group_id]}}}
            )
            c1_vertical.children.append(problem.location)

        partitions_json = [p.to_json() for p in self.course.user_partitions]

        self.client.ajax_post(
            reverse_usage_url("xblock_handler", split_test.location),
            data={'metadata': {'user_partitions': partitions_json}}
        )

        self.save_course()
        return vertical, split_test, problem